diff --git a/BRANCH_TODO b/BRANCH_TODO
index ae725e118a9fdd94240f722cd32a6b5220c4379d..56868a8f8fa0ab0c7eeef01af069c3468e890763 100644
--- a/BRANCH_TODO
+++ b/BRANCH_TODO
@@ -37,8 +37,6 @@
* implement proper compile errors for failing to build glibc crt files and shared libs
* implement -fno-emit-bin
* improve the stage2 tests to support testing with LLVM extensions enabled
- * rename src/ to src/stage1/
- * rename src-self-hosted/ to src/
* implement emit-h in stage2
* multi-thread building C objects
* implement serialization/deserialization of incremental compilation metadata
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6389ec7dbe2611be88adf7c15b3a475e2cc70296..e3035213a5f03a3bfbd56a0081fa092441b1b8ba 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -257,34 +257,34 @@ find_package(Threads)
# This is our shim which will be replaced by stage1.zig.
set(ZIG0_SOURCES
- "${CMAKE_SOURCE_DIR}/src/zig0.cpp"
+ "${CMAKE_SOURCE_DIR}/src/stage1/zig0.cpp"
)
set(ZIG_SOURCES
- "${CMAKE_SOURCE_DIR}/src/analyze.cpp"
- "${CMAKE_SOURCE_DIR}/src/ast_render.cpp"
- "${CMAKE_SOURCE_DIR}/src/bigfloat.cpp"
- "${CMAKE_SOURCE_DIR}/src/bigint.cpp"
- "${CMAKE_SOURCE_DIR}/src/buffer.cpp"
- "${CMAKE_SOURCE_DIR}/src/codegen.cpp"
- "${CMAKE_SOURCE_DIR}/src/dump_analysis.cpp"
- "${CMAKE_SOURCE_DIR}/src/errmsg.cpp"
- "${CMAKE_SOURCE_DIR}/src/error.cpp"
- "${CMAKE_SOURCE_DIR}/src/heap.cpp"
- "${CMAKE_SOURCE_DIR}/src/ir.cpp"
- "${CMAKE_SOURCE_DIR}/src/ir_print.cpp"
- "${CMAKE_SOURCE_DIR}/src/mem.cpp"
- "${CMAKE_SOURCE_DIR}/src/os.cpp"
- "${CMAKE_SOURCE_DIR}/src/parser.cpp"
- "${CMAKE_SOURCE_DIR}/src/range_set.cpp"
- "${CMAKE_SOURCE_DIR}/src/stage1.cpp"
- "${CMAKE_SOURCE_DIR}/src/target.cpp"
- "${CMAKE_SOURCE_DIR}/src/tokenizer.cpp"
- "${CMAKE_SOURCE_DIR}/src/util.cpp"
- "${CMAKE_SOURCE_DIR}/src/softfloat_ext.cpp"
+ "${CMAKE_SOURCE_DIR}/src/stage1/analyze.cpp"
+ "${CMAKE_SOURCE_DIR}/src/stage1/ast_render.cpp"
+ "${CMAKE_SOURCE_DIR}/src/stage1/bigfloat.cpp"
+ "${CMAKE_SOURCE_DIR}/src/stage1/bigint.cpp"
+ "${CMAKE_SOURCE_DIR}/src/stage1/buffer.cpp"
+ "${CMAKE_SOURCE_DIR}/src/stage1/codegen.cpp"
+ "${CMAKE_SOURCE_DIR}/src/stage1/dump_analysis.cpp"
+ "${CMAKE_SOURCE_DIR}/src/stage1/errmsg.cpp"
+ "${CMAKE_SOURCE_DIR}/src/stage1/error.cpp"
+ "${CMAKE_SOURCE_DIR}/src/stage1/heap.cpp"
+ "${CMAKE_SOURCE_DIR}/src/stage1/ir.cpp"
+ "${CMAKE_SOURCE_DIR}/src/stage1/ir_print.cpp"
+ "${CMAKE_SOURCE_DIR}/src/stage1/mem.cpp"
+ "${CMAKE_SOURCE_DIR}/src/stage1/os.cpp"
+ "${CMAKE_SOURCE_DIR}/src/stage1/parser.cpp"
+ "${CMAKE_SOURCE_DIR}/src/stage1/range_set.cpp"
+ "${CMAKE_SOURCE_DIR}/src/stage1/stage1.cpp"
+ "${CMAKE_SOURCE_DIR}/src/stage1/target.cpp"
+ "${CMAKE_SOURCE_DIR}/src/stage1/tokenizer.cpp"
+ "${CMAKE_SOURCE_DIR}/src/stage1/util.cpp"
+ "${CMAKE_SOURCE_DIR}/src/stage1/softfloat_ext.cpp"
)
set(OPTIMIZED_C_SOURCES
- "${CMAKE_SOURCE_DIR}/src/parse_f128.c"
+ "${CMAKE_SOURCE_DIR}/src/stage1/parse_f128.c"
)
set(ZIG_CPP_SOURCES
# These are planned to stay even when we are self-hosted.
@@ -314,7 +314,7 @@ set(ZIG_STD_DEST "${ZIG_LIB_DIR}/std")
set(ZIG_CONFIG_H_OUT "${CMAKE_BINARY_DIR}/config.h")
set(ZIG_CONFIG_ZIG_OUT "${CMAKE_BINARY_DIR}/config.zig")
configure_file (
- "${CMAKE_SOURCE_DIR}/src/config.h.in"
+ "${CMAKE_SOURCE_DIR}/src/stage1/config.h.in"
"${ZIG_CONFIG_H_OUT}"
)
configure_file (
@@ -326,6 +326,7 @@ include_directories(
${CMAKE_SOURCE_DIR}
${CMAKE_BINARY_DIR}
"${CMAKE_SOURCE_DIR}/src"
+ "${CMAKE_SOURCE_DIR}/src/stage1"
)
# These have to go before the -Wno- flags
@@ -444,7 +445,7 @@ else()
endif()
set(BUILD_ZIG1_ARGS
- "src-self-hosted/stage1.zig"
+ "src/stage1.zig"
-target "${ZIG_TARGET_TRIPLE}"
"-mcpu=${ZIG_TARGET_MCPU}"
--name zig1
@@ -480,7 +481,7 @@ else()
endif()
# cmake won't let us configure an executable without C sources.
-add_executable(zig "${CMAKE_SOURCE_DIR}/src/empty.cpp")
+add_executable(zig "${CMAKE_SOURCE_DIR}/src/stage1/empty.cpp")
set_target_properties(zig PROPERTIES
COMPILE_FLAGS ${EXE_CFLAGS}
diff --git a/build.zig b/build.zig
index 9e5f2425c2e8e401b6eaa10ebd69030124186a48..331af1204ca2ddafe0d137de3192930a4eca8a68 100644
--- a/build.zig
+++ b/build.zig
@@ -38,7 +38,7 @@ pub fn build(b: *Builder) !void {
const test_step = b.step("test", "Run all the tests");
- var test_stage2 = b.addTest("src-self-hosted/test.zig");
+ var test_stage2 = b.addTest("src/test.zig");
test_stage2.setBuildMode(mode);
test_stage2.addPackagePath("stage2_tests", "test/stage2/test.zig");
@@ -76,7 +76,7 @@ pub fn build(b: *Builder) !void {
const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source");
const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse enable_llvm;
- var exe = b.addExecutable("zig", "src-self-hosted/main.zig");
+ var exe = b.addExecutable("zig", "src/main.zig");
exe.install();
exe.setBuildMode(mode);
exe.setTarget(target);
diff --git a/src-self-hosted/Cache.zig b/src-self-hosted/Cache.zig
deleted file mode 100644
index 24c6ae3ac4e62f12c19471038dfc933d77085fbd..0000000000000000000000000000000000000000
--- a/src-self-hosted/Cache.zig
+++ /dev/null
@@ -1,890 +0,0 @@
-gpa: *Allocator,
-manifest_dir: fs.Dir,
-hash: HashHelper = .{},
-
-const Cache = @This();
-const std = @import("std");
-const crypto = std.crypto;
-const fs = std.fs;
-const assert = std.debug.assert;
-const testing = std.testing;
-const mem = std.mem;
-const fmt = std.fmt;
-const Allocator = std.mem.Allocator;
-
-/// Be sure to call `CacheHash.deinit` after successful initialization.
-pub fn obtain(cache: *const Cache) CacheHash {
- return CacheHash{
- .cache = cache,
- .hash = cache.hash,
- .manifest_file = null,
- .manifest_dirty = false,
- .hex_digest = undefined,
- };
-}
-
-/// This is 128 bits - Even with 2^54 cache entries, the probably of a collision would be under 10^-6
-pub const bin_digest_len = 16;
-pub const hex_digest_len = bin_digest_len * 2;
-
-const manifest_file_size_max = 50 * 1024 * 1024;
-
-/// The type used for hashing file contents. Currently, this is SipHash128(1, 3), because it
-/// provides enough collision resistance for the CacheHash use cases, while being one of our
-/// fastest options right now.
-pub const Hasher = crypto.auth.siphash.SipHash128(1, 3);
-
-/// Initial state, that can be copied.
-pub const hasher_init: Hasher = Hasher.init(&[_]u8{0} ** Hasher.minimum_key_length);
-
-pub const File = struct {
- path: ?[]const u8,
- max_file_size: ?usize,
- stat: fs.File.Stat,
- bin_digest: [bin_digest_len]u8,
- contents: ?[]const u8,
-
- pub fn deinit(self: *File, allocator: *Allocator) void {
- if (self.path) |owned_slice| {
- allocator.free(owned_slice);
- self.path = null;
- }
- if (self.contents) |contents| {
- allocator.free(contents);
- self.contents = null;
- }
- self.* = undefined;
- }
-};
-
-pub const HashHelper = struct {
- hasher: Hasher = hasher_init,
-
- /// Record a slice of bytes as an dependency of the process being cached
- pub fn addBytes(hh: *HashHelper, bytes: []const u8) void {
- hh.hasher.update(mem.asBytes(&bytes.len));
- hh.hasher.update(bytes);
- }
-
- pub fn addOptionalBytes(hh: *HashHelper, optional_bytes: ?[]const u8) void {
- hh.add(optional_bytes != null);
- hh.addBytes(optional_bytes orelse return);
- }
-
- pub fn addListOfBytes(hh: *HashHelper, list_of_bytes: []const []const u8) void {
- hh.add(list_of_bytes.len);
- for (list_of_bytes) |bytes| hh.addBytes(bytes);
- }
-
- /// Convert the input value into bytes and record it as a dependency of the process being cached.
- pub fn add(hh: *HashHelper, x: anytype) void {
- switch (@TypeOf(x)) {
- std.builtin.Version => {
- hh.add(x.major);
- hh.add(x.minor);
- hh.add(x.patch);
- },
- std.Target.Os.TaggedVersionRange => {
- switch (x) {
- .linux => |linux| {
- hh.add(linux.range.min);
- hh.add(linux.range.max);
- hh.add(linux.glibc);
- },
- .windows => |windows| {
- hh.add(windows.min);
- hh.add(windows.max);
- },
- .semver => |semver| {
- hh.add(semver.min);
- hh.add(semver.max);
- },
- .none => {},
- }
- },
- else => switch (@typeInfo(@TypeOf(x))) {
- .Bool, .Int, .Enum, .Array => hh.addBytes(mem.asBytes(&x)),
- else => @compileError("unable to hash type " ++ @typeName(@TypeOf(x))),
- },
- }
- }
-
- pub fn addOptional(hh: *HashHelper, optional: anytype) void {
- hh.add(optional != null);
- hh.add(optional orelse return);
- }
-
- /// Returns a hex encoded hash of the inputs, without modifying state.
- pub fn peek(hh: HashHelper) [hex_digest_len]u8 {
- var copy = hh;
- return copy.final();
- }
-
- /// Returns a hex encoded hash of the inputs, mutating the state of the hasher.
- pub fn final(hh: *HashHelper) [hex_digest_len]u8 {
- var bin_digest: [bin_digest_len]u8 = undefined;
- hh.hasher.final(&bin_digest);
-
- var out_digest: [hex_digest_len]u8 = undefined;
- _ = std.fmt.bufPrint(&out_digest, "{x}", .{bin_digest}) catch unreachable;
- return out_digest;
- }
-};
-
-pub const Lock = struct {
- manifest_file: fs.File,
-
- pub fn release(lock: *Lock) void {
- lock.manifest_file.close();
- lock.* = undefined;
- }
-};
-
-/// CacheHash manages project-local `zig-cache` directories.
-/// This is not a general-purpose cache.
-/// It is designed to be fast and simple, not to withstand attacks using specially-crafted input.
-pub const CacheHash = struct {
- cache: *const Cache,
- /// Current state for incremental hashing.
- hash: HashHelper,
- manifest_file: ?fs.File,
- manifest_dirty: bool,
- files: std.ArrayListUnmanaged(File) = .{},
- hex_digest: [hex_digest_len]u8,
-
- /// Add a file as a dependency of process being cached. When `hit` is
- /// called, the file's contents will be checked to ensure that it matches
- /// the contents from previous times.
- ///
- /// Max file size will be used to determine the amount of space to the file contents
- /// are allowed to take up in memory. If max_file_size is null, then the contents
- /// will not be loaded into memory.
- ///
- /// Returns the index of the entry in the `files` array list. You can use it
- /// to access the contents of the file after calling `hit()` like so:
- ///
- /// ```
- /// var file_contents = cache_hash.files.items[file_index].contents.?;
- /// ```
- pub fn addFile(self: *CacheHash, file_path: []const u8, max_file_size: ?usize) !usize {
- assert(self.manifest_file == null);
-
- try self.files.ensureCapacity(self.cache.gpa, self.files.items.len + 1);
- const resolved_path = try fs.path.resolve(self.cache.gpa, &[_][]const u8{file_path});
-
- const idx = self.files.items.len;
- self.files.addOneAssumeCapacity().* = .{
- .path = resolved_path,
- .contents = null,
- .max_file_size = max_file_size,
- .stat = undefined,
- .bin_digest = undefined,
- };
-
- self.hash.addBytes(resolved_path);
-
- return idx;
- }
-
- pub fn addOptionalFile(self: *CacheHash, optional_file_path: ?[]const u8) !void {
- self.hash.add(optional_file_path != null);
- const file_path = optional_file_path orelse return;
- _ = try self.addFile(file_path, null);
- }
-
- pub fn addListOfFiles(self: *CacheHash, list_of_files: []const []const u8) !void {
- self.hash.add(list_of_files.len);
- for (list_of_files) |file_path| {
- _ = try self.addFile(file_path, null);
- }
- }
-
- /// Check the cache to see if the input exists in it. If it exists, returns `true`.
- /// A hex encoding of its hash is available by calling `final`.
- ///
- /// This function will also acquire an exclusive lock to the manifest file. This means
- /// that a process holding a CacheHash will block any other process attempting to
- /// acquire the lock.
- ///
- /// The lock on the manifest file is released when `deinit` is called. As another
- /// option, one may call `toOwnedLock` to obtain a smaller object which can represent
- /// the lock. `deinit` is safe to call whether or not `toOwnedLock` has been called.
- pub fn hit(self: *CacheHash) !bool {
- assert(self.manifest_file == null);
-
- const ext = ".txt";
- var manifest_file_path: [self.hex_digest.len + ext.len]u8 = undefined;
-
- var bin_digest: [bin_digest_len]u8 = undefined;
- self.hash.hasher.final(&bin_digest);
-
- _ = std.fmt.bufPrint(&self.hex_digest, "{x}", .{bin_digest}) catch unreachable;
-
- self.hash.hasher = hasher_init;
- self.hash.hasher.update(&bin_digest);
-
- mem.copy(u8, &manifest_file_path, &self.hex_digest);
- manifest_file_path[self.hex_digest.len..][0..ext.len].* = ext.*;
-
- if (self.files.items.len != 0) {
- self.manifest_file = try self.cache.manifest_dir.createFile(&manifest_file_path, .{
- .read = true,
- .truncate = false,
- .lock = .Exclusive,
- });
- } else {
- // If there are no file inputs, we check if the manifest file exists instead of
- // comparing the hashes on the files used for the cached item
- self.manifest_file = self.cache.manifest_dir.openFile(&manifest_file_path, .{
- .read = true,
- .write = true,
- .lock = .Exclusive,
- }) catch |err| switch (err) {
- error.FileNotFound => {
- self.manifest_dirty = true;
- self.manifest_file = try self.cache.manifest_dir.createFile(&manifest_file_path, .{
- .read = true,
- .truncate = false,
- .lock = .Exclusive,
- });
- return false;
- },
- else => |e| return e,
- };
- }
-
- const file_contents = try self.manifest_file.?.inStream().readAllAlloc(self.cache.gpa, manifest_file_size_max);
- defer self.cache.gpa.free(file_contents);
-
- const input_file_count = self.files.items.len;
- var any_file_changed = false;
- var line_iter = mem.tokenize(file_contents, "\n");
- var idx: usize = 0;
- while (line_iter.next()) |line| {
- defer idx += 1;
-
- const cache_hash_file = if (idx < input_file_count) &self.files.items[idx] else blk: {
- const new = try self.files.addOne(self.cache.gpa);
- new.* = .{
- .path = null,
- .contents = null,
- .max_file_size = null,
- .stat = undefined,
- .bin_digest = undefined,
- };
- break :blk new;
- };
-
- var iter = mem.tokenize(line, " ");
- const size = iter.next() orelse return error.InvalidFormat;
- const inode = iter.next() orelse return error.InvalidFormat;
- const mtime_nsec_str = iter.next() orelse return error.InvalidFormat;
- const digest_str = iter.next() orelse return error.InvalidFormat;
- const file_path = iter.rest();
-
- cache_hash_file.stat.size = fmt.parseInt(u64, size, 10) catch return error.InvalidFormat;
- cache_hash_file.stat.inode = fmt.parseInt(fs.File.INode, inode, 10) catch return error.InvalidFormat;
- cache_hash_file.stat.mtime = fmt.parseInt(i64, mtime_nsec_str, 10) catch return error.InvalidFormat;
- std.fmt.hexToBytes(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat;
-
- if (file_path.len == 0) {
- return error.InvalidFormat;
- }
- if (cache_hash_file.path) |p| {
- if (!mem.eql(u8, file_path, p)) {
- return error.InvalidFormat;
- }
- }
-
- if (cache_hash_file.path == null) {
- cache_hash_file.path = try self.cache.gpa.dupe(u8, file_path);
- }
-
- const this_file = fs.cwd().openFile(cache_hash_file.path.?, .{ .read = true }) catch {
- return error.CacheUnavailable;
- };
- defer this_file.close();
-
- const actual_stat = try this_file.stat();
- const size_match = actual_stat.size == cache_hash_file.stat.size;
- const mtime_match = actual_stat.mtime == cache_hash_file.stat.mtime;
- const inode_match = actual_stat.inode == cache_hash_file.stat.inode;
-
- if (!size_match or !mtime_match or !inode_match) {
- self.manifest_dirty = true;
-
- cache_hash_file.stat = actual_stat;
-
- if (isProblematicTimestamp(cache_hash_file.stat.mtime)) {
- cache_hash_file.stat.mtime = 0;
- cache_hash_file.stat.inode = 0;
- }
-
- var actual_digest: [bin_digest_len]u8 = undefined;
- try hashFile(this_file, &actual_digest);
-
- if (!mem.eql(u8, &cache_hash_file.bin_digest, &actual_digest)) {
- cache_hash_file.bin_digest = actual_digest;
- // keep going until we have the input file digests
- any_file_changed = true;
- }
- }
-
- if (!any_file_changed) {
- self.hash.hasher.update(&cache_hash_file.bin_digest);
- }
- }
-
- if (any_file_changed) {
- // cache miss
- // keep the manifest file open
- // reset the hash
- self.hash.hasher = hasher_init;
- self.hash.hasher.update(&bin_digest);
-
- // Remove files not in the initial hash
- for (self.files.items[input_file_count..]) |*file| {
- file.deinit(self.cache.gpa);
- }
- self.files.shrinkRetainingCapacity(input_file_count);
-
- for (self.files.items) |file| {
- self.hash.hasher.update(&file.bin_digest);
- }
- return false;
- }
-
- if (idx < input_file_count) {
- self.manifest_dirty = true;
- while (idx < input_file_count) : (idx += 1) {
- const ch_file = &self.files.items[idx];
- try self.populateFileHash(ch_file);
- }
- return false;
- }
-
- return true;
- }
-
- fn populateFileHash(self: *CacheHash, ch_file: *File) !void {
- const file = try fs.cwd().openFile(ch_file.path.?, .{});
- defer file.close();
-
- ch_file.stat = try file.stat();
-
- if (isProblematicTimestamp(ch_file.stat.mtime)) {
- ch_file.stat.mtime = 0;
- ch_file.stat.inode = 0;
- }
-
- if (ch_file.max_file_size) |max_file_size| {
- if (ch_file.stat.size > max_file_size) {
- return error.FileTooBig;
- }
-
- const contents = try self.cache.gpa.alloc(u8, @intCast(usize, ch_file.stat.size));
- errdefer self.cache.gpa.free(contents);
-
- // Hash while reading from disk, to keep the contents in the cpu cache while
- // doing hashing.
- var hasher = hasher_init;
- var off: usize = 0;
- while (true) {
- // give me everything you've got, captain
- const bytes_read = try file.read(contents[off..]);
- if (bytes_read == 0) break;
- hasher.update(contents[off..][0..bytes_read]);
- off += bytes_read;
- }
- hasher.final(&ch_file.bin_digest);
-
- ch_file.contents = contents;
- } else {
- try hashFile(file, &ch_file.bin_digest);
- }
-
- self.hash.hasher.update(&ch_file.bin_digest);
- }
-
- /// Add a file as a dependency of process being cached, after the initial hash has been
- /// calculated. This is useful for processes that don't know the all the files that
- /// are depended on ahead of time. For example, a source file that can import other files
- /// will need to be recompiled if the imported file is changed.
- pub fn addFilePostFetch(self: *CacheHash, file_path: []const u8, max_file_size: usize) ![]const u8 {
- assert(self.manifest_file != null);
-
- const resolved_path = try fs.path.resolve(self.cache.gpa, &[_][]const u8{file_path});
- errdefer self.cache.gpa.free(resolved_path);
-
- const new_ch_file = try self.files.addOne(self.cache.gpa);
- new_ch_file.* = .{
- .path = resolved_path,
- .max_file_size = max_file_size,
- .stat = undefined,
- .bin_digest = undefined,
- .contents = null,
- };
- errdefer self.files.shrinkRetainingCapacity(self.files.items.len - 1);
-
- try self.populateFileHash(new_ch_file);
-
- return new_ch_file.contents.?;
- }
-
- /// Add a file as a dependency of process being cached, after the initial hash has been
- /// calculated. This is useful for processes that don't know the all the files that
- /// are depended on ahead of time. For example, a source file that can import other files
- /// will need to be recompiled if the imported file is changed.
- pub fn addFilePost(self: *CacheHash, file_path: []const u8) !void {
- assert(self.manifest_file != null);
-
- const resolved_path = try fs.path.resolve(self.cache.gpa, &[_][]const u8{file_path});
- errdefer self.cache.gpa.free(resolved_path);
-
- const new_ch_file = try self.files.addOne(self.cache.gpa);
- new_ch_file.* = .{
- .path = resolved_path,
- .max_file_size = null,
- .stat = undefined,
- .bin_digest = undefined,
- .contents = null,
- };
- errdefer self.files.shrinkRetainingCapacity(self.files.items.len - 1);
-
- try self.populateFileHash(new_ch_file);
- }
-
- pub fn addDepFilePost(self: *CacheHash, dir: fs.Dir, dep_file_basename: []const u8) !void {
- assert(self.manifest_file != null);
-
- const dep_file_contents = try dir.readFileAlloc(self.cache.gpa, dep_file_basename, manifest_file_size_max);
- defer self.cache.gpa.free(dep_file_contents);
-
- var error_buf = std.ArrayList(u8).init(self.cache.gpa);
- defer error_buf.deinit();
-
- var it: @import("DepTokenizer.zig") = .{ .bytes = dep_file_contents };
-
- // Skip first token: target.
- switch (it.next() orelse return) { // Empty dep file OK.
- .target, .target_must_resolve, .prereq => {},
- else => |err| {
- try err.printError(error_buf.writer());
- std.log.err("failed parsing {}: {}", .{ dep_file_basename, error_buf.items });
- return error.InvalidDepFile;
- },
- }
- // Process 0+ preqreqs.
- // Clang is invoked in single-source mode so we never get more targets.
- while (true) {
- switch (it.next() orelse return) {
- .target, .target_must_resolve => return,
- .prereq => |bytes| try self.addFilePost(bytes),
- else => |err| {
- try err.printError(error_buf.writer());
- std.log.err("failed parsing {}: {}", .{ dep_file_basename, error_buf.items });
- return error.InvalidDepFile;
- },
- }
- }
- }
-
- /// Returns a hex encoded hash of the inputs.
- pub fn final(self: *CacheHash) [hex_digest_len]u8 {
- assert(self.manifest_file != null);
-
- // We don't close the manifest file yet, because we want to
- // keep it locked until the API user is done using it.
- // We also don't write out the manifest yet, because until
- // cache_release is called we still might be working on creating
- // the artifacts to cache.
-
- var bin_digest: [bin_digest_len]u8 = undefined;
- self.hash.hasher.final(&bin_digest);
-
- var out_digest: [hex_digest_len]u8 = undefined;
- _ = std.fmt.bufPrint(&out_digest, "{x}", .{bin_digest}) catch unreachable;
-
- return out_digest;
- }
-
- pub fn writeManifest(self: *CacheHash) !void {
- assert(self.manifest_file != null);
- if (!self.manifest_dirty) return;
-
- var encoded_digest: [hex_digest_len]u8 = undefined;
- var contents = std.ArrayList(u8).init(self.cache.gpa);
- var writer = contents.writer();
- defer contents.deinit();
-
- for (self.files.items) |file| {
- _ = std.fmt.bufPrint(&encoded_digest, "{x}", .{file.bin_digest}) catch unreachable;
- try writer.print("{d} {d} {d} {s} {s}\n", .{
- file.stat.size,
- file.stat.inode,
- file.stat.mtime,
- &encoded_digest,
- file.path,
- });
- }
-
- try self.manifest_file.?.pwriteAll(contents.items, 0);
- self.manifest_dirty = false;
- }
-
- /// Obtain only the data needed to maintain a lock on the manifest file.
- /// The `CacheHash` remains safe to deinit.
- /// Don't forget to call `writeManifest` before this!
- pub fn toOwnedLock(self: *CacheHash) Lock {
- const manifest_file = self.manifest_file.?;
- self.manifest_file = null;
- return Lock{ .manifest_file = manifest_file };
- }
-
- /// Releases the manifest file and frees any memory the CacheHash was using.
- /// `CacheHash.hit` must be called first.
- /// Don't forget to call `writeManifest` before this!
- pub fn deinit(self: *CacheHash) void {
- if (self.manifest_file) |file| {
- file.close();
- }
- for (self.files.items) |*file| {
- file.deinit(self.cache.gpa);
- }
- self.files.deinit(self.cache.gpa);
- }
-};
-
-fn hashFile(file: fs.File, bin_digest: []u8) !void {
- var buf: [1024]u8 = undefined;
-
- var hasher = hasher_init;
- while (true) {
- const bytes_read = try file.read(&buf);
- if (bytes_read == 0) break;
- hasher.update(buf[0..bytes_read]);
- }
-
- hasher.final(bin_digest);
-}
-
-/// If the wall clock time, rounded to the same precision as the
-/// mtime, is equal to the mtime, then we cannot rely on this mtime
-/// yet. We will instead save an mtime value that indicates the hash
-/// must be unconditionally computed.
-/// This function recognizes the precision of mtime by looking at trailing
-/// zero bits of the seconds and nanoseconds.
-fn isProblematicTimestamp(fs_clock: i128) bool {
- const wall_clock = std.time.nanoTimestamp();
-
- // We have to break the nanoseconds into seconds and remainder nanoseconds
- // to detect precision of seconds, because looking at the zero bits in base
- // 2 would not detect precision of the seconds value.
- const fs_sec = @intCast(i64, @divFloor(fs_clock, std.time.ns_per_s));
- const fs_nsec = @intCast(i64, @mod(fs_clock, std.time.ns_per_s));
- var wall_sec = @intCast(i64, @divFloor(wall_clock, std.time.ns_per_s));
- var wall_nsec = @intCast(i64, @mod(wall_clock, std.time.ns_per_s));
-
- // First make all the least significant zero bits in the fs_clock, also zero bits in the wall clock.
- if (fs_nsec == 0) {
- wall_nsec = 0;
- if (fs_sec == 0) {
- wall_sec = 0;
- } else {
- wall_sec &= @as(i64, -1) << @intCast(u6, @ctz(i64, fs_sec));
- }
- } else {
- wall_nsec &= @as(i64, -1) << @intCast(u6, @ctz(i64, fs_nsec));
- }
- return wall_nsec == fs_nsec and wall_sec == fs_sec;
-}
-
-test "cache file and then recall it" {
- if (std.Target.current.os.tag == .wasi) {
- // https://github.com/ziglang/zig/issues/5437
- return error.SkipZigTest;
- }
- const cwd = fs.cwd();
-
- const temp_file = "test.txt";
- const temp_manifest_dir = "temp_manifest_dir";
-
- const ts = std.time.nanoTimestamp();
- try cwd.writeFile(temp_file, "Hello, world!\n");
-
- while (isProblematicTimestamp(ts)) {
- std.time.sleep(1);
- }
-
- var digest1: [hex_digest_len]u8 = undefined;
- var digest2: [hex_digest_len]u8 = undefined;
-
- {
- var cache = Cache{
- .gpa = testing.allocator,
- .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),
- };
- defer cache.manifest_dir.close();
-
- {
- var ch = cache.obtain();
- defer ch.deinit();
-
- ch.hash.add(true);
- ch.hash.add(@as(u16, 1234));
- ch.hash.addBytes("1234");
- _ = try ch.addFile(temp_file, null);
-
- // There should be nothing in the cache
- testing.expectEqual(false, try ch.hit());
-
- digest1 = ch.final();
- try ch.writeManifest();
- }
- {
- var ch = cache.obtain();
- defer ch.deinit();
-
- ch.hash.add(true);
- ch.hash.add(@as(u16, 1234));
- ch.hash.addBytes("1234");
- _ = try ch.addFile(temp_file, null);
-
- // Cache hit! We just "built" the same file
- testing.expect(try ch.hit());
- digest2 = ch.final();
-
- try ch.writeManifest();
- }
-
- testing.expectEqual(digest1, digest2);
- }
-
- try cwd.deleteTree(temp_manifest_dir);
- try cwd.deleteFile(temp_file);
-}
-
-test "give problematic timestamp" {
- var fs_clock = std.time.nanoTimestamp();
- // to make it problematic, we make it only accurate to the second
- fs_clock = @divTrunc(fs_clock, std.time.ns_per_s);
- fs_clock *= std.time.ns_per_s;
- testing.expect(isProblematicTimestamp(fs_clock));
-}
-
-test "give nonproblematic timestamp" {
- testing.expect(!isProblematicTimestamp(std.time.nanoTimestamp() - std.time.ns_per_s));
-}
-
-test "check that changing a file makes cache fail" {
- if (std.Target.current.os.tag == .wasi) {
- // https://github.com/ziglang/zig/issues/5437
- return error.SkipZigTest;
- }
- const cwd = fs.cwd();
-
- const temp_file = "cache_hash_change_file_test.txt";
- const temp_manifest_dir = "cache_hash_change_file_manifest_dir";
- const original_temp_file_contents = "Hello, world!\n";
- const updated_temp_file_contents = "Hello, world; but updated!\n";
-
- try cwd.deleteTree(temp_manifest_dir);
- try cwd.deleteTree(temp_file);
-
- const ts = std.time.nanoTimestamp();
- try cwd.writeFile(temp_file, original_temp_file_contents);
-
- while (isProblematicTimestamp(ts)) {
- std.time.sleep(1);
- }
-
- var digest1: [hex_digest_len]u8 = undefined;
- var digest2: [hex_digest_len]u8 = undefined;
-
- {
- var cache = Cache{
- .gpa = testing.allocator,
- .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),
- };
- defer cache.manifest_dir.close();
-
- {
- var ch = cache.obtain();
- defer ch.deinit();
-
- ch.hash.addBytes("1234");
- const temp_file_idx = try ch.addFile(temp_file, 100);
-
- // There should be nothing in the cache
- testing.expectEqual(false, try ch.hit());
-
- testing.expect(mem.eql(u8, original_temp_file_contents, ch.files.items[temp_file_idx].contents.?));
-
- digest1 = ch.final();
-
- try ch.writeManifest();
- }
-
- try cwd.writeFile(temp_file, updated_temp_file_contents);
-
- {
- var ch = cache.obtain();
- defer ch.deinit();
-
- ch.hash.addBytes("1234");
- const temp_file_idx = try ch.addFile(temp_file, 100);
-
- // A file that we depend on has been updated, so the cache should not contain an entry for it
- testing.expectEqual(false, try ch.hit());
-
- // The cache system does not keep the contents of re-hashed input files.
- testing.expect(ch.files.items[temp_file_idx].contents == null);
-
- digest2 = ch.final();
-
- try ch.writeManifest();
- }
-
- testing.expect(!mem.eql(u8, digest1[0..], digest2[0..]));
- }
-
- try cwd.deleteTree(temp_manifest_dir);
- try cwd.deleteTree(temp_file);
-}
-
-test "no file inputs" {
- if (std.Target.current.os.tag == .wasi) {
- // https://github.com/ziglang/zig/issues/5437
- return error.SkipZigTest;
- }
- const cwd = fs.cwd();
- const temp_manifest_dir = "no_file_inputs_manifest_dir";
- defer cwd.deleteTree(temp_manifest_dir) catch {};
-
- var digest1: [hex_digest_len]u8 = undefined;
- var digest2: [hex_digest_len]u8 = undefined;
-
- var cache = Cache{
- .gpa = testing.allocator,
- .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),
- };
- defer cache.manifest_dir.close();
-
- {
- var ch = cache.obtain();
- defer ch.deinit();
-
- ch.hash.addBytes("1234");
-
- // There should be nothing in the cache
- testing.expectEqual(false, try ch.hit());
-
- digest1 = ch.final();
-
- try ch.writeManifest();
- }
- {
- var ch = cache.obtain();
- defer ch.deinit();
-
- ch.hash.addBytes("1234");
-
- testing.expect(try ch.hit());
- digest2 = ch.final();
- try ch.writeManifest();
- }
-
- testing.expectEqual(digest1, digest2);
-}
-
-test "CacheHashes with files added after initial hash work" {
- if (std.Target.current.os.tag == .wasi) {
- // https://github.com/ziglang/zig/issues/5437
- return error.SkipZigTest;
- }
- const cwd = fs.cwd();
-
- const temp_file1 = "cache_hash_post_file_test1.txt";
- const temp_file2 = "cache_hash_post_file_test2.txt";
- const temp_manifest_dir = "cache_hash_post_file_manifest_dir";
-
- const ts1 = std.time.nanoTimestamp();
- try cwd.writeFile(temp_file1, "Hello, world!\n");
- try cwd.writeFile(temp_file2, "Hello world the second!\n");
-
- while (isProblematicTimestamp(ts1)) {
- std.time.sleep(1);
- }
-
- var digest1: [hex_digest_len]u8 = undefined;
- var digest2: [hex_digest_len]u8 = undefined;
- var digest3: [hex_digest_len]u8 = undefined;
-
- {
- var cache = Cache{
- .gpa = testing.allocator,
- .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),
- };
- defer cache.manifest_dir.close();
-
- {
- var ch = cache.obtain();
- defer ch.deinit();
-
- ch.hash.addBytes("1234");
- _ = try ch.addFile(temp_file1, null);
-
- // There should be nothing in the cache
- testing.expectEqual(false, try ch.hit());
-
- _ = try ch.addFilePost(temp_file2);
-
- digest1 = ch.final();
- try ch.writeManifest();
- }
- {
- var ch = cache.obtain();
- defer ch.deinit();
-
- ch.hash.addBytes("1234");
- _ = try ch.addFile(temp_file1, null);
-
- testing.expect(try ch.hit());
- digest2 = ch.final();
-
- try ch.writeManifest();
- }
- testing.expect(mem.eql(u8, &digest1, &digest2));
-
- // Modify the file added after initial hash
- const ts2 = std.time.nanoTimestamp();
- try cwd.writeFile(temp_file2, "Hello world the second, updated\n");
-
- while (isProblematicTimestamp(ts2)) {
- std.time.sleep(1);
- }
-
- {
- var ch = cache.obtain();
- defer ch.deinit();
-
- ch.hash.addBytes("1234");
- _ = try ch.addFile(temp_file1, null);
-
- // A file that we depend on has been updated, so the cache should not contain an entry for it
- testing.expectEqual(false, try ch.hit());
-
- _ = try ch.addFilePost(temp_file2);
-
- digest3 = ch.final();
-
- try ch.writeManifest();
- }
-
- testing.expect(!mem.eql(u8, &digest1, &digest3));
- }
-
- try cwd.deleteTree(temp_manifest_dir);
- try cwd.deleteFile(temp_file1);
- try cwd.deleteFile(temp_file2);
-}
diff --git a/src-self-hosted/Compilation.zig b/src-self-hosted/Compilation.zig
deleted file mode 100644
index 29c6dc36cf9c4cc7e4202e473dcfb4e5cc5c289f..0000000000000000000000000000000000000000
--- a/src-self-hosted/Compilation.zig
+++ /dev/null
@@ -1,2246 +0,0 @@
-const Compilation = @This();
-
-const std = @import("std");
-const mem = std.mem;
-const Allocator = std.mem.Allocator;
-const Value = @import("value.zig").Value;
-const assert = std.debug.assert;
-const log = std.log.scoped(.compilation);
-const Target = std.Target;
-const target_util = @import("target.zig");
-const Package = @import("Package.zig");
-const link = @import("link.zig");
-const trace = @import("tracy.zig").trace;
-const liveness = @import("liveness.zig");
-const build_options = @import("build_options");
-const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
-const glibc = @import("glibc.zig");
-const libunwind = @import("libunwind.zig");
-const fatal = @import("main.zig").fatal;
-const Module = @import("Module.zig");
-const Cache = @import("Cache.zig");
-const stage1 = @import("stage1.zig");
-
-/// General-purpose allocator. Used for both temporary and long-term storage.
-gpa: *Allocator,
-/// Arena-allocated memory used during initialization. Should be untouched until deinit.
-arena_state: std.heap.ArenaAllocator.State,
-bin_file: *link.File,
-c_object_table: std.AutoArrayHashMapUnmanaged(*CObject, void) = .{},
-stage1_lock: ?Cache.Lock = null,
-stage1_cache_hash: *Cache.CacheHash = undefined,
-
-link_error_flags: link.File.ErrorFlags = .{},
-
-work_queue: std.fifo.LinearFifo(Job, .Dynamic),
-
-/// The ErrorMsg memory is owned by the `CObject`, using Compilation's general purpose allocator.
-failed_c_objects: std.AutoArrayHashMapUnmanaged(*CObject, *ErrorMsg) = .{},
-
-keep_source_files_loaded: bool,
-use_clang: bool,
-sanitize_c: bool,
-/// When this is `true` it means invoking clang as a sub-process is expected to inherit
-/// stdin, stdout, stderr, and if it returns non success, to forward the exit code.
-/// Otherwise we attempt to parse the error messages and expose them via the Compilation API.
-/// This is `true` for `zig cc`, `zig c++`, and `zig translate-c`.
-clang_passthrough_mode: bool,
-/// Whether to print clang argvs to stdout.
-verbose_cc: bool,
-verbose_tokenize: bool,
-verbose_ast: bool,
-verbose_ir: bool,
-verbose_llvm_ir: bool,
-verbose_cimport: bool,
-verbose_llvm_cpu_features: bool,
-disable_c_depfile: bool,
-is_test: bool,
-time_report: bool,
-
-c_source_files: []const CSourceFile,
-clang_argv: []const []const u8,
-cache_parent: *Cache,
-/// Path to own executable for invoking `zig clang`.
-self_exe_path: ?[]const u8,
-zig_lib_directory: Directory,
-zig_cache_directory: Directory,
-libc_include_dir_list: []const []const u8,
-rand: *std.rand.Random,
-
-/// Populated when we build the libc++ static library. A Job to build this is placed in the queue
-/// and resolved before calling linker.flush().
-libcxx_static_lib: ?[]const u8 = null,
-/// Populated when we build the libc++abi static library. A Job to build this is placed in the queue
-/// and resolved before calling linker.flush().
-libcxxabi_static_lib: ?[]const u8 = null,
-/// Populated when we build the libunwind static library. A Job to build this is placed in the queue
-/// and resolved before calling linker.flush().
-libunwind_static_lib: ?CRTFile = null,
-/// Populated when we build the libc static library. A Job to build this is placed in the queue
-/// and resolved before calling linker.flush().
-libc_static_lib: ?CRTFile = null,
-/// Populated when we build the libcompiler_rt static library. A Job to build this is placed in the queue
-/// and resolved before calling linker.flush().
-compiler_rt_static_lib: ?CRTFile = null,
-
-glibc_so_files: ?glibc.BuiltSharedObjects = null,
-
-/// For example `Scrt1.o` and `libc_nonshared.a`. These are populated after building libc from source,
-/// The set of needed CRT (C runtime) files differs depending on the target and compilation settings.
-/// The key is the basename, and the value is the absolute path to the completed build artifact.
-crt_files: std.StringHashMapUnmanaged(CRTFile) = .{},
-
-/// Keeping track of this possibly open resource so we can close it later.
-owned_link_dir: ?std.fs.Dir,
-
-/// This is for stage1 and should be deleted upon completion of self-hosting.
-/// Don't use this for anything other than stage1 compatibility.
-color: @import("main.zig").Color = .Auto,
-
-pub const InnerError = Module.InnerError;
-
-pub const CRTFile = struct {
- lock: Cache.Lock,
- full_object_path: []const u8,
-
- fn deinit(self: *CRTFile, gpa: *Allocator) void {
- self.lock.release();
- gpa.free(self.full_object_path);
- self.* = undefined;
- }
-};
-
-/// For passing to a C compiler.
-pub const CSourceFile = struct {
- src_path: []const u8,
- extra_flags: []const []const u8 = &[0][]const u8{},
-};
-
-const Job = union(enum) {
- /// Write the machine code for a Decl to the output file.
- codegen_decl: *Module.Decl,
- /// The Decl needs to be analyzed and possibly export itself.
- /// It may have already be analyzed, or it may have been determined
- /// to be outdated; in this case perform semantic analysis again.
- analyze_decl: *Module.Decl,
- /// The source file containing the Decl has been updated, and so the
- /// Decl may need its line number information updated in the debug info.
- update_line_number: *Module.Decl,
- /// Invoke the Clang compiler to create an object file, which gets linked
- /// with the Compilation.
- c_object: *CObject,
-
- /// one of the glibc static objects
- glibc_crt_file: glibc.CRTFile,
- /// all of the glibc shared objects
- glibc_shared_objects,
- /// libunwind.a, usually needed when linking libc
- libunwind: void,
- /// needed when producing a dynamic library or executable
- libcompiler_rt: void,
- /// needed when not linking libc and using LLVM for code generation because it generates
- /// calls to, for example, memcpy and memset.
- zig_libc: void,
-
- /// Generate builtin.zig source code and write it into the correct place.
- generate_builtin_zig: void,
- /// Use stage1 C++ code to compile zig code into an object file.
- stage1_module: void,
-};
-
-pub const CObject = struct {
- /// Relative to cwd. Owned by arena.
- src: CSourceFile,
- status: union(enum) {
- new,
- success: struct {
- /// The outputted result. Owned by gpa.
- object_path: []u8,
- /// This is a file system lock on the cache hash manifest representing this
- /// object. It prevents other invocations of the Zig compiler from interfering
- /// with this object until released.
- lock: Cache.Lock,
- },
- /// There will be a corresponding ErrorMsg in Compilation.failed_c_objects.
- failure,
- },
-
- /// Returns if there was failure.
- pub fn clearStatus(self: *CObject, gpa: *Allocator) bool {
- switch (self.status) {
- .new => return false,
- .failure => {
- self.status = .new;
- return true;
- },
- .success => |*success| {
- gpa.free(success.object_path);
- success.lock.release();
- self.status = .new;
- return false;
- },
- }
- }
-
- pub fn destroy(self: *CObject, gpa: *Allocator) void {
- _ = self.clearStatus(gpa);
- gpa.destroy(self);
- }
-};
-
-pub const AllErrors = struct {
- arena: std.heap.ArenaAllocator.State,
- list: []const Message,
-
- pub const Message = struct {
- src_path: []const u8,
- line: usize,
- column: usize,
- byte_offset: usize,
- msg: []const u8,
-
- pub fn renderToStdErr(self: Message) void {
- std.debug.print("{}:{}:{}: error: {}\n", .{
- self.src_path,
- self.line + 1,
- self.column + 1,
- self.msg,
- });
- }
- };
-
- pub fn deinit(self: *AllErrors, gpa: *Allocator) void {
- self.arena.promote(gpa).deinit();
- }
-
- fn add(
- arena: *std.heap.ArenaAllocator,
- errors: *std.ArrayList(Message),
- sub_file_path: []const u8,
- source: []const u8,
- simple_err_msg: ErrorMsg,
- ) !void {
- const loc = std.zig.findLineColumn(source, simple_err_msg.byte_offset);
- try errors.append(.{
- .src_path = try arena.allocator.dupe(u8, sub_file_path),
- .msg = try arena.allocator.dupe(u8, simple_err_msg.msg),
- .byte_offset = simple_err_msg.byte_offset,
- .line = loc.line,
- .column = loc.column,
- });
- }
-};
-
-pub const Directory = struct {
- /// This field is redundant for operations that can act on the open directory handle
- /// directly, but it is needed when passing the directory to a child process.
- /// `null` means cwd.
- path: ?[]const u8,
- handle: std.fs.Dir,
-
- pub fn join(self: Directory, allocator: *Allocator, paths: []const []const u8) ![]u8 {
- if (self.path) |p| {
- // TODO clean way to do this with only 1 allocation
- const part2 = try std.fs.path.join(allocator, paths);
- defer allocator.free(part2);
- return std.fs.path.join(allocator, &[_][]const u8{ p, part2 });
- } else {
- return std.fs.path.join(allocator, paths);
- }
- }
-};
-
-pub const EmitLoc = struct {
- /// If this is `null` it means the file will be output to the cache directory.
- /// When provided, both the open file handle and the path name must outlive the `Compilation`.
- directory: ?Compilation.Directory,
- /// This may not have sub-directories in it.
- basename: []const u8,
-};
-
-pub const InitOptions = struct {
- zig_lib_directory: Directory,
- zig_cache_directory: Directory,
- target: Target,
- root_name: []const u8,
- root_pkg: ?*Package,
- output_mode: std.builtin.OutputMode,
- rand: *std.rand.Random,
- dynamic_linker: ?[]const u8 = null,
- /// `null` means to not emit a binary file.
- emit_bin: ?EmitLoc,
- /// `null` means to not emit a C header file.
- emit_h: ?EmitLoc = null,
- link_mode: ?std.builtin.LinkMode = null,
- dll_export_fns: ?bool = false,
- object_format: ?std.builtin.ObjectFormat = null,
- optimize_mode: std.builtin.Mode = .Debug,
- keep_source_files_loaded: bool = false,
- clang_argv: []const []const u8 = &[0][]const u8{},
- lld_argv: []const []const u8 = &[0][]const u8{},
- lib_dirs: []const []const u8 = &[0][]const u8{},
- rpath_list: []const []const u8 = &[0][]const u8{},
- c_source_files: []const CSourceFile = &[0]CSourceFile{},
- link_objects: []const []const u8 = &[0][]const u8{},
- framework_dirs: []const []const u8 = &[0][]const u8{},
- frameworks: []const []const u8 = &[0][]const u8{},
- system_libs: []const []const u8 = &[0][]const u8{},
- link_libc: bool = false,
- link_libcpp: bool = false,
- want_pic: ?bool = null,
- want_sanitize_c: ?bool = null,
- want_stack_check: ?bool = null,
- want_valgrind: ?bool = null,
- use_llvm: ?bool = null,
- use_lld: ?bool = null,
- use_clang: ?bool = null,
- rdynamic: bool = false,
- strip: bool = false,
- single_threaded: bool = false,
- is_native_os: bool,
- time_report: bool = false,
- link_eh_frame_hdr: bool = false,
- linker_script: ?[]const u8 = null,
- version_script: ?[]const u8 = null,
- override_soname: ?[]const u8 = null,
- linker_gc_sections: ?bool = null,
- function_sections: ?bool = null,
- linker_allow_shlib_undefined: ?bool = null,
- linker_bind_global_refs_locally: ?bool = null,
- disable_c_depfile: bool = false,
- linker_z_nodelete: bool = false,
- linker_z_defs: bool = false,
- clang_passthrough_mode: bool = false,
- verbose_cc: bool = false,
- verbose_link: bool = false,
- verbose_tokenize: bool = false,
- verbose_ast: bool = false,
- verbose_ir: bool = false,
- verbose_llvm_ir: bool = false,
- verbose_cimport: bool = false,
- verbose_llvm_cpu_features: bool = false,
- is_test: bool = false,
- stack_size_override: ?u64 = null,
- self_exe_path: ?[]const u8 = null,
- version: ?std.builtin.Version = null,
- libc_installation: ?*const LibCInstallation = null,
- machine_code_model: std.builtin.CodeModel = .default,
- /// This is for stage1 and should be deleted upon completion of self-hosting.
- color: @import("main.zig").Color = .Auto,
-};
-
-pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
- const is_dyn_lib = switch (options.output_mode) {
- .Obj, .Exe => false,
- .Lib => (options.link_mode orelse .Static) == .Dynamic,
- };
- const is_exe_or_dyn_lib = switch (options.output_mode) {
- .Obj => false,
- .Lib => is_dyn_lib,
- .Exe => true,
- };
- const comp: *Compilation = comp: {
- // For allocations that have the same lifetime as Compilation. This arena is used only during this
- // initialization and then is freed in deinit().
- var arena_allocator = std.heap.ArenaAllocator.init(gpa);
- errdefer arena_allocator.deinit();
- const arena = &arena_allocator.allocator;
-
- // We put the `Compilation` itself in the arena. Freeing the arena will free the module.
- // It's initialized later after we prepare the initialization options.
- const comp = try arena.create(Compilation);
- const root_name = try arena.dupe(u8, options.root_name);
-
- const ofmt = options.object_format orelse options.target.getObjectFormat();
-
- // Make a decision on whether to use LLD or our own linker.
- const use_lld = if (options.use_lld) |explicit| explicit else blk: {
- if (!build_options.have_llvm)
- break :blk false;
-
- if (ofmt == .c)
- break :blk false;
-
- // Our linker can't handle objects or most advanced options yet.
- if (options.link_objects.len != 0 or
- options.c_source_files.len != 0 or
- options.frameworks.len != 0 or
- options.system_libs.len != 0 or
- options.link_libc or options.link_libcpp or
- options.link_eh_frame_hdr or
- options.output_mode == .Lib or
- options.lld_argv.len != 0 or
- options.linker_script != null or options.version_script != null)
- {
- break :blk true;
- }
-
- if (build_options.is_stage1) {
- // If stage1 generates an object file, self-hosted linker is not
- // yet sophisticated enough to handle that.
- break :blk options.root_pkg != null;
- }
-
- break :blk false;
- };
-
- // Make a decision on whether to use LLVM or our own backend.
- const use_llvm = if (options.use_llvm) |explicit| explicit else blk: {
- // If we have no zig code to compile, no need for LLVM.
- if (options.root_pkg == null)
- break :blk false;
-
- // If we are the stage1 compiler, we depend on the stage1 c++ llvm backend
- // to compile zig code.
- if (build_options.is_stage1)
- break :blk true;
-
- // We would want to prefer LLVM for release builds when it is available, however
- // we don't have an LLVM backend yet :)
- // We would also want to prefer LLVM for architectures that we don't have self-hosted support for too.
- break :blk false;
- };
- if (!use_llvm and options.machine_code_model != .default) {
- return error.MachineCodeModelNotSupported;
- }
-
- const must_dynamic_link = dl: {
- if (target_util.cannotDynamicLink(options.target))
- break :dl false;
- if (target_util.osRequiresLibC(options.target))
- break :dl true;
- if (is_exe_or_dyn_lib and options.link_libc and options.target.isGnuLibC())
- break :dl true;
- if (options.system_libs.len != 0)
- break :dl true;
-
- break :dl false;
- };
- const default_link_mode: std.builtin.LinkMode = if (must_dynamic_link) .Dynamic else .Static;
- const link_mode: std.builtin.LinkMode = if (options.link_mode) |lm| blk: {
- if (lm == .Static and must_dynamic_link) {
- return error.UnableToStaticLink;
- }
- break :blk lm;
- } else default_link_mode;
-
- const dll_export_fns = if (options.dll_export_fns) |explicit| explicit else is_dyn_lib;
-
- const libc_dirs = try detectLibCIncludeDirs(
- arena,
- options.zig_lib_directory.path.?,
- options.target,
- options.is_native_os,
- options.link_libc,
- options.libc_installation,
- );
-
- const must_pic: bool = b: {
- if (target_util.requiresPIC(options.target, options.link_libc))
- break :b true;
- break :b link_mode == .Dynamic;
- };
- const pic = if (options.want_pic) |explicit| pic: {
- if (!explicit and must_pic) {
- return error.TargetRequiresPIC;
- }
- break :pic explicit;
- } else must_pic;
-
- if (options.emit_h != null) fatal("-femit-h not supported yet", .{}); // TODO
-
- const emit_bin = options.emit_bin orelse fatal("-fno-emit-bin not supported yet", .{}); // TODO
-
- // Make a decision on whether to use Clang for translate-c and compiling C files.
- const use_clang = if (options.use_clang) |explicit| explicit else blk: {
- if (build_options.have_llvm) {
- // Can't use it if we don't have it!
- break :blk false;
- }
- // It's not planned to do our own translate-c or C compilation.
- break :blk true;
- };
-
- const is_safe_mode = switch (options.optimize_mode) {
- .Debug, .ReleaseSafe => true,
- .ReleaseFast, .ReleaseSmall => false,
- };
-
- const sanitize_c = options.want_sanitize_c orelse is_safe_mode;
-
- const stack_check: bool = b: {
- if (!target_util.supportsStackProbing(options.target))
- break :b false;
- break :b options.want_stack_check orelse is_safe_mode;
- };
-
- const valgrind: bool = b: {
- if (!target_util.hasValgrindSupport(options.target))
- break :b false;
- break :b options.want_valgrind orelse (options.optimize_mode == .Debug);
- };
-
- const single_threaded = options.single_threaded or target_util.isSingleThreaded(options.target);
- const function_sections = options.function_sections orelse false;
-
- const llvm_cpu_features: ?[*:0]const u8 = if (build_options.have_llvm and use_llvm) blk: {
- var buf = std.ArrayList(u8).init(arena);
- for (options.target.cpu.arch.allFeaturesList()) |feature, index_usize| {
- const index = @intCast(Target.Cpu.Feature.Set.Index, index_usize);
- const is_enabled = options.target.cpu.features.isEnabled(index);
-
- if (feature.llvm_name) |llvm_name| {
- const plus_or_minus = "-+"[@boolToInt(is_enabled)];
- try buf.ensureCapacity(buf.items.len + 2 + llvm_name.len);
- buf.appendAssumeCapacity(plus_or_minus);
- buf.appendSliceAssumeCapacity(llvm_name);
- buf.appendSliceAssumeCapacity(",");
- }
- }
- assert(mem.endsWith(u8, buf.items, ","));
- buf.items[buf.items.len - 1] = 0;
- buf.shrink(buf.items.len);
- break :blk buf.items[0 .. buf.items.len - 1 :0].ptr;
- } else null;
-
- // We put everything into the cache hash that *cannot be modified during an incremental update*.
- // For example, one cannot change the target between updates, but one can change source files,
- // so the target goes into the cache hash, but source files do not. This is so that we can
- // find the same binary and incrementally update it even if there are modified source files.
- // We do this even if outputting to the current directory because we need somewhere to store
- // incremental compilation metadata.
- const cache = try arena.create(Cache);
- cache.* = .{
- .gpa = gpa,
- .manifest_dir = try options.zig_cache_directory.handle.makeOpenPath("h", .{}),
- };
- errdefer cache.manifest_dir.close();
-
- // This is shared hasher state common to zig source and all C source files.
- cache.hash.addBytes(build_options.version);
- cache.hash.addBytes(options.zig_lib_directory.path orelse ".");
- cache.hash.add(options.optimize_mode);
- cache.hash.add(options.target.cpu.arch);
- cache.hash.addBytes(options.target.cpu.model.name);
- cache.hash.add(options.target.cpu.features.ints);
- cache.hash.add(options.target.os.tag);
- cache.hash.add(options.is_native_os);
- cache.hash.add(options.target.abi);
- cache.hash.add(ofmt);
- cache.hash.add(pic);
- cache.hash.add(stack_check);
- cache.hash.add(link_mode);
- cache.hash.add(function_sections);
- cache.hash.add(options.strip);
- cache.hash.add(options.link_libc);
- cache.hash.add(options.link_libcpp);
- cache.hash.add(options.output_mode);
- cache.hash.add(options.machine_code_model);
- // TODO audit this and make sure everything is in it
-
- const module: ?*Module = if (options.root_pkg) |root_pkg| blk: {
- // Options that are specific to zig source files, that cannot be
- // modified between incremental updates.
- var hash = cache.hash;
-
- // Here we put the root source file path name, but *not* with addFile. We want the
- // hash to be the same regardless of the contents of the source file, because
- // incremental compilation will handle it, but we do want to namespace different
- // source file names because they are likely different compilations and therefore this
- // would be likely to cause cache hits.
- hash.addBytes(root_pkg.root_src_path);
- hash.addOptionalBytes(root_pkg.root_src_directory.path);
- hash.add(valgrind);
- hash.add(single_threaded);
- hash.add(options.target.os.getVersionRange());
- hash.add(dll_export_fns);
-
- const digest = hash.final();
- const artifact_sub_dir = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest });
- var artifact_dir = try options.zig_cache_directory.handle.makeOpenPath(artifact_sub_dir, .{});
- errdefer artifact_dir.close();
- const zig_cache_artifact_directory: Directory = .{
- .handle = artifact_dir,
- .path = if (options.zig_cache_directory.path) |p|
- try std.fs.path.join(arena, &[_][]const u8{ p, artifact_sub_dir })
- else
- artifact_sub_dir,
- };
-
- // TODO when we implement serialization and deserialization of incremental compilation metadata,
- // this is where we would load it. We have open a handle to the directory where
- // the output either already is, or will be.
- // However we currently do not have serialization of such metadata, so for now
- // we set up an empty Module that does the entire compilation fresh.
-
- const root_scope = rs: {
- if (mem.endsWith(u8, root_pkg.root_src_path, ".zig")) {
- const root_scope = try gpa.create(Module.Scope.File);
- root_scope.* = .{
- .sub_file_path = root_pkg.root_src_path,
- .source = .{ .unloaded = {} },
- .contents = .{ .not_available = {} },
- .status = .never_loaded,
- .root_container = .{
- .file_scope = root_scope,
- .decls = .{},
- },
- };
- break :rs &root_scope.base;
- } else if (mem.endsWith(u8, root_pkg.root_src_path, ".zir")) {
- const root_scope = try gpa.create(Module.Scope.ZIRModule);
- root_scope.* = .{
- .sub_file_path = root_pkg.root_src_path,
- .source = .{ .unloaded = {} },
- .contents = .{ .not_available = {} },
- .status = .never_loaded,
- .decls = .{},
- };
- break :rs &root_scope.base;
- } else {
- unreachable;
- }
- };
-
- const module = try arena.create(Module);
- module.* = .{
- .gpa = gpa,
- .comp = comp,
- .root_pkg = root_pkg,
- .root_scope = root_scope,
- .zig_cache_artifact_directory = zig_cache_artifact_directory,
- };
- break :blk module;
- } else null;
- errdefer if (module) |zm| zm.deinit();
-
- // For resource management purposes.
- var owned_link_dir: ?std.fs.Dir = null;
- errdefer if (owned_link_dir) |*dir| dir.close();
-
- const bin_directory = emit_bin.directory orelse blk: {
- if (module) |zm| break :blk zm.zig_cache_artifact_directory;
-
- // We could use the cache hash as is no problem, however, we increase
- // the likelihood of cache hits by adding the first C source file
- // path name (not contents) to the hash. This way if the user is compiling
- // foo.c and bar.c as separate compilations, they get different cache
- // directories.
- var hash = cache.hash;
- if (options.c_source_files.len >= 1) {
- hash.addBytes(options.c_source_files[0].src_path);
- } else if (options.link_objects.len >= 1) {
- hash.addBytes(options.link_objects[0]);
- }
-
- const digest = hash.final();
- const artifact_sub_dir = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest });
- var artifact_dir = try options.zig_cache_directory.handle.makeOpenPath(artifact_sub_dir, .{});
- owned_link_dir = artifact_dir;
- const link_artifact_directory: Directory = .{
- .handle = artifact_dir,
- .path = try options.zig_cache_directory.join(arena, &[_][]const u8{artifact_sub_dir}),
- };
- break :blk link_artifact_directory;
- };
-
- const error_return_tracing = !options.strip and switch (options.optimize_mode) {
- .Debug, .ReleaseSafe => true,
- .ReleaseFast, .ReleaseSmall => false,
- };
-
- const bin_file = try link.File.openPath(gpa, .{
- .directory = bin_directory,
- .sub_path = emit_bin.basename,
- .root_name = root_name,
- .module = module,
- .target = options.target,
- .dynamic_linker = options.dynamic_linker,
- .output_mode = options.output_mode,
- .link_mode = link_mode,
- .object_format = ofmt,
- .optimize_mode = options.optimize_mode,
- .use_lld = use_lld,
- .use_llvm = use_llvm,
- .link_libc = options.link_libc,
- .link_libcpp = options.link_libcpp,
- .objects = options.link_objects,
- .frameworks = options.frameworks,
- .framework_dirs = options.framework_dirs,
- .system_libs = options.system_libs,
- .lib_dirs = options.lib_dirs,
- .rpath_list = options.rpath_list,
- .strip = options.strip,
- .is_native_os = options.is_native_os,
- .function_sections = options.function_sections orelse false,
- .allow_shlib_undefined = options.linker_allow_shlib_undefined,
- .bind_global_refs_locally = options.linker_bind_global_refs_locally orelse false,
- .z_nodelete = options.linker_z_nodelete,
- .z_defs = options.linker_z_defs,
- .stack_size_override = options.stack_size_override,
- .linker_script = options.linker_script,
- .version_script = options.version_script,
- .gc_sections = options.linker_gc_sections,
- .eh_frame_hdr = options.link_eh_frame_hdr,
- .rdynamic = options.rdynamic,
- .extra_lld_args = options.lld_argv,
- .override_soname = options.override_soname,
- .version = options.version,
- .libc_installation = libc_dirs.libc_installation,
- .pic = pic,
- .valgrind = valgrind,
- .stack_check = stack_check,
- .single_threaded = single_threaded,
- .verbose_link = options.verbose_link,
- .machine_code_model = options.machine_code_model,
- .dll_export_fns = dll_export_fns,
- .error_return_tracing = error_return_tracing,
- .llvm_cpu_features = llvm_cpu_features,
- });
- errdefer bin_file.destroy();
-
- comp.* = .{
- .gpa = gpa,
- .arena_state = arena_allocator.state,
- .zig_lib_directory = options.zig_lib_directory,
- .zig_cache_directory = options.zig_cache_directory,
- .bin_file = bin_file,
- .work_queue = std.fifo.LinearFifo(Job, .Dynamic).init(gpa),
- .keep_source_files_loaded = options.keep_source_files_loaded,
- .use_clang = use_clang,
- .clang_argv = options.clang_argv,
- .c_source_files = options.c_source_files,
- .cache_parent = cache,
- .self_exe_path = options.self_exe_path,
- .libc_include_dir_list = libc_dirs.libc_include_dir_list,
- .sanitize_c = sanitize_c,
- .rand = options.rand,
- .clang_passthrough_mode = options.clang_passthrough_mode,
- .verbose_cc = options.verbose_cc,
- .verbose_tokenize = options.verbose_tokenize,
- .verbose_ast = options.verbose_ast,
- .verbose_ir = options.verbose_ir,
- .verbose_llvm_ir = options.verbose_llvm_ir,
- .verbose_cimport = options.verbose_cimport,
- .verbose_llvm_cpu_features = options.verbose_llvm_cpu_features,
- .disable_c_depfile = options.disable_c_depfile,
- .owned_link_dir = owned_link_dir,
- .is_test = options.is_test,
- .color = options.color,
- .time_report = options.time_report,
- };
- break :comp comp;
- };
- errdefer comp.destroy();
-
- if (comp.bin_file.options.module) |mod| {
- try comp.work_queue.writeItem(.{ .generate_builtin_zig = {} });
- }
-
- // Add a `CObject` for each `c_source_files`.
- try comp.c_object_table.ensureCapacity(gpa, options.c_source_files.len);
- for (options.c_source_files) |c_source_file| {
- const c_object = try gpa.create(CObject);
- errdefer gpa.destroy(c_object);
-
- c_object.* = .{
- .status = .{ .new = {} },
- .src = c_source_file,
- };
- comp.c_object_table.putAssumeCapacityNoClobber(c_object, {});
- }
-
- // If we need to build glibc for the target, add work items for it.
- // We go through the work queue so that building can be done in parallel.
- if (comp.wantBuildGLibCFromSource()) {
- try comp.addBuildingGLibCJobs();
- }
- if (comp.wantBuildLibUnwindFromSource()) {
- try comp.work_queue.writeItem(.{ .libunwind = {} });
- }
- if (build_options.is_stage1 and comp.bin_file.options.use_llvm) {
- try comp.work_queue.writeItem(.{ .stage1_module = {} });
- }
- if (is_exe_or_dyn_lib) {
- try comp.work_queue.writeItem(.{ .libcompiler_rt = {} });
- if (!comp.bin_file.options.link_libc) {
- try comp.work_queue.writeItem(.{ .zig_libc = {} });
- }
- }
-
- return comp;
-}
-
-fn releaseStage1Lock(comp: *Compilation) void {
- if (comp.stage1_lock) |*lock| {
- lock.release();
- comp.stage1_lock = null;
- }
-}
-
-pub fn destroy(self: *Compilation) void {
- const optional_module = self.bin_file.options.module;
- self.bin_file.destroy();
- if (optional_module) |module| module.deinit();
-
- self.releaseStage1Lock();
-
- const gpa = self.gpa;
- self.work_queue.deinit();
-
- {
- var it = self.crt_files.iterator();
- while (it.next()) |entry| {
- entry.value.deinit(gpa);
- }
- self.crt_files.deinit(gpa);
- }
-
- if (self.libunwind_static_lib) |*crt_file| {
- crt_file.deinit(gpa);
- }
- if (self.compiler_rt_static_lib) |*crt_file| {
- crt_file.deinit(gpa);
- }
- if (self.libc_static_lib) |*crt_file| {
- crt_file.deinit(gpa);
- }
-
- for (self.c_object_table.items()) |entry| {
- entry.key.destroy(gpa);
- }
- self.c_object_table.deinit(gpa);
-
- for (self.failed_c_objects.items()) |entry| {
- entry.value.destroy(gpa);
- }
- self.failed_c_objects.deinit(gpa);
-
- self.cache_parent.manifest_dir.close();
- if (self.owned_link_dir) |*dir| dir.close();
-
- // This destroys `self`.
- self.arena_state.promote(gpa).deinit();
-}
-
-pub fn getTarget(self: Compilation) Target {
- return self.bin_file.options.target;
-}
-
-/// Detect changes to source files, perform semantic analysis, and update the output files.
-pub fn update(self: *Compilation) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- // For compiling C objects, we rely on the cache hash system to avoid duplicating work.
- // Add a Job for each C object.
- try self.work_queue.ensureUnusedCapacity(self.c_object_table.items().len);
- for (self.c_object_table.items()) |entry| {
- self.work_queue.writeItemAssumeCapacity(.{ .c_object = entry.key });
- }
-
- const use_stage1 = build_options.is_stage1 and self.bin_file.options.use_llvm;
- if (!use_stage1) {
- if (self.bin_file.options.module) |module| {
- module.generation += 1;
-
- // TODO Detect which source files changed.
- // Until then we simulate a full cache miss. Source files could have been loaded for any reason;
- // to force a refresh we unload now.
- if (module.root_scope.cast(Module.Scope.File)) |zig_file| {
- zig_file.unload(module.gpa);
- module.analyzeContainer(&zig_file.root_container) catch |err| switch (err) {
- error.AnalysisFail => {
- assert(self.totalErrorCount() != 0);
- },
- else => |e| return e,
- };
- } else if (module.root_scope.cast(Module.Scope.ZIRModule)) |zir_module| {
- zir_module.unload(module.gpa);
- module.analyzeRootZIRModule(zir_module) catch |err| switch (err) {
- error.AnalysisFail => {
- assert(self.totalErrorCount() != 0);
- },
- else => |e| return e,
- };
- }
- }
- }
-
- try self.performAllTheWork();
-
- if (!use_stage1) {
- if (self.bin_file.options.module) |module| {
- // Process the deletion set.
- while (module.deletion_set.popOrNull()) |decl| {
- if (decl.dependants.items().len != 0) {
- decl.deletion_flag = false;
- continue;
- }
- try module.deleteDecl(decl);
- }
- }
- }
-
- if (self.totalErrorCount() != 0) {
- // Skip flushing.
- self.link_error_flags = .{};
- return;
- }
-
- // This is needed before reading the error flags.
- try self.bin_file.flush(self);
-
- self.link_error_flags = self.bin_file.errorFlags();
-
- // If there are any errors, we anticipate the source files being loaded
- // to report error messages. Otherwise we unload all source files to save memory.
- if (self.totalErrorCount() == 0 and !self.keep_source_files_loaded) {
- if (self.bin_file.options.module) |module| {
- module.root_scope.unload(self.gpa);
- }
- }
-}
-
-/// Having the file open for writing is problematic as far as executing the
-/// binary is concerned. This will remove the write flag, or close the file,
-/// or whatever is needed so that it can be executed.
-/// After this, one must call` makeFileWritable` before calling `update`.
-pub fn makeBinFileExecutable(self: *Compilation) !void {
- return self.bin_file.makeExecutable();
-}
-
-pub fn makeBinFileWritable(self: *Compilation) !void {
- return self.bin_file.makeWritable();
-}
-
-pub fn totalErrorCount(self: *Compilation) usize {
- var total: usize = self.failed_c_objects.items().len;
-
- if (self.bin_file.options.module) |module| {
- total += module.failed_decls.items().len +
- module.failed_exports.items().len +
- module.failed_files.items().len;
- }
-
- // The "no entry point found" error only counts if there are no other errors.
- if (total == 0) {
- return @boolToInt(self.link_error_flags.no_entry_point_found);
- }
-
- return total;
-}
-
-pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
- var arena = std.heap.ArenaAllocator.init(self.gpa);
- errdefer arena.deinit();
-
- var errors = std.ArrayList(AllErrors.Message).init(self.gpa);
- defer errors.deinit();
-
- for (self.failed_c_objects.items()) |entry| {
- const c_object = entry.key;
- const err_msg = entry.value;
- try AllErrors.add(&arena, &errors, c_object.src.src_path, "", err_msg.*);
- }
- if (self.bin_file.options.module) |module| {
- for (module.failed_files.items()) |entry| {
- const scope = entry.key;
- const err_msg = entry.value;
- const source = try scope.getSource(module);
- try AllErrors.add(&arena, &errors, scope.subFilePath(), source, err_msg.*);
- }
- for (module.failed_decls.items()) |entry| {
- const decl = entry.key;
- const err_msg = entry.value;
- const source = try decl.scope.getSource(module);
- try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*);
- }
- for (module.failed_exports.items()) |entry| {
- const decl = entry.key.owner_decl;
- const err_msg = entry.value;
- const source = try decl.scope.getSource(module);
- try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*);
- }
- }
-
- if (errors.items.len == 0 and self.link_error_flags.no_entry_point_found) {
- const global_err_src_path = blk: {
- if (self.bin_file.options.module) |module| break :blk module.root_pkg.root_src_path;
- if (self.c_source_files.len != 0) break :blk self.c_source_files[0].src_path;
- if (self.bin_file.options.objects.len != 0) break :blk self.bin_file.options.objects[0];
- break :blk "(no file)";
- };
- try errors.append(.{
- .src_path = global_err_src_path,
- .line = 0,
- .column = 0,
- .byte_offset = 0,
- .msg = try std.fmt.allocPrint(&arena.allocator, "no entry point found", .{}),
- });
- }
-
- assert(errors.items.len == self.totalErrorCount());
-
- return AllErrors{
- .list = try arena.allocator.dupe(AllErrors.Message, errors.items),
- .arena = arena.state,
- };
-}
-
-pub fn performAllTheWork(self: *Compilation) error{OutOfMemory}!void {
- while (self.work_queue.readItem()) |work_item| switch (work_item) {
- .codegen_decl => |decl| switch (decl.analysis) {
- .unreferenced => unreachable,
- .in_progress => unreachable,
- .outdated => unreachable,
-
- .sema_failure,
- .codegen_failure,
- .dependency_failure,
- .sema_failure_retryable,
- => continue,
-
- .complete, .codegen_failure_retryable => {
- const module = self.bin_file.options.module.?;
- if (decl.typed_value.most_recent.typed_value.val.cast(Value.Payload.Function)) |payload| {
- switch (payload.func.analysis) {
- .queued => module.analyzeFnBody(decl, payload.func) catch |err| switch (err) {
- error.AnalysisFail => {
- assert(payload.func.analysis != .in_progress);
- continue;
- },
- error.OutOfMemory => return error.OutOfMemory,
- },
- .in_progress => unreachable,
- .sema_failure, .dependency_failure => continue,
- .success => {},
- }
- // Here we tack on additional allocations to the Decl's arena. The allocations are
- // lifetime annotations in the ZIR.
- var decl_arena = decl.typed_value.most_recent.arena.?.promote(module.gpa);
- defer decl.typed_value.most_recent.arena.?.* = decl_arena.state;
- log.debug("analyze liveness of {}\n", .{decl.name});
- try liveness.analyze(module.gpa, &decl_arena.allocator, payload.func.analysis.success);
- }
-
- assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits());
-
- self.bin_file.updateDecl(module, decl) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- error.AnalysisFail => {
- decl.analysis = .dependency_failure;
- },
- else => {
- try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1);
- module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
- module.gpa,
- decl.src(),
- "unable to codegen: {}",
- .{@errorName(err)},
- ));
- decl.analysis = .codegen_failure_retryable;
- },
- };
- },
- },
- .analyze_decl => |decl| {
- const module = self.bin_file.options.module.?;
- module.ensureDeclAnalyzed(decl) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- error.AnalysisFail => continue,
- };
- },
- .update_line_number => |decl| {
- const module = self.bin_file.options.module.?;
- self.bin_file.updateDeclLineNumber(module, decl) catch |err| {
- try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1);
- module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
- module.gpa,
- decl.src(),
- "unable to update line number: {}",
- .{@errorName(err)},
- ));
- decl.analysis = .codegen_failure_retryable;
- };
- },
- .c_object => |c_object| {
- self.updateCObject(c_object) catch |err| switch (err) {
- error.AnalysisFail => continue,
- else => {
- try self.failed_c_objects.ensureCapacity(self.gpa, self.failed_c_objects.items().len + 1);
- self.failed_c_objects.putAssumeCapacityNoClobber(c_object, try ErrorMsg.create(
- self.gpa,
- 0,
- "unable to build C object: {}",
- .{@errorName(err)},
- ));
- c_object.status = .{ .failure = {} };
- },
- };
- },
- .glibc_crt_file => |crt_file| {
- glibc.buildCRTFile(self, crt_file) catch |err| {
- // TODO Expose this as a normal compile error rather than crashing here.
- fatal("unable to build glibc CRT file: {}", .{@errorName(err)});
- };
- },
- .glibc_shared_objects => {
- glibc.buildSharedObjects(self) catch |err| {
- // TODO Expose this as a normal compile error rather than crashing here.
- fatal("unable to build glibc shared objects: {}", .{@errorName(err)});
- };
- },
- .libunwind => {
- libunwind.buildStaticLib(self) catch |err| {
- // TODO Expose this as a normal compile error rather than crashing here.
- fatal("unable to build libunwind: {}", .{@errorName(err)});
- };
- },
- .libcompiler_rt => {
- self.buildStaticLibFromZig("compiler_rt.zig", &self.compiler_rt_static_lib) catch |err| {
- // TODO Expose this as a normal compile error rather than crashing here.
- fatal("unable to build compiler_rt: {}", .{@errorName(err)});
- };
- },
- .zig_libc => {
- self.buildStaticLibFromZig("c.zig", &self.libc_static_lib) catch |err| {
- // TODO Expose this as a normal compile error rather than crashing here.
- fatal("unable to build zig's multitarget libc: {}", .{@errorName(err)});
- };
- },
- .generate_builtin_zig => {
- // This Job is only queued up if there is a zig module.
- self.updateBuiltinZigFile(self.bin_file.options.module.?) catch |err| {
- // TODO Expose this as a normal compile error rather than crashing here.
- fatal("unable to update builtin.zig file: {}", .{@errorName(err)});
- };
- },
- .stage1_module => {
- self.updateStage1Module() catch |err| {
- fatal("unable to build stage1 zig object: {}", .{@errorName(err)});
- };
- },
- };
-}
-
-fn updateCObject(comp: *Compilation, c_object: *CObject) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- if (!build_options.have_llvm) {
- return comp.failCObj(c_object, "clang not available: compiler built without LLVM extensions", .{});
- }
- const self_exe_path = comp.self_exe_path orelse
- return comp.failCObj(c_object, "clang compilation disabled", .{});
-
- if (c_object.clearStatus(comp.gpa)) {
- // There was previous failure.
- comp.failed_c_objects.removeAssertDiscard(c_object);
- }
-
- var ch = comp.cache_parent.obtain();
- defer ch.deinit();
-
- ch.hash.add(comp.sanitize_c);
- ch.hash.addListOfBytes(comp.clang_argv);
- ch.hash.add(comp.bin_file.options.link_libcpp);
- ch.hash.addListOfBytes(comp.libc_include_dir_list);
- _ = try ch.addFile(c_object.src.src_path, null);
- {
- // Hash the extra flags, with special care to call addFile for file parameters.
- // TODO this logic can likely be improved by utilizing clang_options_data.zig.
- const file_args = [_][]const u8{"-include"};
- var arg_i: usize = 0;
- while (arg_i < c_object.src.extra_flags.len) : (arg_i += 1) {
- const arg = c_object.src.extra_flags[arg_i];
- ch.hash.addBytes(arg);
- for (file_args) |file_arg| {
- if (mem.eql(u8, file_arg, arg) and arg_i + 1 < c_object.src.extra_flags.len) {
- arg_i += 1;
- _ = try ch.addFile(c_object.src.extra_flags[arg_i], null);
- }
- }
- }
- }
-
- var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
- defer arena_allocator.deinit();
- const arena = &arena_allocator.allocator;
-
- const c_source_basename = std.fs.path.basename(c_object.src.src_path);
- // Special case when doing build-obj for just one C file. When there are more than one object
- // file and building an object we need to link them together, but with just one it should go
- // directly to the output file.
- const direct_o = comp.c_source_files.len == 1 and comp.bin_file.options.module == null and
- comp.bin_file.options.output_mode == .Obj and comp.bin_file.options.objects.len == 0;
- const o_basename_noext = if (direct_o)
- comp.bin_file.options.root_name
- else
- mem.split(c_source_basename, ".").next().?;
- const o_basename = try std.fmt.allocPrint(arena, "{}{}", .{ o_basename_noext, comp.getTarget().oFileExt() });
-
- const digest = if ((try ch.hit()) and !comp.disable_c_depfile) ch.final() else blk: {
- var argv = std.ArrayList([]const u8).init(comp.gpa);
- defer argv.deinit();
-
- // We can't know the digest until we do the C compiler invocation, so we need a temporary filename.
- const out_obj_path = try comp.tmpFilePath(arena, o_basename);
- var zig_cache_tmp_dir = try comp.zig_cache_directory.handle.makeOpenPath("tmp", .{});
- defer zig_cache_tmp_dir.close();
-
- try argv.appendSlice(&[_][]const u8{ self_exe_path, "clang", "-c" });
-
- const ext = classifyFileExt(c_object.src.src_path);
- const out_dep_path: ?[]const u8 = if (comp.disable_c_depfile or !ext.clangSupportsDepFile())
- null
- else
- try std.fmt.allocPrint(arena, "{}.d", .{out_obj_path});
- try comp.addCCArgs(arena, &argv, ext, false, out_dep_path);
-
- try argv.append("-o");
- try argv.append(out_obj_path);
-
- try argv.append(c_object.src.src_path);
- try argv.appendSlice(c_object.src.extra_flags);
-
- if (comp.verbose_cc) {
- dump_argv(argv.items);
- }
-
- const child = try std.ChildProcess.init(argv.items, arena);
- defer child.deinit();
-
- if (comp.clang_passthrough_mode) {
- child.stdin_behavior = .Inherit;
- child.stdout_behavior = .Inherit;
- child.stderr_behavior = .Inherit;
-
- const term = child.spawnAndWait() catch |err| {
- return comp.failCObj(c_object, "unable to spawn {}: {}", .{ argv.items[0], @errorName(err) });
- };
- switch (term) {
- .Exited => |code| {
- if (code != 0) {
- // TODO https://github.com/ziglang/zig/issues/6342
- std.process.exit(1);
- }
- },
- else => std.process.exit(1),
- }
- } else {
- child.stdin_behavior = .Ignore;
- child.stdout_behavior = .Pipe;
- child.stderr_behavior = .Pipe;
-
- try child.spawn();
-
- const stdout_reader = child.stdout.?.reader();
- const stderr_reader = child.stderr.?.reader();
-
- // TODO https://github.com/ziglang/zig/issues/6343
- const stdout = try stdout_reader.readAllAlloc(arena, std.math.maxInt(u32));
- const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);
-
- const term = child.wait() catch |err| {
- return comp.failCObj(c_object, "unable to spawn {}: {}", .{ argv.items[0], @errorName(err) });
- };
-
- switch (term) {
- .Exited => |code| {
- if (code != 0) {
- // TODO parse clang stderr and turn it into an error message
- // and then call failCObjWithOwnedErrorMsg
- std.log.err("clang failed with stderr: {}", .{stderr});
- return comp.failCObj(c_object, "clang exited with code {}", .{code});
- }
- },
- else => {
- std.log.err("clang terminated with stderr: {}", .{stderr});
- return comp.failCObj(c_object, "clang terminated unexpectedly", .{});
- },
- }
- }
-
- if (out_dep_path) |dep_file_path| {
- const dep_basename = std.fs.path.basename(dep_file_path);
- // Add the files depended on to the cache system.
- try ch.addDepFilePost(zig_cache_tmp_dir, dep_basename);
- // Just to save disk space, we delete the file because it is never needed again.
- zig_cache_tmp_dir.deleteFile(dep_basename) catch |err| {
- std.log.warn("failed to delete '{}': {}", .{ dep_file_path, @errorName(err) });
- };
- }
-
- // Rename into place.
- const digest = ch.final();
- const o_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest });
- var o_dir = try comp.zig_cache_directory.handle.makeOpenPath(o_sub_path, .{});
- defer o_dir.close();
- // TODO https://github.com/ziglang/zig/issues/6344
- const tmp_basename = std.fs.path.basename(out_obj_path);
- try std.os.renameat(zig_cache_tmp_dir.fd, tmp_basename, o_dir.fd, o_basename);
-
- ch.writeManifest() catch |err| {
- std.log.warn("failed to write cache manifest when compiling '{}': {}", .{ c_object.src.src_path, @errorName(err) });
- };
- break :blk digest;
- };
-
- const components = if (comp.zig_cache_directory.path) |p|
- &[_][]const u8{ p, "o", &digest, o_basename }
- else
- &[_][]const u8{ "o", &digest, o_basename };
-
- c_object.status = .{
- .success = .{
- .object_path = try std.fs.path.join(comp.gpa, components),
- .lock = ch.toOwnedLock(),
- },
- };
-}
-
-fn tmpFilePath(comp: *Compilation, arena: *Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 {
- const s = std.fs.path.sep_str;
- const rand_int = comp.rand.int(u64);
- if (comp.zig_cache_directory.path) |p| {
- return std.fmt.allocPrint(arena, "{}" ++ s ++ "tmp" ++ s ++ "{x}-{s}", .{ p, rand_int, suffix });
- } else {
- return std.fmt.allocPrint(arena, "tmp" ++ s ++ "{x}-{s}", .{ rand_int, suffix });
- }
-}
-
-/// Add common C compiler args between translate-c and C object compilation.
-pub fn addCCArgs(
- comp: *Compilation,
- arena: *Allocator,
- argv: *std.ArrayList([]const u8),
- ext: FileExt,
- translate_c: bool,
- out_dep_path: ?[]const u8,
-) !void {
- const target = comp.getTarget();
-
- if (translate_c) {
- try argv.appendSlice(&[_][]const u8{ "-x", "c" });
- }
-
- if (ext == .cpp) {
- try argv.append("-nostdinc++");
- }
-
- // We don't ever put `-fcolor-diagnostics` or `-fno-color-diagnostics` because in passthrough mode
- // we want Clang to infer it, and in normal mode we always want it off, which will be true since
- // clang will detect stderr as a pipe rather than a terminal.
- if (!comp.clang_passthrough_mode) {
- // Make stderr more easily parseable.
- try argv.append("-fno-caret-diagnostics");
- }
-
- if (comp.bin_file.options.function_sections) {
- try argv.append("-ffunction-sections");
- }
-
- try argv.ensureCapacity(argv.items.len + comp.bin_file.options.framework_dirs.len * 2);
- for (comp.bin_file.options.framework_dirs) |framework_dir| {
- argv.appendAssumeCapacity("-iframework");
- argv.appendAssumeCapacity(framework_dir);
- }
-
- if (comp.bin_file.options.link_libcpp) {
- const libcxx_include_path = try std.fs.path.join(arena, &[_][]const u8{
- comp.zig_lib_directory.path.?, "libcxx", "include",
- });
- const libcxxabi_include_path = try std.fs.path.join(arena, &[_][]const u8{
- comp.zig_lib_directory.path.?, "libcxxabi", "include",
- });
-
- try argv.append("-isystem");
- try argv.append(libcxx_include_path);
-
- try argv.append("-isystem");
- try argv.append(libcxxabi_include_path);
-
- if (target.abi.isMusl()) {
- try argv.append("-D_LIBCPP_HAS_MUSL_LIBC");
- }
- try argv.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS");
- try argv.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS");
- }
-
- const llvm_triple = try @import("codegen/llvm.zig").targetTriple(arena, target);
- try argv.appendSlice(&[_][]const u8{ "-target", llvm_triple });
-
- switch (ext) {
- .c, .cpp, .h => {
- try argv.appendSlice(&[_][]const u8{
- "-nostdinc",
- "-fno-spell-checking",
- });
-
- // According to Rich Felker libc headers are supposed to go before C language headers.
- // However as noted by @dimenus, appending libc headers before c_headers breaks intrinsics
- // and other compiler specific items.
- const c_headers_dir = try std.fs.path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, "include" });
- try argv.append("-isystem");
- try argv.append(c_headers_dir);
-
- for (comp.libc_include_dir_list) |include_dir| {
- try argv.append("-isystem");
- try argv.append(include_dir);
- }
-
- if (target.cpu.model.llvm_name) |llvm_name| {
- try argv.appendSlice(&[_][]const u8{
- "-Xclang", "-target-cpu", "-Xclang", llvm_name,
- });
- }
-
- // It would be really nice if there was a more compact way to communicate this info to Clang.
- const all_features_list = target.cpu.arch.allFeaturesList();
- try argv.ensureCapacity(argv.items.len + all_features_list.len * 4);
- for (all_features_list) |feature, index_usize| {
- const index = @intCast(std.Target.Cpu.Feature.Set.Index, index_usize);
- const is_enabled = target.cpu.features.isEnabled(index);
-
- if (feature.llvm_name) |llvm_name| {
- argv.appendSliceAssumeCapacity(&[_][]const u8{ "-Xclang", "-target-feature", "-Xclang" });
- const plus_or_minus = "-+"[@boolToInt(is_enabled)];
- const arg = try std.fmt.allocPrint(arena, "{c}{s}", .{ plus_or_minus, llvm_name });
- argv.appendAssumeCapacity(arg);
- }
- }
- const mcmodel = comp.bin_file.options.machine_code_model;
- if (mcmodel != .default) {
- try argv.append(try std.fmt.allocPrint(arena, "-mcmodel={}", .{@tagName(mcmodel)}));
- }
- if (translate_c) {
- // This gives us access to preprocessing entities, presumably at the cost of performance.
- try argv.append("-Xclang");
- try argv.append("-detailed-preprocessing-record");
- }
-
- // windows.h has files such as pshpack1.h which do #pragma packing, triggering a clang warning.
- // So for this target, we disable this warning.
- if (target.os.tag == .windows and target.abi.isGnu()) {
- try argv.append("-Wno-pragma-pack");
- }
-
- if (!comp.bin_file.options.strip) {
- try argv.append("-g");
- }
-
- if (comp.haveFramePointer()) {
- try argv.append("-fno-omit-frame-pointer");
- } else {
- try argv.append("-fomit-frame-pointer");
- }
-
- if (comp.sanitize_c) {
- try argv.append("-fsanitize=undefined");
- try argv.append("-fsanitize-trap=undefined");
- }
-
- switch (comp.bin_file.options.optimize_mode) {
- .Debug => {
- // windows c runtime requires -D_DEBUG if using debug libraries
- try argv.append("-D_DEBUG");
- try argv.append("-Og");
-
- if (comp.bin_file.options.link_libc) {
- try argv.append("-fstack-protector-strong");
- try argv.append("--param");
- try argv.append("ssp-buffer-size=4");
- } else {
- try argv.append("-fno-stack-protector");
- }
- },
- .ReleaseSafe => {
- // See the comment in the BuildModeFastRelease case for why we pass -O2 rather
- // than -O3 here.
- try argv.append("-O2");
- if (comp.bin_file.options.link_libc) {
- try argv.append("-D_FORTIFY_SOURCE=2");
- try argv.append("-fstack-protector-strong");
- try argv.append("--param");
- try argv.append("ssp-buffer-size=4");
- } else {
- try argv.append("-fno-stack-protector");
- }
- },
- .ReleaseFast => {
- try argv.append("-DNDEBUG");
- // Here we pass -O2 rather than -O3 because, although we do the equivalent of
- // -O3 in Zig code, the justification for the difference here is that Zig
- // has better detection and prevention of undefined behavior, so -O3 is safer for
- // Zig code than it is for C code. Also, C programmers are used to their code
- // running in -O2 and thus the -O3 path has been tested less.
- try argv.append("-O2");
- try argv.append("-fno-stack-protector");
- },
- .ReleaseSmall => {
- try argv.append("-DNDEBUG");
- try argv.append("-Os");
- try argv.append("-fno-stack-protector");
- },
- }
-
- if (target_util.supports_fpic(target) and comp.bin_file.options.pic) {
- try argv.append("-fPIC");
- }
- },
- .shared_library, .assembly, .ll, .bc, .unknown, .static_library, .object, .zig, .zir => {},
- }
- if (out_dep_path) |p| {
- try argv.appendSlice(&[_][]const u8{ "-MD", "-MV", "-MF", p });
- }
- // Argh, why doesn't the assembler accept the list of CPU features?!
- // I don't see a way to do this other than hard coding everything.
- switch (target.cpu.arch) {
- .riscv32, .riscv64 => {
- if (std.Target.riscv.featureSetHas(target.cpu.features, .relax)) {
- try argv.append("-mrelax");
- } else {
- try argv.append("-mno-relax");
- }
- },
- else => {
- // TODO
- },
- }
-
- if (target.os.tag == .freestanding) {
- try argv.append("-ffreestanding");
- }
-
- try argv.appendSlice(comp.clang_argv);
-}
-
-fn failCObj(comp: *Compilation, c_object: *CObject, comptime format: []const u8, args: anytype) InnerError {
- @setCold(true);
- const err_msg = try ErrorMsg.create(comp.gpa, 0, "unable to build C object: " ++ format, args);
- return comp.failCObjWithOwnedErrorMsg(c_object, err_msg);
-}
-
-fn failCObjWithOwnedErrorMsg(comp: *Compilation, c_object: *CObject, err_msg: *ErrorMsg) InnerError {
- {
- errdefer err_msg.destroy(comp.gpa);
- try comp.failed_c_objects.ensureCapacity(comp.gpa, comp.failed_c_objects.items().len + 1);
- }
- comp.failed_c_objects.putAssumeCapacityNoClobber(c_object, err_msg);
- c_object.status = .failure;
- return error.AnalysisFail;
-}
-
-pub const ErrorMsg = struct {
- byte_offset: usize,
- msg: []const u8,
-
- pub fn create(gpa: *Allocator, byte_offset: usize, comptime format: []const u8, args: anytype) !*ErrorMsg {
- const self = try gpa.create(ErrorMsg);
- errdefer gpa.destroy(self);
- self.* = try init(gpa, byte_offset, format, args);
- return self;
- }
-
- /// Assumes the ErrorMsg struct and msg were both allocated with allocator.
- pub fn destroy(self: *ErrorMsg, gpa: *Allocator) void {
- self.deinit(gpa);
- gpa.destroy(self);
- }
-
- pub fn init(gpa: *Allocator, byte_offset: usize, comptime format: []const u8, args: anytype) !ErrorMsg {
- return ErrorMsg{
- .byte_offset = byte_offset,
- .msg = try std.fmt.allocPrint(gpa, format, args),
- };
- }
-
- pub fn deinit(self: *ErrorMsg, gpa: *Allocator) void {
- gpa.free(self.msg);
- self.* = undefined;
- }
-};
-
-pub const FileExt = enum {
- c,
- cpp,
- h,
- ll,
- bc,
- assembly,
- shared_library,
- object,
- static_library,
- zig,
- zir,
- unknown,
-
- pub fn clangSupportsDepFile(ext: FileExt) bool {
- return switch (ext) {
- .c, .cpp, .h => true,
-
- .ll,
- .bc,
- .assembly,
- .shared_library,
- .object,
- .static_library,
- .zig,
- .zir,
- .unknown,
- => false,
- };
- }
-};
-
-pub fn hasObjectExt(filename: []const u8) bool {
- return mem.endsWith(u8, filename, ".o") or mem.endsWith(u8, filename, ".obj");
-}
-
-pub fn hasStaticLibraryExt(filename: []const u8) bool {
- return mem.endsWith(u8, filename, ".a") or mem.endsWith(u8, filename, ".lib");
-}
-
-pub fn hasCExt(filename: []const u8) bool {
- return mem.endsWith(u8, filename, ".c");
-}
-
-pub fn hasCppExt(filename: []const u8) bool {
- return mem.endsWith(u8, filename, ".C") or
- mem.endsWith(u8, filename, ".cc") or
- mem.endsWith(u8, filename, ".cpp") or
- mem.endsWith(u8, filename, ".cxx");
-}
-
-pub fn hasAsmExt(filename: []const u8) bool {
- return mem.endsWith(u8, filename, ".s") or mem.endsWith(u8, filename, ".S");
-}
-
-pub fn hasSharedLibraryExt(filename: []const u8) bool {
- if (mem.endsWith(u8, filename, ".so") or
- mem.endsWith(u8, filename, ".dll") or
- mem.endsWith(u8, filename, ".dylib"))
- {
- return true;
- }
- // Look for .so.X, .so.X.Y, .so.X.Y.Z
- var it = mem.split(filename, ".");
- _ = it.next().?;
- var so_txt = it.next() orelse return false;
- while (!mem.eql(u8, so_txt, "so")) {
- so_txt = it.next() orelse return false;
- }
- const n1 = it.next() orelse return false;
- const n2 = it.next();
- const n3 = it.next();
-
- _ = std.fmt.parseInt(u32, n1, 10) catch return false;
- if (n2) |x| _ = std.fmt.parseInt(u32, x, 10) catch return false;
- if (n3) |x| _ = std.fmt.parseInt(u32, x, 10) catch return false;
- if (it.next() != null) return false;
-
- return true;
-}
-
-pub fn classifyFileExt(filename: []const u8) FileExt {
- if (hasCExt(filename)) {
- return .c;
- } else if (hasCppExt(filename)) {
- return .cpp;
- } else if (mem.endsWith(u8, filename, ".ll")) {
- return .ll;
- } else if (mem.endsWith(u8, filename, ".bc")) {
- return .bc;
- } else if (hasAsmExt(filename)) {
- return .assembly;
- } else if (mem.endsWith(u8, filename, ".h")) {
- return .h;
- } else if (mem.endsWith(u8, filename, ".zig")) {
- return .zig;
- } else if (mem.endsWith(u8, filename, ".zir")) {
- return .zir;
- } else if (hasSharedLibraryExt(filename)) {
- return .shared_library;
- } else if (hasStaticLibraryExt(filename)) {
- return .static_library;
- } else if (hasObjectExt(filename)) {
- return .object;
- } else {
- return .unknown;
- }
-}
-
-test "classifyFileExt" {
- std.testing.expectEqual(FileExt.cpp, classifyFileExt("foo.cc"));
- std.testing.expectEqual(FileExt.unknown, classifyFileExt("foo.nim"));
- std.testing.expectEqual(FileExt.so, classifyFileExt("foo.so"));
- std.testing.expectEqual(FileExt.so, classifyFileExt("foo.so.1"));
- std.testing.expectEqual(FileExt.so, classifyFileExt("foo.so.1.2"));
- std.testing.expectEqual(FileExt.so, classifyFileExt("foo.so.1.2.3"));
- std.testing.expectEqual(FileExt.unknown, classifyFileExt("foo.so.1.2.3~"));
-}
-
-fn haveFramePointer(comp: *Compilation) bool {
- // If you complicate this logic make sure you update the parent cache hash.
- // Right now it's not in the cache hash because the value depends on optimize_mode
- // and strip which are both already part of the hash.
- return switch (comp.bin_file.options.optimize_mode) {
- .Debug, .ReleaseSafe => !comp.bin_file.options.strip,
- .ReleaseSmall, .ReleaseFast => false,
- };
-}
-
-const LibCDirs = struct {
- libc_include_dir_list: []const []const u8,
- libc_installation: ?*const LibCInstallation,
-};
-
-fn detectLibCIncludeDirs(
- arena: *Allocator,
- zig_lib_dir: []const u8,
- target: Target,
- is_native_os: bool,
- link_libc: bool,
- libc_installation: ?*const LibCInstallation,
-) !LibCDirs {
- if (!link_libc) {
- return LibCDirs{
- .libc_include_dir_list = &[0][]u8{},
- .libc_installation = null,
- };
- }
-
- if (libc_installation) |lci| {
- return detectLibCFromLibCInstallation(arena, target, lci);
- }
-
- if (target_util.canBuildLibC(target)) {
- const generic_name = target_util.libCGenericName(target);
- // Some architectures are handled by the same set of headers.
- const arch_name = if (target.abi.isMusl()) target_util.archMuslName(target.cpu.arch) else @tagName(target.cpu.arch);
- const os_name = @tagName(target.os.tag);
- // Musl's headers are ABI-agnostic and so they all have the "musl" ABI name.
- const abi_name = if (target.abi.isMusl()) "musl" else @tagName(target.abi);
- const s = std.fs.path.sep_str;
- const arch_include_dir = try std.fmt.allocPrint(
- arena,
- "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-{}-{}",
- .{ zig_lib_dir, arch_name, os_name, abi_name },
- );
- const generic_include_dir = try std.fmt.allocPrint(
- arena,
- "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "generic-{}",
- .{ zig_lib_dir, generic_name },
- );
- const arch_os_include_dir = try std.fmt.allocPrint(
- arena,
- "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-{}-any",
- .{ zig_lib_dir, @tagName(target.cpu.arch), os_name },
- );
- const generic_os_include_dir = try std.fmt.allocPrint(
- arena,
- "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-{}-any",
- .{ zig_lib_dir, os_name },
- );
-
- const list = try arena.alloc([]const u8, 4);
- list[0] = arch_include_dir;
- list[1] = generic_include_dir;
- list[2] = arch_os_include_dir;
- list[3] = generic_os_include_dir;
- return LibCDirs{
- .libc_include_dir_list = list,
- .libc_installation = null,
- };
- }
-
- if (is_native_os) {
- const libc = try arena.create(LibCInstallation);
- libc.* = try LibCInstallation.findNative(.{ .allocator = arena });
- return detectLibCFromLibCInstallation(arena, target, libc);
- }
-
- return LibCDirs{
- .libc_include_dir_list = &[0][]u8{},
- .libc_installation = null,
- };
-}
-
-fn detectLibCFromLibCInstallation(arena: *Allocator, target: Target, lci: *const LibCInstallation) !LibCDirs {
- var list = std.ArrayList([]const u8).init(arena);
- try list.ensureCapacity(4);
-
- list.appendAssumeCapacity(lci.include_dir.?);
-
- const is_redundant = mem.eql(u8, lci.sys_include_dir.?, lci.include_dir.?);
- if (!is_redundant) list.appendAssumeCapacity(lci.sys_include_dir.?);
-
- if (target.os.tag == .windows) {
- if (std.fs.path.dirname(lci.include_dir.?)) |include_dir_parent| {
- const um_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_parent, "um" });
- list.appendAssumeCapacity(um_dir);
-
- const shared_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_parent, "shared" });
- list.appendAssumeCapacity(shared_dir);
- }
- }
- return LibCDirs{
- .libc_include_dir_list = list.items,
- .libc_installation = lci,
- };
-}
-
-pub fn get_libc_crt_file(comp: *Compilation, arena: *Allocator, basename: []const u8) ![]const u8 {
- if (comp.wantBuildGLibCFromSource()) {
- return comp.crt_files.get(basename).?.full_object_path;
- }
- const lci = comp.bin_file.options.libc_installation orelse return error.LibCInstallationNotAvailable;
- const crt_dir_path = lci.crt_dir orelse return error.LibCInstallationMissingCRTDir;
- const full_path = try std.fs.path.join(arena, &[_][]const u8{ crt_dir_path, basename });
- return full_path;
-}
-
-fn addBuildingGLibCJobs(comp: *Compilation) !void {
- try comp.work_queue.write(&[_]Job{
- .{ .glibc_crt_file = .crti_o },
- .{ .glibc_crt_file = .crtn_o },
- .{ .glibc_crt_file = .scrt1_o },
- .{ .glibc_crt_file = .libc_nonshared_a },
- .{ .glibc_shared_objects = {} },
- });
-}
-
-fn wantBuildGLibCFromSource(comp: *Compilation) bool {
- const is_exe_or_dyn_lib = switch (comp.bin_file.options.output_mode) {
- .Obj => false,
- .Lib => comp.bin_file.options.link_mode == .Dynamic,
- .Exe => true,
- };
- return comp.bin_file.options.link_libc and is_exe_or_dyn_lib and
- comp.bin_file.options.libc_installation == null and
- comp.bin_file.options.target.isGnuLibC();
-}
-
-fn wantBuildLibUnwindFromSource(comp: *Compilation) bool {
- const is_exe_or_dyn_lib = switch (comp.bin_file.options.output_mode) {
- .Obj => false,
- .Lib => comp.bin_file.options.link_mode == .Dynamic,
- .Exe => true,
- };
- return comp.bin_file.options.link_libc and is_exe_or_dyn_lib and
- comp.bin_file.options.libc_installation == null;
-}
-
-fn updateBuiltinZigFile(comp: *Compilation, mod: *Module) !void {
- const source = try comp.generateBuiltinZigSource(comp.gpa);
- defer comp.gpa.free(source);
- try mod.zig_cache_artifact_directory.handle.writeFile("builtin.zig", source);
-}
-
-pub fn dump_argv(argv: []const []const u8) void {
- for (argv[0 .. argv.len - 1]) |arg| {
- std.debug.print("{} ", .{arg});
- }
- std.debug.print("{}\n", .{argv[argv.len - 1]});
-}
-
-pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8 {
- var buffer = std.ArrayList(u8).init(allocator);
- defer buffer.deinit();
-
- const target = comp.getTarget();
- const generic_arch_name = target.cpu.arch.genericName();
-
- @setEvalBranchQuota(4000);
- try buffer.writer().print(
- \\usingnamespace @import("std").builtin;
- \\/// Deprecated
- \\pub const arch = Target.current.cpu.arch;
- \\/// Deprecated
- \\pub const endian = Target.current.cpu.arch.endian();
- \\pub const output_mode = OutputMode.{};
- \\pub const link_mode = LinkMode.{};
- \\pub const is_test = {};
- \\pub const single_threaded = {};
- \\pub const abi = Abi.{};
- \\pub const cpu: Cpu = Cpu{{
- \\ .arch = .{},
- \\ .model = &Target.{}.cpu.{},
- \\ .features = Target.{}.featureSet(&[_]Target.{}.Feature{{
- \\
- , .{
- @tagName(comp.bin_file.options.output_mode),
- @tagName(comp.bin_file.options.link_mode),
- comp.is_test,
- comp.bin_file.options.single_threaded,
- @tagName(target.abi),
- @tagName(target.cpu.arch),
- generic_arch_name,
- target.cpu.model.name,
- generic_arch_name,
- generic_arch_name,
- });
-
- for (target.cpu.arch.allFeaturesList()) |feature, index_usize| {
- const index = @intCast(std.Target.Cpu.Feature.Set.Index, index_usize);
- const is_enabled = target.cpu.features.isEnabled(index);
- if (is_enabled) {
- // TODO some kind of "zig identifier escape" function rather than
- // unconditionally using @"" syntax
- try buffer.appendSlice(" .@\"");
- try buffer.appendSlice(feature.name);
- try buffer.appendSlice("\",\n");
- }
- }
-
- try buffer.writer().print(
- \\ }}),
- \\}};
- \\pub const os = Os{{
- \\ .tag = .{},
- \\ .version_range = .{{
- ,
- .{@tagName(target.os.tag)},
- );
-
- switch (target.os.getVersionRange()) {
- .none => try buffer.appendSlice(" .none = {} }\n"),
- .semver => |semver| try buffer.outStream().print(
- \\ .semver = .{{
- \\ .min = .{{
- \\ .major = {},
- \\ .minor = {},
- \\ .patch = {},
- \\ }},
- \\ .max = .{{
- \\ .major = {},
- \\ .minor = {},
- \\ .patch = {},
- \\ }},
- \\ }}}},
- \\
- , .{
- semver.min.major,
- semver.min.minor,
- semver.min.patch,
-
- semver.max.major,
- semver.max.minor,
- semver.max.patch,
- }),
- .linux => |linux| try buffer.outStream().print(
- \\ .linux = .{{
- \\ .range = .{{
- \\ .min = .{{
- \\ .major = {},
- \\ .minor = {},
- \\ .patch = {},
- \\ }},
- \\ .max = .{{
- \\ .major = {},
- \\ .minor = {},
- \\ .patch = {},
- \\ }},
- \\ }},
- \\ .glibc = .{{
- \\ .major = {},
- \\ .minor = {},
- \\ .patch = {},
- \\ }},
- \\ }}}},
- \\
- , .{
- linux.range.min.major,
- linux.range.min.minor,
- linux.range.min.patch,
-
- linux.range.max.major,
- linux.range.max.minor,
- linux.range.max.patch,
-
- linux.glibc.major,
- linux.glibc.minor,
- linux.glibc.patch,
- }),
- .windows => |windows| try buffer.outStream().print(
- \\ .windows = .{{
- \\ .min = {s},
- \\ .max = {s},
- \\ }}}},
- \\
- ,
- .{ windows.min, windows.max },
- ),
- }
- try buffer.appendSlice("};\n");
- try buffer.writer().print(
- \\pub const object_format = ObjectFormat.{};
- \\pub const mode = Mode.{};
- \\pub const link_libc = {};
- \\pub const link_libcpp = {};
- \\pub const have_error_return_tracing = {};
- \\pub const valgrind_support = {};
- \\pub const position_independent_code = {};
- \\pub const strip_debug_info = {};
- \\pub const code_model = CodeModel.{};
- \\
- , .{
- @tagName(comp.bin_file.options.object_format),
- @tagName(comp.bin_file.options.optimize_mode),
- comp.bin_file.options.link_libc,
- comp.bin_file.options.link_libcpp,
- comp.bin_file.options.error_return_tracing,
- comp.bin_file.options.valgrind,
- comp.bin_file.options.pic,
- comp.bin_file.options.strip,
- @tagName(comp.bin_file.options.machine_code_model),
- });
- return buffer.toOwnedSlice();
-}
-
-pub fn updateSubCompilation(sub_compilation: *Compilation) !void {
- try sub_compilation.update();
-
- // Look for compilation errors in this sub_compilation
- var errors = try sub_compilation.getAllErrorsAlloc();
- defer errors.deinit(sub_compilation.gpa);
-
- if (errors.list.len != 0) {
- for (errors.list) |full_err_msg| {
- std.log.err("{}:{}:{}: {}\n", .{
- full_err_msg.src_path,
- full_err_msg.line + 1,
- full_err_msg.column + 1,
- full_err_msg.msg,
- });
- }
- return error.BuildingLibCObjectFailed;
- }
-}
-
-fn buildStaticLibFromZig(comp: *Compilation, basename: []const u8, out: *?CRTFile) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- const special_sub = "std" ++ std.fs.path.sep_str ++ "special";
- const special_path = try comp.zig_lib_directory.join(comp.gpa, &[_][]const u8{special_sub});
- defer comp.gpa.free(special_path);
-
- var special_dir = try comp.zig_lib_directory.handle.openDir(special_sub, .{});
- defer special_dir.close();
-
- var root_pkg: Package = .{
- .root_src_directory = .{
- .path = special_path,
- .handle = special_dir,
- },
- .root_src_path = basename,
- };
-
- const emit_bin = Compilation.EmitLoc{
- .directory = null, // Put it in the cache directory.
- .basename = basename,
- };
- const optimize_mode: std.builtin.Mode = blk: {
- if (comp.is_test)
- break :blk comp.bin_file.options.optimize_mode;
- switch (comp.bin_file.options.optimize_mode) {
- .Debug, .ReleaseFast, .ReleaseSafe => break :blk .ReleaseFast,
- .ReleaseSmall => break :blk .ReleaseSmall,
- }
- };
- const sub_compilation = try Compilation.create(comp.gpa, .{
- // TODO use the global cache directory here
- .zig_cache_directory = comp.zig_cache_directory,
- .zig_lib_directory = comp.zig_lib_directory,
- .target = comp.getTarget(),
- .root_name = mem.split(basename, ".").next().?,
- .root_pkg = &root_pkg,
- .output_mode = .Lib,
- .rand = comp.rand,
- .libc_installation = comp.bin_file.options.libc_installation,
- .emit_bin = emit_bin,
- .optimize_mode = optimize_mode,
- .link_mode = .Static,
- .function_sections = true,
- .want_sanitize_c = false,
- .want_stack_check = false,
- .want_valgrind = false,
- .want_pic = comp.bin_file.options.pic,
- .emit_h = null,
- .strip = comp.bin_file.options.strip,
- .is_native_os = comp.bin_file.options.is_native_os,
- .self_exe_path = comp.self_exe_path,
- .verbose_cc = comp.verbose_cc,
- .verbose_link = comp.bin_file.options.verbose_link,
- .verbose_tokenize = comp.verbose_tokenize,
- .verbose_ast = comp.verbose_ast,
- .verbose_ir = comp.verbose_ir,
- .verbose_llvm_ir = comp.verbose_llvm_ir,
- .verbose_cimport = comp.verbose_cimport,
- .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
- .clang_passthrough_mode = comp.clang_passthrough_mode,
- });
- defer sub_compilation.destroy();
-
- try sub_compilation.updateSubCompilation();
-
- assert(out.* == null);
- out.* = Compilation.CRTFile{
- .full_object_path = try sub_compilation.bin_file.options.directory.join(comp.gpa, &[_][]const u8{basename}),
- .lock = sub_compilation.bin_file.toOwnedLock(),
- };
-}
-
-fn updateStage1Module(comp: *Compilation) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
- defer arena_allocator.deinit();
- const arena = &arena_allocator.allocator;
-
- // Here we use the legacy stage1 C++ compiler to compile Zig code.
- const mod = comp.bin_file.options.module.?;
- const directory = mod.zig_cache_artifact_directory; // Just an alias to make it shorter to type.
- const main_zig_file = try mod.root_pkg.root_src_directory.join(arena, &[_][]const u8{
- mod.root_pkg.root_src_path,
- });
- const zig_lib_dir = comp.zig_lib_directory.path.?;
- const builtin_zig_path = try directory.join(arena, &[_][]const u8{"builtin.zig"});
- const target = comp.getTarget();
- const id_symlink_basename = "stage1.id";
-
- // We are about to obtain this lock, so here we give other processes a chance first.
- comp.releaseStage1Lock();
-
- // Unlike with the self-hosted Zig module, stage1 does not support incremental compilation,
- // so we input all the zig source files into the cache hash system. We're going to keep
- // the artifact directory the same, however, so we take the same strategy as linking
- // does where we have a file which specifies the hash of the output directory so that we can
- // skip the expensive compilation step if the hash matches.
- var ch = comp.cache_parent.obtain();
- defer ch.deinit();
-
- _ = try ch.addFile(main_zig_file, null);
- ch.hash.add(comp.bin_file.options.valgrind);
- ch.hash.add(comp.bin_file.options.single_threaded);
- ch.hash.add(target.os.getVersionRange());
- ch.hash.add(comp.bin_file.options.dll_export_fns);
- ch.hash.add(comp.bin_file.options.function_sections);
-
- if (try ch.hit()) {
- const digest = ch.final();
-
- var prev_digest_buf: [digest.len]u8 = undefined;
- const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: {
- // Handle this as a cache miss.
- break :blk prev_digest_buf[0..0];
- };
- if (mem.eql(u8, prev_digest, &digest)) {
- comp.stage1_lock = ch.toOwnedLock();
- return;
- }
- }
-
- const stage2_target = try arena.create(stage1.Stage2Target);
- stage2_target.* = .{
- .arch = @enumToInt(target.cpu.arch) + 1, // skip over ZigLLVM_UnknownArch
- .os = @enumToInt(target.os.tag),
- .abi = @enumToInt(target.abi),
- .is_native_os = comp.bin_file.options.is_native_os,
- .is_native_cpu = false, // Only true when bootstrapping the compiler.
- .llvm_cpu_name = if (target.cpu.model.llvm_name) |s| s.ptr else null,
- .llvm_cpu_features = comp.bin_file.options.llvm_cpu_features.?,
- };
- var progress: std.Progress = .{};
- var main_progress_node = try progress.start("", 100);
- defer main_progress_node.end();
- if (comp.color == .Off) progress.terminal = null;
-
- comp.stage1_cache_hash = &ch;
-
- const stage1_module = stage1.create(
- @enumToInt(comp.bin_file.options.optimize_mode),
- undefined,
- 0, // TODO --main-pkg-path
- main_zig_file.ptr,
- main_zig_file.len,
- zig_lib_dir.ptr,
- zig_lib_dir.len,
- stage2_target,
- comp.is_test,
- ) orelse return error.OutOfMemory;
-
- const stage1_pkg = try arena.create(stage1.Pkg);
- stage1_pkg.* = .{
- .name_ptr = undefined,
- .name_len = 0,
- .path_ptr = undefined,
- .path_len = 0,
- .children_ptr = undefined,
- .children_len = 0,
- .parent = null,
- };
- const output_dir = comp.bin_file.options.directory.path orelse ".";
- stage1_module.* = .{
- .root_name_ptr = comp.bin_file.options.root_name.ptr,
- .root_name_len = comp.bin_file.options.root_name.len,
- .output_dir_ptr = output_dir.ptr,
- .output_dir_len = output_dir.len,
- .builtin_zig_path_ptr = builtin_zig_path.ptr,
- .builtin_zig_path_len = builtin_zig_path.len,
- .test_filter_ptr = "",
- .test_filter_len = 0,
- .test_name_prefix_ptr = "",
- .test_name_prefix_len = 0,
- .userdata = @ptrToInt(comp),
- .root_pkg = stage1_pkg,
- .code_model = @enumToInt(comp.bin_file.options.machine_code_model),
- .subsystem = stage1.TargetSubsystem.Auto,
- .err_color = @enumToInt(comp.color),
- .pic = comp.bin_file.options.pic,
- .link_libc = comp.bin_file.options.link_libc,
- .link_libcpp = comp.bin_file.options.link_libcpp,
- .strip = comp.bin_file.options.strip,
- .is_single_threaded = comp.bin_file.options.single_threaded,
- .dll_export_fns = comp.bin_file.options.dll_export_fns,
- .link_mode_dynamic = comp.bin_file.options.link_mode == .Dynamic,
- .valgrind_enabled = comp.bin_file.options.valgrind,
- .function_sections = comp.bin_file.options.function_sections,
- .enable_stack_probing = comp.bin_file.options.stack_check,
- .enable_time_report = comp.time_report,
- .enable_stack_report = false,
- .dump_analysis = false,
- .enable_doc_generation = false,
- .emit_bin = true,
- .emit_asm = false,
- .emit_llvm_ir = false,
- .test_is_evented = false,
- .verbose_tokenize = comp.verbose_tokenize,
- .verbose_ast = comp.verbose_ast,
- .verbose_ir = comp.verbose_ir,
- .verbose_llvm_ir = comp.verbose_llvm_ir,
- .verbose_cimport = comp.verbose_cimport,
- .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
- .main_progress_node = main_progress_node,
- };
- stage1_module.build_object();
- stage1_module.destroy();
-
- const digest = ch.final();
-
- // Update the dangling symlink with the digest. If it fails we can continue; it only
- // means that the next invocation will have an unnecessary cache miss.
- directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| {
- std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)});
- };
- // Again failure here only means an unnecessary cache miss.
- ch.writeManifest() catch |err| {
- std.log.warn("failed to write cache manifest when linking: {}", .{@errorName(err)});
- };
- // We hang on to this lock so that the output file path can be used without
- // other processes clobbering it.
- comp.stage1_lock = ch.toOwnedLock();
-}
diff --git a/src-self-hosted/DepTokenizer.zig b/src-self-hosted/DepTokenizer.zig
deleted file mode 100644
index cc2211a1aa1bb90ecbdc825b1633229886bbc408..0000000000000000000000000000000000000000
--- a/src-self-hosted/DepTokenizer.zig
+++ /dev/null
@@ -1,1064 +0,0 @@
-const Tokenizer = @This();
-
-index: usize = 0,
-bytes: []const u8,
-state: State = .lhs,
-
-const std = @import("std");
-const testing = std.testing;
-const assert = std.debug.assert;
-
-pub fn next(self: *Tokenizer) ?Token {
- var start = self.index;
- var must_resolve = false;
- while (self.index < self.bytes.len) {
- const char = self.bytes[self.index];
- switch (self.state) {
- .lhs => switch (char) {
- '\t', '\n', '\r', ' ' => {
- // silently ignore whitespace
- self.index += 1;
- },
- else => {
- start = self.index;
- self.state = .target;
- },
- },
- .target => switch (char) {
- '\t', '\n', '\r', ' ' => {
- return errorIllegalChar(.invalid_target, self.index, char);
- },
- '$' => {
- self.state = .target_dollar_sign;
- self.index += 1;
- },
- '\\' => {
- self.state = .target_reverse_solidus;
- self.index += 1;
- },
- ':' => {
- self.state = .target_colon;
- self.index += 1;
- },
- else => {
- self.index += 1;
- },
- },
- .target_reverse_solidus => switch (char) {
- '\t', '\n', '\r' => {
- return errorIllegalChar(.bad_target_escape, self.index, char);
- },
- ' ', '#', '\\' => {
- must_resolve = true;
- self.state = .target;
- self.index += 1;
- },
- '$' => {
- self.state = .target_dollar_sign;
- self.index += 1;
- },
- else => {
- self.state = .target;
- self.index += 1;
- },
- },
- .target_dollar_sign => switch (char) {
- '$' => {
- must_resolve = true;
- self.state = .target;
- self.index += 1;
- },
- else => {
- return errorIllegalChar(.expected_dollar_sign, self.index, char);
- },
- },
- .target_colon => switch (char) {
- '\n', '\r' => {
- const bytes = self.bytes[start .. self.index - 1];
- if (bytes.len != 0) {
- self.state = .lhs;
- return finishTarget(must_resolve, bytes);
- }
- // silently ignore null target
- self.state = .lhs;
- },
- '\\' => {
- self.state = .target_colon_reverse_solidus;
- self.index += 1;
- },
- else => {
- const bytes = self.bytes[start .. self.index - 1];
- if (bytes.len != 0) {
- self.state = .rhs;
- return finishTarget(must_resolve, bytes);
- }
- // silently ignore null target
- self.state = .lhs;
- },
- },
- .target_colon_reverse_solidus => switch (char) {
- '\n', '\r' => {
- const bytes = self.bytes[start .. self.index - 2];
- if (bytes.len != 0) {
- self.state = .lhs;
- return finishTarget(must_resolve, bytes);
- }
- // silently ignore null target
- self.state = .lhs;
- },
- else => {
- self.state = .target;
- },
- },
- .rhs => switch (char) {
- '\t', ' ' => {
- // silently ignore horizontal whitespace
- self.index += 1;
- },
- '\n', '\r' => {
- self.state = .lhs;
- },
- '\\' => {
- self.state = .rhs_continuation;
- self.index += 1;
- },
- '"' => {
- self.state = .prereq_quote;
- self.index += 1;
- start = self.index;
- },
- else => {
- start = self.index;
- self.state = .prereq;
- },
- },
- .rhs_continuation => switch (char) {
- '\n' => {
- self.state = .rhs;
- self.index += 1;
- },
- '\r' => {
- self.state = .rhs_continuation_linefeed;
- self.index += 1;
- },
- else => {
- return errorIllegalChar(.continuation_eol, self.index, char);
- },
- },
- .rhs_continuation_linefeed => switch (char) {
- '\n' => {
- self.state = .rhs;
- self.index += 1;
- },
- else => {
- return errorIllegalChar(.continuation_eol, self.index, char);
- },
- },
- .prereq_quote => switch (char) {
- '"' => {
- self.index += 1;
- self.state = .rhs;
- return Token{ .prereq = self.bytes[start .. self.index - 1] };
- },
- else => {
- self.index += 1;
- },
- },
- .prereq => switch (char) {
- '\t', ' ' => {
- self.state = .rhs;
- return Token{ .prereq = self.bytes[start..self.index] };
- },
- '\n', '\r' => {
- self.state = .lhs;
- return Token{ .prereq = self.bytes[start..self.index] };
- },
- '\\' => {
- self.state = .prereq_continuation;
- self.index += 1;
- },
- else => {
- self.index += 1;
- },
- },
- .prereq_continuation => switch (char) {
- '\n' => {
- self.index += 1;
- self.state = .rhs;
- return Token{ .prereq = self.bytes[start .. self.index - 2] };
- },
- '\r' => {
- self.state = .prereq_continuation_linefeed;
- self.index += 1;
- },
- else => {
- // not continuation
- self.state = .prereq;
- self.index += 1;
- },
- },
- .prereq_continuation_linefeed => switch (char) {
- '\n' => {
- self.index += 1;
- self.state = .rhs;
- return Token{ .prereq = self.bytes[start .. self.index - 1] };
- },
- else => {
- return errorIllegalChar(.continuation_eol, self.index, char);
- },
- },
- }
- } else {
- switch (self.state) {
- .lhs,
- .rhs,
- .rhs_continuation,
- .rhs_continuation_linefeed,
- => return null,
- .target => {
- return errorPosition(.incomplete_target, start, self.bytes[start..]);
- },
- .target_reverse_solidus,
- .target_dollar_sign,
- => {
- const idx = self.index - 1;
- return errorIllegalChar(.incomplete_escape, idx, self.bytes[idx]);
- },
- .target_colon => {
- const bytes = self.bytes[start .. self.index - 1];
- if (bytes.len != 0) {
- self.index += 1;
- self.state = .rhs;
- return finishTarget(must_resolve, bytes);
- }
- // silently ignore null target
- self.state = .lhs;
- return null;
- },
- .target_colon_reverse_solidus => {
- const bytes = self.bytes[start .. self.index - 2];
- if (bytes.len != 0) {
- self.index += 1;
- self.state = .rhs;
- return finishTarget(must_resolve, bytes);
- }
- // silently ignore null target
- self.state = .lhs;
- return null;
- },
- .prereq_quote => {
- return errorPosition(.incomplete_quoted_prerequisite, start, self.bytes[start..]);
- },
- .prereq => {
- self.state = .lhs;
- return Token{ .prereq = self.bytes[start..] };
- },
- .prereq_continuation => {
- self.state = .lhs;
- return Token{ .prereq = self.bytes[start .. self.index - 1] };
- },
- .prereq_continuation_linefeed => {
- self.state = .lhs;
- return Token{ .prereq = self.bytes[start .. self.index - 2] };
- },
- }
- }
- unreachable;
-}
-
-fn errorPosition(comptime id: @TagType(Token), index: usize, bytes: []const u8) Token {
- return @unionInit(Token, @tagName(id), .{ .index = index, .bytes = bytes });
-}
-
-fn errorIllegalChar(comptime id: @TagType(Token), index: usize, char: u8) Token {
- return @unionInit(Token, @tagName(id), .{ .index = index, .char = char });
-}
-
-fn finishTarget(must_resolve: bool, bytes: []const u8) Token {
- return if (must_resolve)
- .{ .target_must_resolve = bytes }
- else
- .{ .target = bytes };
-}
-
-const State = enum {
- lhs,
- target,
- target_reverse_solidus,
- target_dollar_sign,
- target_colon,
- target_colon_reverse_solidus,
- rhs,
- rhs_continuation,
- rhs_continuation_linefeed,
- prereq_quote,
- prereq,
- prereq_continuation,
- prereq_continuation_linefeed,
-};
-
-pub const Token = union(enum) {
- target: []const u8,
- target_must_resolve: []const u8,
- prereq: []const u8,
-
- incomplete_quoted_prerequisite: IndexAndBytes,
- incomplete_target: IndexAndBytes,
-
- invalid_target: IndexAndChar,
- bad_target_escape: IndexAndChar,
- expected_dollar_sign: IndexAndChar,
- continuation_eol: IndexAndChar,
- incomplete_escape: IndexAndChar,
-
- pub const IndexAndChar = struct {
- index: usize,
- char: u8,
- };
-
- pub const IndexAndBytes = struct {
- index: usize,
- bytes: []const u8,
- };
-
- /// Resolve escapes in target. Only valid with .target_must_resolve.
- pub fn resolve(self: Token, writer: anytype) @TypeOf(writer).Error!void {
- const bytes = self.target_must_resolve; // resolve called on incorrect token
-
- var state: enum { start, escape, dollar } = .start;
- for (bytes) |c| {
- switch (state) {
- .start => {
- switch (c) {
- '\\' => state = .escape,
- '$' => state = .dollar,
- else => try writer.writeByte(c),
- }
- },
- .escape => {
- switch (c) {
- ' ', '#', '\\' => {},
- '$' => {
- try writer.writeByte('\\');
- state = .dollar;
- continue;
- },
- else => try writer.writeByte('\\'),
- }
- try writer.writeByte(c);
- state = .start;
- },
- .dollar => {
- try writer.writeByte('$');
- switch (c) {
- '$' => {},
- else => try writer.writeByte(c),
- }
- state = .start;
- },
- }
- }
- }
-
- pub fn printError(self: Token, writer: anytype) @TypeOf(writer).Error!void {
- switch (self) {
- .target, .target_must_resolve, .prereq => unreachable, // not an error
- .incomplete_quoted_prerequisite,
- .incomplete_target,
- => |index_and_bytes| {
- try writer.print("{} '", .{self.errStr()});
- if (self == .incomplete_target) {
- const tmp = Token{ .target_must_resolve = index_and_bytes.bytes };
- try tmp.resolve(writer);
- } else {
- try printCharValues(writer, index_and_bytes.bytes);
- }
- try writer.print("' at position {}", .{index_and_bytes.index});
- },
- .invalid_target,
- .bad_target_escape,
- .expected_dollar_sign,
- .continuation_eol,
- .incomplete_escape,
- => |index_and_char| {
- try writer.writeAll("illegal char ");
- try printUnderstandableChar(writer, index_and_char.char);
- try writer.print(" at position {}: {}", .{ index_and_char.index, self.errStr() });
- },
- }
- }
-
- fn errStr(self: Token) []const u8 {
- return switch (self) {
- .target, .target_must_resolve, .prereq => unreachable, // not an error
- .incomplete_quoted_prerequisite => "incomplete quoted prerequisite",
- .incomplete_target => "incomplete target",
- .invalid_target => "invalid target",
- .bad_target_escape => "bad target escape",
- .expected_dollar_sign => "expecting '$'",
- .continuation_eol => "continuation expecting end-of-line",
- .incomplete_escape => "incomplete escape",
- };
- }
-};
-
-test "empty file" {
- try depTokenizer("", "");
-}
-
-test "empty whitespace" {
- try depTokenizer("\n", "");
- try depTokenizer("\r", "");
- try depTokenizer("\r\n", "");
- try depTokenizer(" ", "");
-}
-
-test "empty colon" {
- try depTokenizer(":", "");
- try depTokenizer("\n:", "");
- try depTokenizer("\r:", "");
- try depTokenizer("\r\n:", "");
- try depTokenizer(" :", "");
-}
-
-test "empty target" {
- try depTokenizer("foo.o:", "target = {foo.o}");
- try depTokenizer(
- \\foo.o:
- \\bar.o:
- \\abcd.o:
- ,
- \\target = {foo.o}
- \\target = {bar.o}
- \\target = {abcd.o}
- );
-}
-
-test "whitespace empty target" {
- try depTokenizer("\nfoo.o:", "target = {foo.o}");
- try depTokenizer("\rfoo.o:", "target = {foo.o}");
- try depTokenizer("\r\nfoo.o:", "target = {foo.o}");
- try depTokenizer(" foo.o:", "target = {foo.o}");
-}
-
-test "escape empty target" {
- try depTokenizer("\\ foo.o:", "target = { foo.o}");
- try depTokenizer("\\#foo.o:", "target = {#foo.o}");
- try depTokenizer("\\\\foo.o:", "target = {\\foo.o}");
- try depTokenizer("$$foo.o:", "target = {$foo.o}");
-}
-
-test "empty target linefeeds" {
- try depTokenizer("\n", "");
- try depTokenizer("\r\n", "");
-
- const expect = "target = {foo.o}";
- try depTokenizer(
- \\foo.o:
- , expect);
- try depTokenizer(
- \\foo.o:
- \\
- , expect);
- try depTokenizer(
- \\foo.o:
- , expect);
- try depTokenizer(
- \\foo.o:
- \\
- , expect);
-}
-
-test "empty target linefeeds + continuations" {
- const expect = "target = {foo.o}";
- try depTokenizer(
- \\foo.o:\
- , expect);
- try depTokenizer(
- \\foo.o:\
- \\
- , expect);
- try depTokenizer(
- \\foo.o:\
- , expect);
- try depTokenizer(
- \\foo.o:\
- \\
- , expect);
-}
-
-test "empty target linefeeds + hspace + continuations" {
- const expect = "target = {foo.o}";
- try depTokenizer(
- \\foo.o: \
- , expect);
- try depTokenizer(
- \\foo.o: \
- \\
- , expect);
- try depTokenizer(
- \\foo.o: \
- , expect);
- try depTokenizer(
- \\foo.o: \
- \\
- , expect);
-}
-
-test "prereq" {
- const expect =
- \\target = {foo.o}
- \\prereq = {foo.c}
- ;
- try depTokenizer("foo.o: foo.c", expect);
- try depTokenizer(
- \\foo.o: \
- \\foo.c
- , expect);
- try depTokenizer(
- \\foo.o: \
- \\ foo.c
- , expect);
- try depTokenizer(
- \\foo.o: \
- \\ foo.c
- , expect);
-}
-
-test "prereq continuation" {
- const expect =
- \\target = {foo.o}
- \\prereq = {foo.h}
- \\prereq = {bar.h}
- ;
- try depTokenizer(
- \\foo.o: foo.h\
- \\bar.h
- , expect);
- try depTokenizer(
- \\foo.o: foo.h\
- \\bar.h
- , expect);
-}
-
-test "multiple prereqs" {
- const expect =
- \\target = {foo.o}
- \\prereq = {foo.c}
- \\prereq = {foo.h}
- \\prereq = {bar.h}
- ;
- try depTokenizer("foo.o: foo.c foo.h bar.h", expect);
- try depTokenizer(
- \\foo.o: \
- \\foo.c foo.h bar.h
- , expect);
- try depTokenizer(
- \\foo.o: foo.c foo.h bar.h\
- , expect);
- try depTokenizer(
- \\foo.o: foo.c foo.h bar.h\
- \\
- , expect);
- try depTokenizer(
- \\foo.o: \
- \\foo.c \
- \\ foo.h\
- \\bar.h
- \\
- , expect);
- try depTokenizer(
- \\foo.o: \
- \\foo.c \
- \\ foo.h\
- \\bar.h\
- \\
- , expect);
- try depTokenizer(
- \\foo.o: \
- \\foo.c \
- \\ foo.h\
- \\bar.h\
- , expect);
-}
-
-test "multiple targets and prereqs" {
- try depTokenizer(
- \\foo.o: foo.c
- \\bar.o: bar.c a.h b.h c.h
- \\abc.o: abc.c \
- \\ one.h two.h \
- \\ three.h four.h
- ,
- \\target = {foo.o}
- \\prereq = {foo.c}
- \\target = {bar.o}
- \\prereq = {bar.c}
- \\prereq = {a.h}
- \\prereq = {b.h}
- \\prereq = {c.h}
- \\target = {abc.o}
- \\prereq = {abc.c}
- \\prereq = {one.h}
- \\prereq = {two.h}
- \\prereq = {three.h}
- \\prereq = {four.h}
- );
- try depTokenizer(
- \\ascii.o: ascii.c
- \\base64.o: base64.c stdio.h
- \\elf.o: elf.c a.h b.h c.h
- \\macho.o: \
- \\ macho.c\
- \\ a.h b.h c.h
- ,
- \\target = {ascii.o}
- \\prereq = {ascii.c}
- \\target = {base64.o}
- \\prereq = {base64.c}
- \\prereq = {stdio.h}
- \\target = {elf.o}
- \\prereq = {elf.c}
- \\prereq = {a.h}
- \\prereq = {b.h}
- \\prereq = {c.h}
- \\target = {macho.o}
- \\prereq = {macho.c}
- \\prereq = {a.h}
- \\prereq = {b.h}
- \\prereq = {c.h}
- );
- try depTokenizer(
- \\a$$scii.o: ascii.c
- \\\\base64.o: "\base64.c" "s t#dio.h"
- \\e\\lf.o: "e\lf.c" "a.h$$" "$$b.h c.h$$"
- \\macho.o: \
- \\ "macho!.c" \
- \\ a.h b.h c.h
- ,
- \\target = {a$scii.o}
- \\prereq = {ascii.c}
- \\target = {\base64.o}
- \\prereq = {\base64.c}
- \\prereq = {s t#dio.h}
- \\target = {e\lf.o}
- \\prereq = {e\lf.c}
- \\prereq = {a.h$$}
- \\prereq = {$$b.h c.h$$}
- \\target = {macho.o}
- \\prereq = {macho!.c}
- \\prereq = {a.h}
- \\prereq = {b.h}
- \\prereq = {c.h}
- );
-}
-
-test "windows quoted prereqs" {
- try depTokenizer(
- \\c:\foo.o: "C:\Program Files (x86)\Microsoft Visual Studio\foo.c"
- \\c:\foo2.o: "C:\Program Files (x86)\Microsoft Visual Studio\foo2.c" \
- \\ "C:\Program Files (x86)\Microsoft Visual Studio\foo1.h" \
- \\ "C:\Program Files (x86)\Microsoft Visual Studio\foo2.h"
- ,
- \\target = {c:\foo.o}
- \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo.c}
- \\target = {c:\foo2.o}
- \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo2.c}
- \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo1.h}
- \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo2.h}
- );
-}
-
-test "windows mixed prereqs" {
- try depTokenizer(
- \\cimport.o: \
- \\ C:\msys64\home\anon\project\zig\master\zig-cache\o\qhvhbUo7GU5iKyQ5mpA8TcQpncCYaQu0wwvr3ybiSTj_Dtqi1Nmcb70kfODJ2Qlg\cimport.h \
- \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\stdio.h" \
- \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt.h" \
- \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime.h" \
- \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\sal.h" \
- \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\concurrencysal.h" \
- \\ C:\msys64\opt\zig\lib\zig\include\vadefs.h \
- \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vadefs.h" \
- \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstdio.h" \
- \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_stdio_config.h" \
- \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\string.h" \
- \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memory.h" \
- \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memcpy_s.h" \
- \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\errno.h" \
- \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime_string.h" \
- \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstring.h"
- ,
- \\target = {cimport.o}
- \\prereq = {C:\msys64\home\anon\project\zig\master\zig-cache\o\qhvhbUo7GU5iKyQ5mpA8TcQpncCYaQu0wwvr3ybiSTj_Dtqi1Nmcb70kfODJ2Qlg\cimport.h}
- \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\stdio.h}
- \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt.h}
- \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime.h}
- \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\sal.h}
- \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\concurrencysal.h}
- \\prereq = {C:\msys64\opt\zig\lib\zig\include\vadefs.h}
- \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vadefs.h}
- \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstdio.h}
- \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_stdio_config.h}
- \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\string.h}
- \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memory.h}
- \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memcpy_s.h}
- \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\errno.h}
- \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime_string.h}
- \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstring.h}
- );
-}
-
-test "funky targets" {
- try depTokenizer(
- \\C:\Users\anon\foo.o:
- \\C:\Users\anon\foo\ .o:
- \\C:\Users\anon\foo\#.o:
- \\C:\Users\anon\foo$$.o:
- \\C:\Users\anon\\\ foo.o:
- \\C:\Users\anon\\#foo.o:
- \\C:\Users\anon\$$foo.o:
- \\C:\Users\anon\\\ \ \ \ \ foo.o:
- ,
- \\target = {C:\Users\anon\foo.o}
- \\target = {C:\Users\anon\foo .o}
- \\target = {C:\Users\anon\foo#.o}
- \\target = {C:\Users\anon\foo$.o}
- \\target = {C:\Users\anon\ foo.o}
- \\target = {C:\Users\anon\#foo.o}
- \\target = {C:\Users\anon\$foo.o}
- \\target = {C:\Users\anon\ foo.o}
- );
-}
-
-test "error incomplete escape - reverse_solidus" {
- try depTokenizer("\\",
- \\ERROR: illegal char '\' at position 0: incomplete escape
- );
- try depTokenizer("\t\\",
- \\ERROR: illegal char '\' at position 1: incomplete escape
- );
- try depTokenizer("\n\\",
- \\ERROR: illegal char '\' at position 1: incomplete escape
- );
- try depTokenizer("\r\\",
- \\ERROR: illegal char '\' at position 1: incomplete escape
- );
- try depTokenizer("\r\n\\",
- \\ERROR: illegal char '\' at position 2: incomplete escape
- );
- try depTokenizer(" \\",
- \\ERROR: illegal char '\' at position 1: incomplete escape
- );
-}
-
-test "error incomplete escape - dollar_sign" {
- try depTokenizer("$",
- \\ERROR: illegal char '$' at position 0: incomplete escape
- );
- try depTokenizer("\t$",
- \\ERROR: illegal char '$' at position 1: incomplete escape
- );
- try depTokenizer("\n$",
- \\ERROR: illegal char '$' at position 1: incomplete escape
- );
- try depTokenizer("\r$",
- \\ERROR: illegal char '$' at position 1: incomplete escape
- );
- try depTokenizer("\r\n$",
- \\ERROR: illegal char '$' at position 2: incomplete escape
- );
- try depTokenizer(" $",
- \\ERROR: illegal char '$' at position 1: incomplete escape
- );
-}
-
-test "error incomplete target" {
- try depTokenizer("foo.o",
- \\ERROR: incomplete target 'foo.o' at position 0
- );
- try depTokenizer("\tfoo.o",
- \\ERROR: incomplete target 'foo.o' at position 1
- );
- try depTokenizer("\nfoo.o",
- \\ERROR: incomplete target 'foo.o' at position 1
- );
- try depTokenizer("\rfoo.o",
- \\ERROR: incomplete target 'foo.o' at position 1
- );
- try depTokenizer("\r\nfoo.o",
- \\ERROR: incomplete target 'foo.o' at position 2
- );
- try depTokenizer(" foo.o",
- \\ERROR: incomplete target 'foo.o' at position 1
- );
-
- try depTokenizer("\\ foo.o",
- \\ERROR: incomplete target ' foo.o' at position 0
- );
- try depTokenizer("\\#foo.o",
- \\ERROR: incomplete target '#foo.o' at position 0
- );
- try depTokenizer("\\\\foo.o",
- \\ERROR: incomplete target '\foo.o' at position 0
- );
- try depTokenizer("$$foo.o",
- \\ERROR: incomplete target '$foo.o' at position 0
- );
-}
-
-test "error illegal char at position - bad target escape" {
- try depTokenizer("\\\t",
- \\ERROR: illegal char \x09 at position 1: bad target escape
- );
- try depTokenizer("\\\n",
- \\ERROR: illegal char \x0A at position 1: bad target escape
- );
- try depTokenizer("\\\r",
- \\ERROR: illegal char \x0D at position 1: bad target escape
- );
- try depTokenizer("\\\r\n",
- \\ERROR: illegal char \x0D at position 1: bad target escape
- );
-}
-
-test "error illegal char at position - execting dollar_sign" {
- try depTokenizer("$\t",
- \\ERROR: illegal char \x09 at position 1: expecting '$'
- );
- try depTokenizer("$\n",
- \\ERROR: illegal char \x0A at position 1: expecting '$'
- );
- try depTokenizer("$\r",
- \\ERROR: illegal char \x0D at position 1: expecting '$'
- );
- try depTokenizer("$\r\n",
- \\ERROR: illegal char \x0D at position 1: expecting '$'
- );
-}
-
-test "error illegal char at position - invalid target" {
- try depTokenizer("foo\t.o",
- \\ERROR: illegal char \x09 at position 3: invalid target
- );
- try depTokenizer("foo\n.o",
- \\ERROR: illegal char \x0A at position 3: invalid target
- );
- try depTokenizer("foo\r.o",
- \\ERROR: illegal char \x0D at position 3: invalid target
- );
- try depTokenizer("foo\r\n.o",
- \\ERROR: illegal char \x0D at position 3: invalid target
- );
-}
-
-test "error target - continuation expecting end-of-line" {
- try depTokenizer("foo.o: \\\t",
- \\target = {foo.o}
- \\ERROR: illegal char \x09 at position 8: continuation expecting end-of-line
- );
- try depTokenizer("foo.o: \\ ",
- \\target = {foo.o}
- \\ERROR: illegal char \x20 at position 8: continuation expecting end-of-line
- );
- try depTokenizer("foo.o: \\x",
- \\target = {foo.o}
- \\ERROR: illegal char 'x' at position 8: continuation expecting end-of-line
- );
- try depTokenizer("foo.o: \\\x0dx",
- \\target = {foo.o}
- \\ERROR: illegal char 'x' at position 9: continuation expecting end-of-line
- );
-}
-
-test "error prereq - continuation expecting end-of-line" {
- try depTokenizer("foo.o: foo.h\\\x0dx",
- \\target = {foo.o}
- \\ERROR: illegal char 'x' at position 14: continuation expecting end-of-line
- );
-}
-
-// - tokenize input, emit textual representation, and compare to expect
-fn depTokenizer(input: []const u8, expect: []const u8) !void {
- var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);
- const arena = &arena_allocator.allocator;
- defer arena_allocator.deinit();
-
- var it: Tokenizer = .{ .bytes = input };
- var buffer = try std.ArrayListSentineled(u8, 0).initSize(arena, 0);
- var resolve_buf = std.ArrayList(u8).init(arena);
- var i: usize = 0;
- while (it.next()) |token| {
- if (i != 0) try buffer.appendSlice("\n");
- switch (token) {
- .target, .prereq => |bytes| {
- try buffer.appendSlice(@tagName(token));
- try buffer.appendSlice(" = {");
- for (bytes) |b| {
- try buffer.append(printable_char_tab[b]);
- }
- try buffer.appendSlice("}");
- },
- .target_must_resolve => {
- try buffer.appendSlice("target = {");
- try token.resolve(resolve_buf.writer());
- for (resolve_buf.items) |b| {
- try buffer.append(printable_char_tab[b]);
- }
- resolve_buf.items.len = 0;
- try buffer.appendSlice("}");
- },
- else => {
- try buffer.appendSlice("ERROR: ");
- try token.printError(buffer.outStream());
- break;
- },
- }
- i += 1;
- }
- const got: []const u8 = buffer.span();
-
- if (std.mem.eql(u8, expect, got)) {
- testing.expect(true);
- return;
- }
-
- const out = std.io.getStdErr().writer();
-
- try out.writeAll("\n");
- try printSection(out, "<<<< input", input);
- try printSection(out, "==== expect", expect);
- try printSection(out, ">>>> got", got);
- try printRuler(out);
-
- testing.expect(false);
-}
-
-fn printSection(out: anytype, label: []const u8, bytes: []const u8) !void {
- try printLabel(out, label, bytes);
- try hexDump(out, bytes);
- try printRuler(out);
- try out.writeAll(bytes);
- try out.writeAll("\n");
-}
-
-fn printLabel(out: anytype, label: []const u8, bytes: []const u8) !void {
- var buf: [80]u8 = undefined;
- var text = try std.fmt.bufPrint(buf[0..], "{} {} bytes ", .{ label, bytes.len });
- try out.writeAll(text);
- var i: usize = text.len;
- const end = 79;
- while (i < 79) : (i += 1) {
- try out.writeAll(&[_]u8{label[0]});
- }
- try out.writeAll("\n");
-}
-
-fn printRuler(out: anytype) !void {
- var i: usize = 0;
- const end = 79;
- while (i < 79) : (i += 1) {
- try out.writeAll("-");
- }
- try out.writeAll("\n");
-}
-
-fn hexDump(out: anytype, bytes: []const u8) !void {
- const n16 = bytes.len >> 4;
- var line: usize = 0;
- var offset: usize = 0;
- while (line < n16) : (line += 1) {
- try hexDump16(out, offset, bytes[offset .. offset + 16]);
- offset += 16;
- }
-
- const n = bytes.len & 0x0f;
- if (n > 0) {
- try printDecValue(out, offset, 8);
- try out.writeAll(":");
- try out.writeAll(" ");
- var end1 = std.math.min(offset + n, offset + 8);
- for (bytes[offset..end1]) |b| {
- try out.writeAll(" ");
- try printHexValue(out, b, 2);
- }
- var end2 = offset + n;
- if (end2 > end1) {
- try out.writeAll(" ");
- for (bytes[end1..end2]) |b| {
- try out.writeAll(" ");
- try printHexValue(out, b, 2);
- }
- }
- const short = 16 - n;
- var i: usize = 0;
- while (i < short) : (i += 1) {
- try out.writeAll(" ");
- }
- if (end2 > end1) {
- try out.writeAll(" |");
- } else {
- try out.writeAll(" |");
- }
- try printCharValues(out, bytes[offset..end2]);
- try out.writeAll("|\n");
- offset += n;
- }
-
- try printDecValue(out, offset, 8);
- try out.writeAll(":");
- try out.writeAll("\n");
-}
-
-fn hexDump16(out: anytype, offset: usize, bytes: []const u8) !void {
- try printDecValue(out, offset, 8);
- try out.writeAll(":");
- try out.writeAll(" ");
- for (bytes[0..8]) |b| {
- try out.writeAll(" ");
- try printHexValue(out, b, 2);
- }
- try out.writeAll(" ");
- for (bytes[8..16]) |b| {
- try out.writeAll(" ");
- try printHexValue(out, b, 2);
- }
- try out.writeAll(" |");
- try printCharValues(out, bytes);
- try out.writeAll("|\n");
-}
-
-fn printDecValue(out: anytype, value: u64, width: u8) !void {
- var buffer: [20]u8 = undefined;
- const len = std.fmt.formatIntBuf(buffer[0..], value, 10, false, .{ .width = width, .fill = '0' });
- try out.writeAll(buffer[0..len]);
-}
-
-fn printHexValue(out: anytype, value: u64, width: u8) !void {
- var buffer: [16]u8 = undefined;
- const len = std.fmt.formatIntBuf(buffer[0..], value, 16, false, .{ .width = width, .fill = '0' });
- try out.writeAll(buffer[0..len]);
-}
-
-fn printCharValues(out: anytype, bytes: []const u8) !void {
- for (bytes) |b| {
- try out.writeAll(&[_]u8{printable_char_tab[b]});
- }
-}
-
-fn printUnderstandableChar(out: anytype, char: u8) !void {
- if (!std.ascii.isPrint(char) or char == ' ') {
- try out.print("\\x{X:0>2}", .{char});
- } else {
- try out.print("'{c}'", .{printable_char_tab[char]});
- }
-}
-
-// zig fmt: off
-const printable_char_tab: [256]u8 = (
- "................................ !\"#$%&'()*+,-./0123456789:;<=>?" ++
- "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~." ++
- "................................................................" ++
- "................................................................"
-).*;
-
diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig
deleted file mode 100644
index ebe7cdfb1e15e3209884153440d04dd655b3a014..0000000000000000000000000000000000000000
--- a/src-self-hosted/Module.zig
+++ /dev/null
@@ -1,3235 +0,0 @@
-const Module = @This();
-const std = @import("std");
-const Compilation = @import("Compilation.zig");
-const mem = std.mem;
-const Allocator = std.mem.Allocator;
-const ArrayListUnmanaged = std.ArrayListUnmanaged;
-const Value = @import("value.zig").Value;
-const Type = @import("type.zig").Type;
-const TypedValue = @import("TypedValue.zig");
-const assert = std.debug.assert;
-const log = std.log.scoped(.module);
-const BigIntConst = std.math.big.int.Const;
-const BigIntMutable = std.math.big.int.Mutable;
-const Target = std.Target;
-const Package = @import("Package.zig");
-const link = @import("link.zig");
-const ir = @import("ir.zig");
-const zir = @import("zir.zig");
-const Inst = ir.Inst;
-const Body = ir.Body;
-const ast = std.zig.ast;
-const trace = @import("tracy.zig").trace;
-const astgen = @import("astgen.zig");
-const zir_sema = @import("zir_sema.zig");
-
-/// General-purpose allocator. Used for both temporary and long-term storage.
-gpa: *Allocator,
-comp: *Compilation,
-
-/// Where our incremental compilation metadata serialization will go.
-zig_cache_artifact_directory: Compilation.Directory,
-/// Pointer to externally managed resource. `null` if there is no zig file being compiled.
-root_pkg: *Package,
-/// Module owns this resource.
-/// The `Scope` is either a `Scope.ZIRModule` or `Scope.File`.
-root_scope: *Scope,
-/// It's rare for a decl to be exported, so we save memory by having a sparse map of
-/// Decl pointers to details about them being exported.
-/// The Export memory is owned by the `export_owners` table; the slice itself is owned by this table.
-decl_exports: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{},
-/// We track which export is associated with the given symbol name for quick
-/// detection of symbol collisions.
-symbol_exports: std.StringArrayHashMapUnmanaged(*Export) = .{},
-/// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl
-/// is modified. Note that the key of this table is not the Decl being exported, but the Decl that
-/// is performing the export of another Decl.
-/// This table owns the Export memory.
-export_owners: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{},
-/// Maps fully qualified namespaced names to the Decl struct for them.
-decl_table: std.ArrayHashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql, false) = .{},
-/// We optimize memory usage for a compilation with no compile errors by storing the
-/// error messages and mapping outside of `Decl`.
-/// The ErrorMsg memory is owned by the decl, using Module's general purpose allocator.
-/// Note that a Decl can succeed but the Fn it represents can fail. In this case,
-/// a Decl can have a failed_decls entry but have analysis status of success.
-failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *Compilation.ErrorMsg) = .{},
-/// Using a map here for consistency with the other fields here.
-/// The ErrorMsg memory is owned by the `Scope`, using Module's general purpose allocator.
-failed_files: std.AutoArrayHashMapUnmanaged(*Scope, *Compilation.ErrorMsg) = .{},
-/// Using a map here for consistency with the other fields here.
-/// The ErrorMsg memory is owned by the `Export`, using Module's general purpose allocator.
-failed_exports: std.AutoArrayHashMapUnmanaged(*Export, *Compilation.ErrorMsg) = .{},
-
-next_anon_name_index: usize = 0,
-
-/// Candidates for deletion. After a semantic analysis update completes, this list
-/// contains Decls that need to be deleted if they end up having no references to them.
-deletion_set: ArrayListUnmanaged(*Decl) = .{},
-
-/// Error tags and their values, tag names are duped with mod.gpa.
-global_error_set: std.StringHashMapUnmanaged(u16) = .{},
-
-/// Incrementing integer used to compare against the corresponding Decl
-/// field to determine whether a Decl's status applies to an ongoing update, or a
-/// previous analysis.
-generation: u32 = 0,
-
-pub const Export = struct {
- options: std.builtin.ExportOptions,
- /// Byte offset into the file that contains the export directive.
- src: usize,
- /// Represents the position of the export, if any, in the output file.
- link: link.File.Elf.Export,
- /// The Decl that performs the export. Note that this is *not* the Decl being exported.
- owner_decl: *Decl,
- /// The Decl being exported. Note this is *not* the Decl performing the export.
- exported_decl: *Decl,
- status: enum {
- in_progress,
- failed,
- /// Indicates that the failure was due to a temporary issue, such as an I/O error
- /// when writing to the output file. Retrying the export may succeed.
- failed_retryable,
- complete,
- },
-};
-
-pub const Decl = struct {
- /// This name is relative to the containing namespace of the decl. It uses a null-termination
- /// to save bytes, since there can be a lot of decls in a compilation. The null byte is not allowed
- /// in symbol names, because executable file formats use null-terminated strings for symbol names.
- /// All Decls have names, even values that are not bound to a zig namespace. This is necessary for
- /// mapping them to an address in the output file.
- /// Memory owned by this decl, using Module's allocator.
- name: [*:0]const u8,
- /// The direct parent container of the Decl. This is either a `Scope.Container` or `Scope.ZIRModule`.
- /// Reference to externally owned memory.
- scope: *Scope,
- /// The AST Node decl index or ZIR Inst index that contains this declaration.
- /// Must be recomputed when the corresponding source file is modified.
- src_index: usize,
- /// The most recent value of the Decl after a successful semantic analysis.
- typed_value: union(enum) {
- never_succeeded: void,
- most_recent: TypedValue.Managed,
- },
- /// Represents the "shallow" analysis status. For example, for decls that are functions,
- /// the function type is analyzed with this set to `in_progress`, however, the semantic
- /// analysis of the function body is performed with this value set to `success`. Functions
- /// have their own analysis status field.
- analysis: enum {
- /// This Decl corresponds to an AST Node that has not been referenced yet, and therefore
- /// because of Zig's lazy declaration analysis, it will remain unanalyzed until referenced.
- unreferenced,
- /// Semantic analysis for this Decl is running right now. This state detects dependency loops.
- in_progress,
- /// This Decl might be OK but it depends on another one which did not successfully complete
- /// semantic analysis.
- dependency_failure,
- /// Semantic analysis failure.
- /// There will be a corresponding ErrorMsg in Module.failed_decls.
- sema_failure,
- /// There will be a corresponding ErrorMsg in Module.failed_decls.
- /// This indicates the failure was something like running out of disk space,
- /// and attempting semantic analysis again may succeed.
- sema_failure_retryable,
- /// There will be a corresponding ErrorMsg in Module.failed_decls.
- codegen_failure,
- /// There will be a corresponding ErrorMsg in Module.failed_decls.
- /// This indicates the failure was something like running out of disk space,
- /// and attempting codegen again may succeed.
- codegen_failure_retryable,
- /// Everything is done. During an update, this Decl may be out of date, depending
- /// on its dependencies. The `generation` field can be used to determine if this
- /// completion status occurred before or after a given update.
- complete,
- /// A Module update is in progress, and this Decl has been flagged as being known
- /// to require re-analysis.
- outdated,
- },
- /// This flag is set when this Decl is added to a check_for_deletion set, and cleared
- /// when removed.
- deletion_flag: bool,
- /// Whether the corresponding AST decl has a `pub` keyword.
- is_pub: bool,
-
- /// An integer that can be checked against the corresponding incrementing
- /// generation field of Module. This is used to determine whether `complete` status
- /// represents pre- or post- re-analysis.
- generation: u32,
-
- /// Represents the position of the code in the output file.
- /// This is populated regardless of semantic analysis and code generation.
- link: link.File.LinkBlock,
-
- /// Represents the function in the linked output file, if the `Decl` is a function.
- /// This is stored here and not in `Fn` because `Decl` survives across updates but
- /// `Fn` does not.
- /// TODO Look into making `Fn` a longer lived structure and moving this field there
- /// to save on memory usage.
- fn_link: link.File.LinkFn,
-
- contents_hash: std.zig.SrcHash,
-
- /// The shallow set of other decls whose typed_value could possibly change if this Decl's
- /// typed_value is modified.
- dependants: DepsTable = .{},
- /// The shallow set of other decls whose typed_value changing indicates that this Decl's
- /// typed_value may need to be regenerated.
- dependencies: DepsTable = .{},
-
- /// The reason this is not `std.AutoArrayHashMapUnmanaged` is a workaround for
- /// stage1 compiler giving me: `error: struct 'Module.Decl' depends on itself`
- pub const DepsTable = std.ArrayHashMapUnmanaged(*Decl, void, std.array_hash_map.getAutoHashFn(*Decl), std.array_hash_map.getAutoEqlFn(*Decl), false);
-
- pub fn destroy(self: *Decl, gpa: *Allocator) void {
- gpa.free(mem.spanZ(self.name));
- if (self.typedValueManaged()) |tvm| {
- tvm.deinit(gpa);
- }
- self.dependants.deinit(gpa);
- self.dependencies.deinit(gpa);
- gpa.destroy(self);
- }
-
- pub fn src(self: Decl) usize {
- switch (self.scope.tag) {
- .container => {
- const container = @fieldParentPtr(Scope.Container, "base", self.scope);
- const tree = container.file_scope.contents.tree;
- // TODO Container should have it's own decls()
- const decl_node = tree.root_node.decls()[self.src_index];
- return tree.token_locs[decl_node.firstToken()].start;
- },
- .zir_module => {
- const zir_module = @fieldParentPtr(Scope.ZIRModule, "base", self.scope);
- const module = zir_module.contents.module;
- const src_decl = module.decls[self.src_index];
- return src_decl.inst.src;
- },
- .file, .block => unreachable,
- .gen_zir => unreachable,
- .local_val => unreachable,
- .local_ptr => unreachable,
- .decl => unreachable,
- }
- }
-
- pub fn fullyQualifiedNameHash(self: Decl) Scope.NameHash {
- return self.scope.fullyQualifiedNameHash(mem.spanZ(self.name));
- }
-
- pub fn typedValue(self: *Decl) error{AnalysisFail}!TypedValue {
- const tvm = self.typedValueManaged() orelse return error.AnalysisFail;
- return tvm.typed_value;
- }
-
- pub fn value(self: *Decl) error{AnalysisFail}!Value {
- return (try self.typedValue()).val;
- }
-
- pub fn dump(self: *Decl) void {
- const loc = std.zig.findLineColumn(self.scope.source.bytes, self.src);
- std.debug.print("{}:{}:{} name={} status={}", .{
- self.scope.sub_file_path,
- loc.line + 1,
- loc.column + 1,
- mem.spanZ(self.name),
- @tagName(self.analysis),
- });
- if (self.typedValueManaged()) |tvm| {
- std.debug.print(" ty={} val={}", .{ tvm.typed_value.ty, tvm.typed_value.val });
- }
- std.debug.print("\n", .{});
- }
-
- pub fn typedValueManaged(self: *Decl) ?*TypedValue.Managed {
- switch (self.typed_value) {
- .most_recent => |*x| return x,
- .never_succeeded => return null,
- }
- }
-
- fn removeDependant(self: *Decl, other: *Decl) void {
- self.dependants.removeAssertDiscard(other);
- }
-
- fn removeDependency(self: *Decl, other: *Decl) void {
- self.dependencies.removeAssertDiscard(other);
- }
-};
-
-/// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
-pub const Fn = struct {
- /// This memory owned by the Decl's TypedValue.Managed arena allocator.
- analysis: union(enum) {
- queued: *ZIR,
- in_progress,
- /// There will be a corresponding ErrorMsg in Module.failed_decls
- sema_failure,
- /// This Fn might be OK but it depends on another Decl which did not successfully complete
- /// semantic analysis.
- dependency_failure,
- success: Body,
- },
- owner_decl: *Decl,
-
- /// This memory is temporary and points to stack memory for the duration
- /// of Fn analysis.
- pub const Analysis = struct {
- inner_block: Scope.Block,
- };
-
- /// Contains un-analyzed ZIR instructions generated from Zig source AST.
- pub const ZIR = struct {
- body: zir.Module.Body,
- arena: std.heap.ArenaAllocator.State,
- };
-
- /// For debugging purposes.
- pub fn dump(self: *Fn, mod: Module) void {
- std.debug.print("Module.Function(name={}) ", .{self.owner_decl.name});
- switch (self.analysis) {
- .queued => {
- std.debug.print("queued\n", .{});
- },
- .in_progress => {
- std.debug.print("in_progress\n", .{});
- },
- else => {
- std.debug.print("\n", .{});
- zir.dumpFn(mod, self);
- },
- }
- }
-};
-
-pub const Var = struct {
- /// if is_extern == true this is undefined
- init: Value,
- owner_decl: *Decl,
-
- is_extern: bool,
- is_mutable: bool,
- is_threadlocal: bool,
-};
-
-pub const Scope = struct {
- tag: Tag,
-
- pub const NameHash = [16]u8;
-
- pub fn cast(base: *Scope, comptime T: type) ?*T {
- if (base.tag != T.base_tag)
- return null;
-
- return @fieldParentPtr(T, "base", base);
- }
-
- /// Asserts the scope has a parent which is a DeclAnalysis and
- /// returns the arena Allocator.
- pub fn arena(self: *Scope) *Allocator {
- switch (self.tag) {
- .block => return self.cast(Block).?.arena,
- .decl => return &self.cast(DeclAnalysis).?.arena.allocator,
- .gen_zir => return self.cast(GenZIR).?.arena,
- .local_val => return self.cast(LocalVal).?.gen_zir.arena,
- .local_ptr => return self.cast(LocalPtr).?.gen_zir.arena,
- .zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator,
- .file => unreachable,
- .container => unreachable,
- }
- }
-
- /// If the scope has a parent which is a `DeclAnalysis`,
- /// returns the `Decl`, otherwise returns `null`.
- pub fn decl(self: *Scope) ?*Decl {
- return switch (self.tag) {
- .block => self.cast(Block).?.decl,
- .gen_zir => self.cast(GenZIR).?.decl,
- .local_val => self.cast(LocalVal).?.gen_zir.decl,
- .local_ptr => self.cast(LocalPtr).?.gen_zir.decl,
- .decl => self.cast(DeclAnalysis).?.decl,
- .zir_module => null,
- .file => null,
- .container => null,
- };
- }
-
- /// Asserts the scope has a parent which is a ZIRModule or Container and
- /// returns it.
- pub fn namespace(self: *Scope) *Scope {
- switch (self.tag) {
- .block => return self.cast(Block).?.decl.scope,
- .gen_zir => return self.cast(GenZIR).?.decl.scope,
- .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope,
- .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope,
- .decl => return self.cast(DeclAnalysis).?.decl.scope,
- .file => return &self.cast(File).?.root_container.base,
- .zir_module, .container => return self,
- }
- }
-
- /// Must generate unique bytes with no collisions with other decls.
- /// The point of hashing here is only to limit the number of bytes of
- /// the unique identifier to a fixed size (16 bytes).
- pub fn fullyQualifiedNameHash(self: *Scope, name: []const u8) NameHash {
- switch (self.tag) {
- .block => unreachable,
- .gen_zir => unreachable,
- .local_val => unreachable,
- .local_ptr => unreachable,
- .decl => unreachable,
- .file => unreachable,
- .zir_module => return self.cast(ZIRModule).?.fullyQualifiedNameHash(name),
- .container => return self.cast(Container).?.fullyQualifiedNameHash(name),
- }
- }
-
- /// Asserts the scope is a child of a File and has an AST tree and returns the tree.
- pub fn tree(self: *Scope) *ast.Tree {
- switch (self.tag) {
- .file => return self.cast(File).?.contents.tree,
- .zir_module => unreachable,
- .decl => return self.cast(DeclAnalysis).?.decl.scope.cast(Container).?.file_scope.contents.tree,
- .block => return self.cast(Block).?.decl.scope.cast(Container).?.file_scope.contents.tree,
- .gen_zir => return self.cast(GenZIR).?.decl.scope.cast(Container).?.file_scope.contents.tree,
- .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope.cast(Container).?.file_scope.contents.tree,
- .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope.cast(Container).?.file_scope.contents.tree,
- .container => return self.cast(Container).?.file_scope.contents.tree,
- }
- }
-
- /// Asserts the scope is a child of a `GenZIR` and returns it.
- pub fn getGenZIR(self: *Scope) *GenZIR {
- return switch (self.tag) {
- .block => unreachable,
- .gen_zir => self.cast(GenZIR).?,
- .local_val => return self.cast(LocalVal).?.gen_zir,
- .local_ptr => return self.cast(LocalPtr).?.gen_zir,
- .decl => unreachable,
- .zir_module => unreachable,
- .file => unreachable,
- .container => unreachable,
- };
- }
-
- /// Asserts the scope has a parent which is a ZIRModule, Contaienr or File and
- /// returns the sub_file_path field.
- pub fn subFilePath(base: *Scope) []const u8 {
- switch (base.tag) {
- .container => return @fieldParentPtr(Container, "base", base).file_scope.sub_file_path,
- .file => return @fieldParentPtr(File, "base", base).sub_file_path,
- .zir_module => return @fieldParentPtr(ZIRModule, "base", base).sub_file_path,
- .block => unreachable,
- .gen_zir => unreachable,
- .local_val => unreachable,
- .local_ptr => unreachable,
- .decl => unreachable,
- }
- }
-
- pub fn unload(base: *Scope, gpa: *Allocator) void {
- switch (base.tag) {
- .file => return @fieldParentPtr(File, "base", base).unload(gpa),
- .zir_module => return @fieldParentPtr(ZIRModule, "base", base).unload(gpa),
- .block => unreachable,
- .gen_zir => unreachable,
- .local_val => unreachable,
- .local_ptr => unreachable,
- .decl => unreachable,
- .container => unreachable,
- }
- }
-
- pub fn getSource(base: *Scope, module: *Module) ![:0]const u8 {
- switch (base.tag) {
- .container => return @fieldParentPtr(Container, "base", base).file_scope.getSource(module),
- .file => return @fieldParentPtr(File, "base", base).getSource(module),
- .zir_module => return @fieldParentPtr(ZIRModule, "base", base).getSource(module),
- .gen_zir => unreachable,
- .local_val => unreachable,
- .local_ptr => unreachable,
- .block => unreachable,
- .decl => unreachable,
- }
- }
-
- /// Asserts the scope is a namespace Scope and removes the Decl from the namespace.
- pub fn removeDecl(base: *Scope, child: *Decl) void {
- switch (base.tag) {
- .container => return @fieldParentPtr(Container, "base", base).removeDecl(child),
- .zir_module => return @fieldParentPtr(ZIRModule, "base", base).removeDecl(child),
- .file => unreachable,
- .block => unreachable,
- .gen_zir => unreachable,
- .local_val => unreachable,
- .local_ptr => unreachable,
- .decl => unreachable,
- }
- }
-
- /// Asserts the scope is a File or ZIRModule and deinitializes it, then deallocates it.
- pub fn destroy(base: *Scope, gpa: *Allocator) void {
- switch (base.tag) {
- .file => {
- const scope_file = @fieldParentPtr(File, "base", base);
- scope_file.deinit(gpa);
- gpa.destroy(scope_file);
- },
- .zir_module => {
- const scope_zir_module = @fieldParentPtr(ZIRModule, "base", base);
- scope_zir_module.deinit(gpa);
- gpa.destroy(scope_zir_module);
- },
- .block => unreachable,
- .gen_zir => unreachable,
- .local_val => unreachable,
- .local_ptr => unreachable,
- .decl => unreachable,
- .container => unreachable,
- }
- }
-
- fn name_hash_hash(x: NameHash) u32 {
- return @truncate(u32, @bitCast(u128, x));
- }
-
- fn name_hash_eql(a: NameHash, b: NameHash) bool {
- return @bitCast(u128, a) == @bitCast(u128, b);
- }
-
- pub const Tag = enum {
- /// .zir source code.
- zir_module,
- /// .zig source code.
- file,
- /// struct, enum or union, every .file contains one of these.
- container,
- block,
- decl,
- gen_zir,
- local_val,
- local_ptr,
- };
-
- pub const Container = struct {
- pub const base_tag: Tag = .container;
- base: Scope = Scope{ .tag = base_tag },
-
- file_scope: *Scope.File,
-
- /// Direct children of the file.
- decls: std.AutoArrayHashMapUnmanaged(*Decl, void),
-
- // TODO implement container types and put this in a status union
- // ty: Type
-
- pub fn deinit(self: *Container, gpa: *Allocator) void {
- self.decls.deinit(gpa);
- self.* = undefined;
- }
-
- pub fn removeDecl(self: *Container, child: *Decl) void {
- _ = self.decls.remove(child);
- }
-
- pub fn fullyQualifiedNameHash(self: *Container, name: []const u8) NameHash {
- // TODO container scope qualified names.
- return std.zig.hashSrc(name);
- }
- };
-
- pub const File = struct {
- pub const base_tag: Tag = .file;
- base: Scope = Scope{ .tag = base_tag },
-
- /// Relative to the owning package's root_src_dir.
- /// Reference to external memory, not owned by File.
- sub_file_path: []const u8,
- source: union(enum) {
- unloaded: void,
- bytes: [:0]const u8,
- },
- contents: union {
- not_available: void,
- tree: *ast.Tree,
- },
- status: enum {
- never_loaded,
- unloaded_success,
- unloaded_parse_failure,
- loaded_success,
- },
-
- root_container: Container,
-
- pub fn unload(self: *File, gpa: *Allocator) void {
- switch (self.status) {
- .never_loaded,
- .unloaded_parse_failure,
- .unloaded_success,
- => {},
-
- .loaded_success => {
- self.contents.tree.deinit();
- self.status = .unloaded_success;
- },
- }
- switch (self.source) {
- .bytes => |bytes| {
- gpa.free(bytes);
- self.source = .{ .unloaded = {} };
- },
- .unloaded => {},
- }
- }
-
- pub fn deinit(self: *File, gpa: *Allocator) void {
- self.root_container.deinit(gpa);
- self.unload(gpa);
- self.* = undefined;
- }
-
- pub fn dumpSrc(self: *File, src: usize) void {
- const loc = std.zig.findLineColumn(self.source.bytes, src);
- std.debug.print("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });
- }
-
- pub fn getSource(self: *File, module: *Module) ![:0]const u8 {
- switch (self.source) {
- .unloaded => {
- const source = try module.root_pkg.root_src_directory.handle.readFileAllocOptions(
- module.gpa,
- self.sub_file_path,
- std.math.maxInt(u32),
- null,
- 1,
- 0,
- );
- self.source = .{ .bytes = source };
- return source;
- },
- .bytes => |bytes| return bytes,
- }
- }
- };
-
- pub const ZIRModule = struct {
- pub const base_tag: Tag = .zir_module;
- base: Scope = Scope{ .tag = base_tag },
- /// Relative to the owning package's root_src_dir.
- /// Reference to external memory, not owned by ZIRModule.
- sub_file_path: []const u8,
- source: union(enum) {
- unloaded: void,
- bytes: [:0]const u8,
- },
- contents: union {
- not_available: void,
- module: *zir.Module,
- },
- status: enum {
- never_loaded,
- unloaded_success,
- unloaded_parse_failure,
- unloaded_sema_failure,
-
- loaded_sema_failure,
- loaded_success,
- },
-
- /// Even though .zir files only have 1 module, this set is still needed
- /// because of anonymous Decls, which can exist in the global set, but
- /// not this one.
- decls: ArrayListUnmanaged(*Decl),
-
- pub fn unload(self: *ZIRModule, gpa: *Allocator) void {
- switch (self.status) {
- .never_loaded,
- .unloaded_parse_failure,
- .unloaded_sema_failure,
- .unloaded_success,
- => {},
-
- .loaded_success => {
- self.contents.module.deinit(gpa);
- gpa.destroy(self.contents.module);
- self.contents = .{ .not_available = {} };
- self.status = .unloaded_success;
- },
- .loaded_sema_failure => {
- self.contents.module.deinit(gpa);
- gpa.destroy(self.contents.module);
- self.contents = .{ .not_available = {} };
- self.status = .unloaded_sema_failure;
- },
- }
- switch (self.source) {
- .bytes => |bytes| {
- gpa.free(bytes);
- self.source = .{ .unloaded = {} };
- },
- .unloaded => {},
- }
- }
-
- pub fn deinit(self: *ZIRModule, gpa: *Allocator) void {
- self.decls.deinit(gpa);
- self.unload(gpa);
- self.* = undefined;
- }
-
- pub fn removeDecl(self: *ZIRModule, child: *Decl) void {
- for (self.decls.items) |item, i| {
- if (item == child) {
- _ = self.decls.swapRemove(i);
- return;
- }
- }
- }
-
- pub fn dumpSrc(self: *ZIRModule, src: usize) void {
- const loc = std.zig.findLineColumn(self.source.bytes, src);
- std.debug.print("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });
- }
-
- pub fn getSource(self: *ZIRModule, module: *Module) ![:0]const u8 {
- switch (self.source) {
- .unloaded => {
- const source = try module.root_pkg.root_src_directory.handle.readFileAllocOptions(
- module.gpa,
- self.sub_file_path,
- std.math.maxInt(u32),
- null,
- 1,
- 0,
- );
- self.source = .{ .bytes = source };
- return source;
- },
- .bytes => |bytes| return bytes,
- }
- }
-
- pub fn fullyQualifiedNameHash(self: *ZIRModule, name: []const u8) NameHash {
- // ZIR modules only have 1 file with all decls global in the same namespace.
- return std.zig.hashSrc(name);
- }
- };
-
- /// This is a temporary structure, references to it are valid only
- /// during semantic analysis of the block.
- pub const Block = struct {
- pub const base_tag: Tag = .block;
- base: Scope = Scope{ .tag = base_tag },
- parent: ?*Block,
- func: ?*Fn,
- decl: *Decl,
- instructions: ArrayListUnmanaged(*Inst),
- /// Points to the arena allocator of DeclAnalysis
- arena: *Allocator,
- label: ?Label = null,
- is_comptime: bool,
-
- pub const Label = struct {
- zir_block: *zir.Inst.Block,
- results: ArrayListUnmanaged(*Inst),
- block_inst: *Inst.Block,
- };
- };
-
- /// This is a temporary structure, references to it are valid only
- /// during semantic analysis of the decl.
- pub const DeclAnalysis = struct {
- pub const base_tag: Tag = .decl;
- base: Scope = Scope{ .tag = base_tag },
- decl: *Decl,
- arena: std.heap.ArenaAllocator,
- };
-
- /// This is a temporary structure, references to it are valid only
- /// during semantic analysis of the decl.
- pub const GenZIR = struct {
- pub const base_tag: Tag = .gen_zir;
- base: Scope = Scope{ .tag = base_tag },
- /// Parents can be: `GenZIR`, `ZIRModule`, `File`
- parent: *Scope,
- decl: *Decl,
- arena: *Allocator,
- /// The first N instructions in a function body ZIR are arg instructions.
- instructions: std.ArrayListUnmanaged(*zir.Inst) = .{},
- label: ?Label = null,
-
- pub const Label = struct {
- token: ast.TokenIndex,
- block_inst: *zir.Inst.Block,
- result_loc: astgen.ResultLoc,
- };
- };
-
- /// This is always a `const` local and importantly the `inst` is a value type, not a pointer.
- /// This structure lives as long as the AST generation of the Block
- /// node that contains the variable.
- pub const LocalVal = struct {
- pub const base_tag: Tag = .local_val;
- base: Scope = Scope{ .tag = base_tag },
- /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`.
- parent: *Scope,
- gen_zir: *GenZIR,
- name: []const u8,
- inst: *zir.Inst,
- };
-
- /// This could be a `const` or `var` local. It has a pointer instead of a value.
- /// This structure lives as long as the AST generation of the Block
- /// node that contains the variable.
- pub const LocalPtr = struct {
- pub const base_tag: Tag = .local_ptr;
- base: Scope = Scope{ .tag = base_tag },
- /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`.
- parent: *Scope,
- gen_zir: *GenZIR,
- name: []const u8,
- ptr: *zir.Inst,
- };
-};
-
-pub const InnerError = error{ OutOfMemory, AnalysisFail };
-
-pub fn deinit(self: *Module) void {
- const gpa = self.gpa;
-
- self.zig_cache_artifact_directory.handle.close();
-
- self.deletion_set.deinit(gpa);
-
- for (self.decl_table.items()) |entry| {
- entry.value.destroy(gpa);
- }
- self.decl_table.deinit(gpa);
-
- for (self.failed_decls.items()) |entry| {
- entry.value.destroy(gpa);
- }
- self.failed_decls.deinit(gpa);
-
- for (self.failed_files.items()) |entry| {
- entry.value.destroy(gpa);
- }
- self.failed_files.deinit(gpa);
-
- for (self.failed_exports.items()) |entry| {
- entry.value.destroy(gpa);
- }
- self.failed_exports.deinit(gpa);
-
- for (self.decl_exports.items()) |entry| {
- const export_list = entry.value;
- gpa.free(export_list);
- }
- self.decl_exports.deinit(gpa);
-
- for (self.export_owners.items()) |entry| {
- freeExportList(gpa, entry.value);
- }
- self.export_owners.deinit(gpa);
-
- self.symbol_exports.deinit(gpa);
- self.root_scope.destroy(gpa);
-
- var it = self.global_error_set.iterator();
- while (it.next()) |entry| {
- gpa.free(entry.key);
- }
- self.global_error_set.deinit(gpa);
-}
-
-fn freeExportList(gpa: *Allocator, export_list: []*Export) void {
- for (export_list) |exp| {
- gpa.free(exp.options.name);
- gpa.destroy(exp);
- }
- gpa.free(export_list);
-}
-
-pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
- const tracy = trace(@src());
- defer tracy.end();
-
- const subsequent_analysis = switch (decl.analysis) {
- .in_progress => unreachable,
-
- .sema_failure,
- .sema_failure_retryable,
- .codegen_failure,
- .dependency_failure,
- .codegen_failure_retryable,
- => return error.AnalysisFail,
-
- .complete => return,
-
- .outdated => blk: {
- log.debug("re-analyzing {}\n", .{decl.name});
-
- // The exports this Decl performs will be re-discovered, so we remove them here
- // prior to re-analysis.
- self.deleteDeclExports(decl);
- // Dependencies will be re-discovered, so we remove them here prior to re-analysis.
- for (decl.dependencies.items()) |entry| {
- const dep = entry.key;
- dep.removeDependant(decl);
- if (dep.dependants.items().len == 0 and !dep.deletion_flag) {
- // We don't perform a deletion here, because this Decl or another one
- // may end up referencing it before the update is complete.
- dep.deletion_flag = true;
- try self.deletion_set.append(self.gpa, dep);
- }
- }
- decl.dependencies.clearRetainingCapacity();
-
- break :blk true;
- },
-
- .unreferenced => false,
- };
-
- const type_changed = if (self.root_scope.cast(Scope.ZIRModule)) |zir_module|
- try zir_sema.analyzeZirDecl(self, decl, zir_module.contents.module.decls[decl.src_index])
- else
- self.astGenAndAnalyzeDecl(decl) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- error.AnalysisFail => return error.AnalysisFail,
- else => {
- try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
- self.failed_decls.putAssumeCapacityNoClobber(decl, try Compilation.ErrorMsg.create(
- self.gpa,
- decl.src(),
- "unable to analyze: {}",
- .{@errorName(err)},
- ));
- decl.analysis = .sema_failure_retryable;
- return error.AnalysisFail;
- },
- };
-
- if (subsequent_analysis) {
- // We may need to chase the dependants and re-analyze them.
- // However, if the decl is a function, and the type is the same, we do not need to.
- if (type_changed or decl.typed_value.most_recent.typed_value.val.tag() != .function) {
- for (decl.dependants.items()) |entry| {
- const dep = entry.key;
- switch (dep.analysis) {
- .unreferenced => unreachable,
- .in_progress => unreachable,
- .outdated => continue, // already queued for update
-
- .dependency_failure,
- .sema_failure,
- .sema_failure_retryable,
- .codegen_failure,
- .codegen_failure_retryable,
- .complete,
- => if (dep.generation != self.generation) {
- try self.markOutdatedDecl(dep);
- },
- }
- }
- }
- }
-}
-
-fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
- const tracy = trace(@src());
- defer tracy.end();
-
- const container_scope = decl.scope.cast(Scope.Container).?;
- const tree = try self.getAstTree(container_scope);
- const ast_node = tree.root_node.decls()[decl.src_index];
- switch (ast_node.tag) {
- .FnProto => {
- const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", ast_node);
-
- decl.analysis = .in_progress;
-
- // This arena allocator's memory is discarded at the end of this function. It is used
- // to determine the type of the function, and hence the type of the decl, which is needed
- // to complete the Decl analysis.
- var fn_type_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
- defer fn_type_scope_arena.deinit();
- var fn_type_scope: Scope.GenZIR = .{
- .decl = decl,
- .arena = &fn_type_scope_arena.allocator,
- .parent = decl.scope,
- };
- defer fn_type_scope.instructions.deinit(self.gpa);
-
- decl.is_pub = fn_proto.getVisibToken() != null;
- const body_node = fn_proto.getBodyNode() orelse
- return self.failTok(&fn_type_scope.base, fn_proto.fn_token, "TODO implement extern functions", .{});
-
- const param_decls = fn_proto.params();
- const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_decls.len);
-
- const fn_src = tree.token_locs[fn_proto.fn_token].start;
- const type_type = try astgen.addZIRInstConst(self, &fn_type_scope.base, fn_src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.type_type),
- });
- const type_type_rl: astgen.ResultLoc = .{ .ty = type_type };
- for (param_decls) |param_decl, i| {
- const param_type_node = switch (param_decl.param_type) {
- .any_type => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement anytype parameter", .{}),
- .type_expr => |node| node,
- };
- param_types[i] = try astgen.expr(self, &fn_type_scope.base, type_type_rl, param_type_node);
- }
- if (fn_proto.getVarArgsToken()) |var_args_token| {
- return self.failTok(&fn_type_scope.base, var_args_token, "TODO implement var args", .{});
- }
- if (fn_proto.getLibName()) |lib_name| {
- return self.failNode(&fn_type_scope.base, lib_name, "TODO implement function library name", .{});
- }
- if (fn_proto.getAlignExpr()) |align_expr| {
- return self.failNode(&fn_type_scope.base, align_expr, "TODO implement function align expression", .{});
- }
- if (fn_proto.getSectionExpr()) |sect_expr| {
- return self.failNode(&fn_type_scope.base, sect_expr, "TODO implement function section expression", .{});
- }
- if (fn_proto.getCallconvExpr()) |callconv_expr| {
- return self.failNode(
- &fn_type_scope.base,
- callconv_expr,
- "TODO implement function calling convention expression",
- .{},
- );
- }
- const return_type_expr = switch (fn_proto.return_type) {
- .Explicit => |node| node,
- .InferErrorSet => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement inferred error sets", .{}),
- .Invalid => |tok| return self.failTok(&fn_type_scope.base, tok, "unable to parse return type", .{}),
- };
-
- const return_type_inst = try astgen.expr(self, &fn_type_scope.base, type_type_rl, return_type_expr);
- const fn_type_inst = try astgen.addZIRInst(self, &fn_type_scope.base, fn_src, zir.Inst.FnType, .{
- .return_type = return_type_inst,
- .param_types = param_types,
- }, .{});
-
- // We need the memory for the Type to go into the arena for the Decl
- var decl_arena = std.heap.ArenaAllocator.init(self.gpa);
- errdefer decl_arena.deinit();
- const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
-
- var block_scope: Scope.Block = .{
- .parent = null,
- .func = null,
- .decl = decl,
- .instructions = .{},
- .arena = &decl_arena.allocator,
- .is_comptime = false,
- };
- defer block_scope.instructions.deinit(self.gpa);
-
- const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, fn_type_inst, .{
- .instructions = fn_type_scope.instructions.items,
- });
- const new_func = try decl_arena.allocator.create(Fn);
- const fn_payload = try decl_arena.allocator.create(Value.Payload.Function);
-
- const fn_zir = blk: {
- // This scope's arena memory is discarded after the ZIR generation
- // pass completes, and semantic analysis of it completes.
- var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
- errdefer gen_scope_arena.deinit();
- var gen_scope: Scope.GenZIR = .{
- .decl = decl,
- .arena = &gen_scope_arena.allocator,
- .parent = decl.scope,
- };
- defer gen_scope.instructions.deinit(self.gpa);
-
- // We need an instruction for each parameter, and they must be first in the body.
- try gen_scope.instructions.resize(self.gpa, fn_proto.params_len);
- var params_scope = &gen_scope.base;
- for (fn_proto.params()) |param, i| {
- const name_token = param.name_token.?;
- const src = tree.token_locs[name_token].start;
- const param_name = tree.tokenSlice(name_token); // TODO: call identifierTokenString
- const arg = try gen_scope_arena.allocator.create(zir.Inst.Arg);
- arg.* = .{
- .base = .{
- .tag = .arg,
- .src = src,
- },
- .positionals = .{
- .name = param_name,
- },
- .kw_args = .{},
- };
- gen_scope.instructions.items[i] = &arg.base;
- const sub_scope = try gen_scope_arena.allocator.create(Scope.LocalVal);
- sub_scope.* = .{
- .parent = params_scope,
- .gen_zir = &gen_scope,
- .name = param_name,
- .inst = &arg.base,
- };
- params_scope = &sub_scope.base;
- }
-
- const body_block = body_node.cast(ast.Node.Block).?;
-
- try astgen.blockExpr(self, params_scope, body_block);
-
- if (gen_scope.instructions.items.len == 0 or
- !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn())
- {
- const src = tree.token_locs[body_block.rbrace].start;
- _ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .returnvoid);
- }
-
- const fn_zir = try gen_scope_arena.allocator.create(Fn.ZIR);
- fn_zir.* = .{
- .body = .{
- .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items),
- },
- .arena = gen_scope_arena.state,
- };
- break :blk fn_zir;
- };
-
- new_func.* = .{
- .analysis = .{ .queued = fn_zir },
- .owner_decl = decl,
- };
- fn_payload.* = .{ .func = new_func };
-
- var prev_type_has_bits = false;
- var type_changed = true;
-
- if (decl.typedValueManaged()) |tvm| {
- prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits();
- type_changed = !tvm.typed_value.ty.eql(fn_type);
-
- tvm.deinit(self.gpa);
- }
-
- decl_arena_state.* = decl_arena.state;
- decl.typed_value = .{
- .most_recent = .{
- .typed_value = .{
- .ty = fn_type,
- .val = Value.initPayload(&fn_payload.base),
- },
- .arena = decl_arena_state,
- },
- };
- decl.analysis = .complete;
- decl.generation = self.generation;
-
- if (fn_type.hasCodeGenBits()) {
- // We don't fully codegen the decl until later, but we do need to reserve a global
- // offset table index for it. This allows us to codegen decls out of dependency order,
- // increasing how many computations can be done in parallel.
- try self.comp.bin_file.allocateDeclIndexes(decl);
- try self.comp.work_queue.writeItem(.{ .codegen_decl = decl });
- } else if (prev_type_has_bits) {
- self.comp.bin_file.freeDecl(decl);
- }
-
- if (fn_proto.getExternExportInlineToken()) |maybe_export_token| {
- if (tree.token_ids[maybe_export_token] == .Keyword_export) {
- const export_src = tree.token_locs[maybe_export_token].start;
- const name_loc = tree.token_locs[fn_proto.getNameToken().?];
- const name = tree.tokenSliceLoc(name_loc);
- // The scope needs to have the decl in it.
- try self.analyzeExport(&block_scope.base, export_src, name, decl);
- }
- }
- return type_changed;
- },
- .VarDecl => {
- const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", ast_node);
-
- decl.analysis = .in_progress;
-
- // We need the memory for the Type to go into the arena for the Decl
- var decl_arena = std.heap.ArenaAllocator.init(self.gpa);
- errdefer decl_arena.deinit();
- const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
-
- var block_scope: Scope.Block = .{
- .parent = null,
- .func = null,
- .decl = decl,
- .instructions = .{},
- .arena = &decl_arena.allocator,
- .is_comptime = true,
- };
- defer block_scope.instructions.deinit(self.gpa);
-
- decl.is_pub = var_decl.getVisibToken() != null;
- const is_extern = blk: {
- const maybe_extern_token = var_decl.getExternExportToken() orelse
- break :blk false;
- if (tree.token_ids[maybe_extern_token] != .Keyword_extern) break :blk false;
- if (var_decl.getInitNode()) |some| {
- return self.failNode(&block_scope.base, some, "extern variables have no initializers", .{});
- }
- break :blk true;
- };
- if (var_decl.getLibName()) |lib_name| {
- assert(is_extern);
- return self.failNode(&block_scope.base, lib_name, "TODO implement function library name", .{});
- }
- const is_mutable = tree.token_ids[var_decl.mut_token] == .Keyword_var;
- const is_threadlocal = if (var_decl.getThreadLocalToken()) |some| blk: {
- if (!is_mutable) {
- return self.failTok(&block_scope.base, some, "threadlocal variable cannot be constant", .{});
- }
- break :blk true;
- } else false;
- assert(var_decl.getComptimeToken() == null);
- if (var_decl.getAlignNode()) |align_expr| {
- return self.failNode(&block_scope.base, align_expr, "TODO implement function align expression", .{});
- }
- if (var_decl.getSectionNode()) |sect_expr| {
- return self.failNode(&block_scope.base, sect_expr, "TODO implement function section expression", .{});
- }
-
- const var_info: struct { ty: Type, val: ?Value } = if (var_decl.getInitNode()) |init_node| vi: {
- var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
- defer gen_scope_arena.deinit();
- var gen_scope: Scope.GenZIR = .{
- .decl = decl,
- .arena = &gen_scope_arena.allocator,
- .parent = decl.scope,
- };
- defer gen_scope.instructions.deinit(self.gpa);
-
- const init_result_loc: astgen.ResultLoc = if (var_decl.getTypeNode()) |type_node| rl: {
- const src = tree.token_locs[type_node.firstToken()].start;
- const type_type = try astgen.addZIRInstConst(self, &gen_scope.base, src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.type_type),
- });
- const var_type = try astgen.expr(self, &gen_scope.base, .{ .ty = type_type }, type_node);
- break :rl .{ .ty = var_type };
- } else .none;
-
- const src = tree.token_locs[init_node.firstToken()].start;
- const init_inst = try astgen.expr(self, &gen_scope.base, init_result_loc, init_node);
-
- var inner_block: Scope.Block = .{
- .parent = null,
- .func = null,
- .decl = decl,
- .instructions = .{},
- .arena = &gen_scope_arena.allocator,
- .is_comptime = true,
- };
- defer inner_block.instructions.deinit(self.gpa);
- try zir_sema.analyzeBody(self, &inner_block.base, .{ .instructions = gen_scope.instructions.items });
-
- // The result location guarantees the type coercion.
- const analyzed_init_inst = init_inst.analyzed_inst.?;
- // The is_comptime in the Scope.Block guarantees the result is comptime-known.
- const val = analyzed_init_inst.value().?;
-
- const ty = try analyzed_init_inst.ty.copy(block_scope.arena);
- break :vi .{
- .ty = ty,
- .val = try val.copy(block_scope.arena),
- };
- } else if (!is_extern) {
- return self.failTok(&block_scope.base, var_decl.firstToken(), "variables must be initialized", .{});
- } else if (var_decl.getTypeNode()) |type_node| vi: {
- // Temporary arena for the zir instructions.
- var type_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
- defer type_scope_arena.deinit();
- var type_scope: Scope.GenZIR = .{
- .decl = decl,
- .arena = &type_scope_arena.allocator,
- .parent = decl.scope,
- };
- defer type_scope.instructions.deinit(self.gpa);
-
- const src = tree.token_locs[type_node.firstToken()].start;
- const type_type = try astgen.addZIRInstConst(self, &type_scope.base, src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.type_type),
- });
- const var_type = try astgen.expr(self, &type_scope.base, .{ .ty = type_type }, type_node);
- const ty = try zir_sema.analyzeBodyValueAsType(self, &block_scope, var_type, .{
- .instructions = type_scope.instructions.items,
- });
- break :vi .{
- .ty = ty,
- .val = null,
- };
- } else {
- return self.failTok(&block_scope.base, var_decl.firstToken(), "unable to infer variable type", .{});
- };
-
- if (is_mutable and !var_info.ty.isValidVarType(is_extern)) {
- return self.failTok(&block_scope.base, var_decl.firstToken(), "variable of type '{}' must be const", .{var_info.ty});
- }
-
- var type_changed = true;
- if (decl.typedValueManaged()) |tvm| {
- type_changed = !tvm.typed_value.ty.eql(var_info.ty);
-
- tvm.deinit(self.gpa);
- }
-
- const new_variable = try decl_arena.allocator.create(Var);
- const var_payload = try decl_arena.allocator.create(Value.Payload.Variable);
- new_variable.* = .{
- .owner_decl = decl,
- .init = var_info.val orelse undefined,
- .is_extern = is_extern,
- .is_mutable = is_mutable,
- .is_threadlocal = is_threadlocal,
- };
- var_payload.* = .{ .variable = new_variable };
-
- decl_arena_state.* = decl_arena.state;
- decl.typed_value = .{
- .most_recent = .{
- .typed_value = .{
- .ty = var_info.ty,
- .val = Value.initPayload(&var_payload.base),
- },
- .arena = decl_arena_state,
- },
- };
- decl.analysis = .complete;
- decl.generation = self.generation;
-
- if (var_decl.getExternExportToken()) |maybe_export_token| {
- if (tree.token_ids[maybe_export_token] == .Keyword_export) {
- const export_src = tree.token_locs[maybe_export_token].start;
- const name_loc = tree.token_locs[var_decl.name_token];
- const name = tree.tokenSliceLoc(name_loc);
- // The scope needs to have the decl in it.
- try self.analyzeExport(&block_scope.base, export_src, name, decl);
- }
- }
- return type_changed;
- },
- .Comptime => {
- const comptime_decl = @fieldParentPtr(ast.Node.Comptime, "base", ast_node);
-
- decl.analysis = .in_progress;
-
- // A comptime decl does not store any value so we can just deinit this arena after analysis is done.
- var analysis_arena = std.heap.ArenaAllocator.init(self.gpa);
- defer analysis_arena.deinit();
- var gen_scope: Scope.GenZIR = .{
- .decl = decl,
- .arena = &analysis_arena.allocator,
- .parent = decl.scope,
- };
- defer gen_scope.instructions.deinit(self.gpa);
-
- _ = try astgen.comptimeExpr(self, &gen_scope.base, .none, comptime_decl.expr);
-
- var block_scope: Scope.Block = .{
- .parent = null,
- .func = null,
- .decl = decl,
- .instructions = .{},
- .arena = &analysis_arena.allocator,
- .is_comptime = true,
- };
- defer block_scope.instructions.deinit(self.gpa);
-
- _ = try zir_sema.analyzeBody(self, &block_scope.base, .{
- .instructions = gen_scope.instructions.items,
- });
-
- decl.analysis = .complete;
- decl.generation = self.generation;
- return true;
- },
- .Use => @panic("TODO usingnamespace decl"),
- else => unreachable,
- }
-}
-
-fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void {
- try depender.dependencies.ensureCapacity(self.gpa, depender.dependencies.items().len + 1);
- try dependee.dependants.ensureCapacity(self.gpa, dependee.dependants.items().len + 1);
-
- depender.dependencies.putAssumeCapacity(dependee, {});
- dependee.dependants.putAssumeCapacity(depender, {});
-}
-
-fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module {
- switch (root_scope.status) {
- .never_loaded, .unloaded_success => {
- try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
-
- const source = try root_scope.getSource(self);
-
- var keep_zir_module = false;
- const zir_module = try self.gpa.create(zir.Module);
- defer if (!keep_zir_module) self.gpa.destroy(zir_module);
-
- zir_module.* = try zir.parse(self.gpa, source);
- defer if (!keep_zir_module) zir_module.deinit(self.gpa);
-
- if (zir_module.error_msg) |src_err_msg| {
- self.failed_files.putAssumeCapacityNoClobber(
- &root_scope.base,
- try Compilation.ErrorMsg.create(self.gpa, src_err_msg.byte_offset, "{}", .{src_err_msg.msg}),
- );
- root_scope.status = .unloaded_parse_failure;
- return error.AnalysisFail;
- }
-
- root_scope.status = .loaded_success;
- root_scope.contents = .{ .module = zir_module };
- keep_zir_module = true;
-
- return zir_module;
- },
-
- .unloaded_parse_failure,
- .unloaded_sema_failure,
- => return error.AnalysisFail,
-
- .loaded_success, .loaded_sema_failure => return root_scope.contents.module,
- }
-}
-
-fn getAstTree(self: *Module, container_scope: *Scope.Container) !*ast.Tree {
- const tracy = trace(@src());
- defer tracy.end();
-
- const root_scope = container_scope.file_scope;
-
- switch (root_scope.status) {
- .never_loaded, .unloaded_success => {
- try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
-
- const source = try root_scope.getSource(self);
-
- var keep_tree = false;
- const tree = try std.zig.parse(self.gpa, source);
- defer if (!keep_tree) tree.deinit();
-
- if (tree.errors.len != 0) {
- const parse_err = tree.errors[0];
-
- var msg = std.ArrayList(u8).init(self.gpa);
- defer msg.deinit();
-
- try parse_err.render(tree.token_ids, msg.outStream());
- const err_msg = try self.gpa.create(Compilation.ErrorMsg);
- err_msg.* = .{
- .msg = msg.toOwnedSlice(),
- .byte_offset = tree.token_locs[parse_err.loc()].start,
- };
-
- self.failed_files.putAssumeCapacityNoClobber(&root_scope.base, err_msg);
- root_scope.status = .unloaded_parse_failure;
- return error.AnalysisFail;
- }
-
- root_scope.status = .loaded_success;
- root_scope.contents = .{ .tree = tree };
- keep_tree = true;
-
- return tree;
- },
-
- .unloaded_parse_failure => return error.AnalysisFail,
-
- .loaded_success => return root_scope.contents.tree,
- }
-}
-
-pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- // We may be analyzing it for the first time, or this may be
- // an incremental update. This code handles both cases.
- const tree = try self.getAstTree(container_scope);
- const decls = tree.root_node.decls();
-
- try self.comp.work_queue.ensureUnusedCapacity(decls.len);
- try container_scope.decls.ensureCapacity(self.gpa, decls.len);
-
- // Keep track of the decls that we expect to see in this file so that
- // we know which ones have been deleted.
- var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(self.gpa);
- defer deleted_decls.deinit();
- try deleted_decls.ensureCapacity(container_scope.decls.items().len);
- for (container_scope.decls.items()) |entry| {
- deleted_decls.putAssumeCapacityNoClobber(entry.key, {});
- }
-
- for (decls) |src_decl, decl_i| {
- if (src_decl.cast(ast.Node.FnProto)) |fn_proto| {
- // We will create a Decl for it regardless of analysis status.
- const name_tok = fn_proto.getNameToken() orelse {
- @panic("TODO missing function name");
- };
-
- const name_loc = tree.token_locs[name_tok];
- const name = tree.tokenSliceLoc(name_loc);
- const name_hash = container_scope.fullyQualifiedNameHash(name);
- const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
- if (self.decl_table.get(name_hash)) |decl| {
- // Update the AST Node index of the decl, even if its contents are unchanged, it may
- // have been re-ordered.
- decl.src_index = decl_i;
- if (deleted_decls.remove(decl) == null) {
- decl.analysis = .sema_failure;
- const err_msg = try Compilation.ErrorMsg.create(self.gpa, tree.token_locs[name_tok].start, "redefinition of '{}'", .{decl.name});
- errdefer err_msg.destroy(self.gpa);
- try self.failed_decls.putNoClobber(self.gpa, decl, err_msg);
- } else {
- if (!srcHashEql(decl.contents_hash, contents_hash)) {
- try self.markOutdatedDecl(decl);
- decl.contents_hash = contents_hash;
- } else switch (self.comp.bin_file.tag) {
- .coff => {
- // TODO Implement for COFF
- },
- .elf => if (decl.fn_link.elf.len != 0) {
- // TODO Look into detecting when this would be unnecessary by storing enough state
- // in `Decl` to notice that the line number did not change.
- self.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
- },
- .macho => {
- // TODO Implement for MachO
- },
- .c, .wasm => {},
- }
- }
- } else {
- const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
- container_scope.decls.putAssumeCapacity(new_decl, {});
- if (fn_proto.getExternExportInlineToken()) |maybe_export_token| {
- if (tree.token_ids[maybe_export_token] == .Keyword_export) {
- self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
- }
- }
- }
- } else if (src_decl.castTag(.VarDecl)) |var_decl| {
- const name_loc = tree.token_locs[var_decl.name_token];
- const name = tree.tokenSliceLoc(name_loc);
- const name_hash = container_scope.fullyQualifiedNameHash(name);
- const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
- if (self.decl_table.get(name_hash)) |decl| {
- // Update the AST Node index of the decl, even if its contents are unchanged, it may
- // have been re-ordered.
- decl.src_index = decl_i;
- if (deleted_decls.remove(decl) == null) {
- decl.analysis = .sema_failure;
- const err_msg = try Compilation.ErrorMsg.create(self.gpa, name_loc.start, "redefinition of '{}'", .{decl.name});
- errdefer err_msg.destroy(self.gpa);
- try self.failed_decls.putNoClobber(self.gpa, decl, err_msg);
- } else if (!srcHashEql(decl.contents_hash, contents_hash)) {
- try self.markOutdatedDecl(decl);
- decl.contents_hash = contents_hash;
- }
- } else {
- const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
- container_scope.decls.putAssumeCapacity(new_decl, {});
- if (var_decl.getExternExportToken()) |maybe_export_token| {
- if (tree.token_ids[maybe_export_token] == .Keyword_export) {
- self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
- }
- }
- }
- } else if (src_decl.castTag(.Comptime)) |comptime_node| {
- const name_index = self.getNextAnonNameIndex();
- const name = try std.fmt.allocPrint(self.gpa, "__comptime_{}", .{name_index});
- defer self.gpa.free(name);
-
- const name_hash = container_scope.fullyQualifiedNameHash(name);
- const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
-
- const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
- container_scope.decls.putAssumeCapacity(new_decl, {});
- self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
- } else if (src_decl.castTag(.ContainerField)) |container_field| {
- log.err("TODO: analyze container field", .{});
- } else if (src_decl.castTag(.TestDecl)) |test_decl| {
- log.err("TODO: analyze test decl", .{});
- } else if (src_decl.castTag(.Use)) |use_decl| {
- log.err("TODO: analyze usingnamespace decl", .{});
- } else {
- unreachable;
- }
- }
- // Handle explicitly deleted decls from the source code. Not to be confused
- // with when we delete decls because they are no longer referenced.
- for (deleted_decls.items()) |entry| {
- log.debug("noticed '{}' deleted from source\n", .{entry.key.name});
- try self.deleteDecl(entry.key);
- }
-}
-
-pub fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {
- // We may be analyzing it for the first time, or this may be
- // an incremental update. This code handles both cases.
- const src_module = try self.getSrcModule(root_scope);
-
- try self.comp.work_queue.ensureUnusedCapacity(src_module.decls.len);
- try root_scope.decls.ensureCapacity(self.gpa, src_module.decls.len);
-
- var exports_to_resolve = std.ArrayList(*zir.Decl).init(self.gpa);
- defer exports_to_resolve.deinit();
-
- // Keep track of the decls that we expect to see in this file so that
- // we know which ones have been deleted.
- var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(self.gpa);
- defer deleted_decls.deinit();
- try deleted_decls.ensureCapacity(self.decl_table.items().len);
- for (self.decl_table.items()) |entry| {
- deleted_decls.putAssumeCapacityNoClobber(entry.value, {});
- }
-
- for (src_module.decls) |src_decl, decl_i| {
- const name_hash = root_scope.fullyQualifiedNameHash(src_decl.name);
- if (self.decl_table.get(name_hash)) |decl| {
- deleted_decls.removeAssertDiscard(decl);
- if (!srcHashEql(src_decl.contents_hash, decl.contents_hash)) {
- try self.markOutdatedDecl(decl);
- decl.contents_hash = src_decl.contents_hash;
- }
- } else {
- const new_decl = try self.createNewDecl(
- &root_scope.base,
- src_decl.name,
- decl_i,
- name_hash,
- src_decl.contents_hash,
- );
- root_scope.decls.appendAssumeCapacity(new_decl);
- if (src_decl.inst.cast(zir.Inst.Export)) |export_inst| {
- try exports_to_resolve.append(src_decl);
- }
- }
- }
- for (exports_to_resolve.items) |export_decl| {
- _ = try zir_sema.resolveZirDecl(self, &root_scope.base, export_decl);
- }
- // Handle explicitly deleted decls from the source code. Not to be confused
- // with when we delete decls because they are no longer referenced.
- for (deleted_decls.items()) |entry| {
- log.debug("noticed '{}' deleted from source\n", .{entry.key.name});
- try self.deleteDecl(entry.key);
- }
-}
-
-pub fn deleteDecl(self: *Module, decl: *Decl) !void {
- try self.deletion_set.ensureCapacity(self.gpa, self.deletion_set.items.len + decl.dependencies.items().len);
-
- // Remove from the namespace it resides in. In the case of an anonymous Decl it will
- // not be present in the set, and this does nothing.
- decl.scope.removeDecl(decl);
-
- log.debug("deleting decl '{}'\n", .{decl.name});
- const name_hash = decl.fullyQualifiedNameHash();
- self.decl_table.removeAssertDiscard(name_hash);
- // Remove itself from its dependencies, because we are about to destroy the decl pointer.
- for (decl.dependencies.items()) |entry| {
- const dep = entry.key;
- dep.removeDependant(decl);
- if (dep.dependants.items().len == 0 and !dep.deletion_flag) {
- // We don't recursively perform a deletion here, because during the update,
- // another reference to it may turn up.
- dep.deletion_flag = true;
- self.deletion_set.appendAssumeCapacity(dep);
- }
- }
- // Anything that depends on this deleted decl certainly needs to be re-analyzed.
- for (decl.dependants.items()) |entry| {
- const dep = entry.key;
- dep.removeDependency(decl);
- if (dep.analysis != .outdated) {
- // TODO Move this failure possibility to the top of the function.
- try self.markOutdatedDecl(dep);
- }
- }
- if (self.failed_decls.remove(decl)) |entry| {
- entry.value.destroy(self.gpa);
- }
- self.deleteDeclExports(decl);
- self.comp.bin_file.freeDecl(decl);
- decl.destroy(self.gpa);
-}
-
-/// Delete all the Export objects that are caused by this Decl. Re-analysis of
-/// this Decl will cause them to be re-created (or not).
-fn deleteDeclExports(self: *Module, decl: *Decl) void {
- const kv = self.export_owners.remove(decl) orelse return;
-
- for (kv.value) |exp| {
- if (self.decl_exports.getEntry(exp.exported_decl)) |decl_exports_kv| {
- // Remove exports with owner_decl matching the regenerating decl.
- const list = decl_exports_kv.value;
- var i: usize = 0;
- var new_len = list.len;
- while (i < new_len) {
- if (list[i].owner_decl == decl) {
- mem.copyBackwards(*Export, list[i..], list[i + 1 .. new_len]);
- new_len -= 1;
- } else {
- i += 1;
- }
- }
- decl_exports_kv.value = self.gpa.shrink(list, new_len);
- if (new_len == 0) {
- self.decl_exports.removeAssertDiscard(exp.exported_decl);
- }
- }
- if (self.comp.bin_file.cast(link.File.Elf)) |elf| {
- elf.deleteExport(exp.link);
- }
- if (self.failed_exports.remove(exp)) |entry| {
- entry.value.destroy(self.gpa);
- }
- _ = self.symbol_exports.remove(exp.options.name);
- self.gpa.free(exp.options.name);
- self.gpa.destroy(exp);
- }
- self.gpa.free(kv.value);
-}
-
-pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- // Use the Decl's arena for function memory.
- var arena = decl.typed_value.most_recent.arena.?.promote(self.gpa);
- defer decl.typed_value.most_recent.arena.?.* = arena.state;
- var inner_block: Scope.Block = .{
- .parent = null,
- .func = func,
- .decl = decl,
- .instructions = .{},
- .arena = &arena.allocator,
- .is_comptime = false,
- };
- defer inner_block.instructions.deinit(self.gpa);
-
- const fn_zir = func.analysis.queued;
- defer fn_zir.arena.promote(self.gpa).deinit();
- func.analysis = .{ .in_progress = {} };
- log.debug("set {} to in_progress\n", .{decl.name});
-
- try zir_sema.analyzeBody(self, &inner_block.base, fn_zir.body);
-
- const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items);
- func.analysis = .{ .success = .{ .instructions = instructions } };
- log.debug("set {} to success\n", .{decl.name});
-}
-
-fn markOutdatedDecl(self: *Module, decl: *Decl) !void {
- log.debug("mark {} outdated\n", .{decl.name});
- try self.comp.work_queue.writeItem(.{ .analyze_decl = decl });
- if (self.failed_decls.remove(decl)) |entry| {
- entry.value.destroy(self.gpa);
- }
- decl.analysis = .outdated;
-}
-
-fn allocateNewDecl(
- self: *Module,
- scope: *Scope,
- src_index: usize,
- contents_hash: std.zig.SrcHash,
-) !*Decl {
- const new_decl = try self.gpa.create(Decl);
- new_decl.* = .{
- .name = "",
- .scope = scope.namespace(),
- .src_index = src_index,
- .typed_value = .{ .never_succeeded = {} },
- .analysis = .unreferenced,
- .deletion_flag = false,
- .contents_hash = contents_hash,
- .link = switch (self.comp.bin_file.tag) {
- .coff => .{ .coff = link.File.Coff.TextBlock.empty },
- .elf => .{ .elf = link.File.Elf.TextBlock.empty },
- .macho => .{ .macho = link.File.MachO.TextBlock.empty },
- .c => .{ .c = {} },
- .wasm => .{ .wasm = {} },
- },
- .fn_link = switch (self.comp.bin_file.tag) {
- .coff => .{ .coff = {} },
- .elf => .{ .elf = link.File.Elf.SrcFn.empty },
- .macho => .{ .macho = link.File.MachO.SrcFn.empty },
- .c => .{ .c = {} },
- .wasm => .{ .wasm = null },
- },
- .generation = 0,
- .is_pub = false,
- };
- return new_decl;
-}
-
-fn createNewDecl(
- self: *Module,
- scope: *Scope,
- decl_name: []const u8,
- src_index: usize,
- name_hash: Scope.NameHash,
- contents_hash: std.zig.SrcHash,
-) !*Decl {
- try self.decl_table.ensureCapacity(self.gpa, self.decl_table.items().len + 1);
- const new_decl = try self.allocateNewDecl(scope, src_index, contents_hash);
- errdefer self.gpa.destroy(new_decl);
- new_decl.name = try mem.dupeZ(self.gpa, u8, decl_name);
- self.decl_table.putAssumeCapacityNoClobber(name_hash, new_decl);
- return new_decl;
-}
-
-/// Get error value for error tag `name`.
-pub fn getErrorValue(self: *Module, name: []const u8) !std.StringHashMapUnmanaged(u16).Entry {
- const gop = try self.global_error_set.getOrPut(self.gpa, name);
- if (gop.found_existing)
- return gop.entry.*;
- errdefer self.global_error_set.removeAssertDiscard(name);
-
- gop.entry.key = try self.gpa.dupe(u8, name);
- gop.entry.value = @intCast(u16, self.global_error_set.count() - 1);
- return gop.entry.*;
-}
-
-pub fn requireFunctionBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
- return scope.cast(Scope.Block) orelse
- return self.fail(scope, src, "instruction illegal outside function body", .{});
-}
-
-pub fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
- const block = try self.requireFunctionBlock(scope, src);
- if (block.is_comptime) {
- return self.fail(scope, src, "unable to resolve comptime value", .{});
- }
- return block;
-}
-
-pub fn resolveConstValue(self: *Module, scope: *Scope, base: *Inst) !Value {
- return (try self.resolveDefinedValue(scope, base)) orelse
- return self.fail(scope, base.src, "unable to resolve comptime value", .{});
-}
-
-pub fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value {
- if (base.value()) |val| {
- if (val.isUndef()) {
- return self.fail(scope, base.src, "use of undefined value here causes undefined behavior", .{});
- }
- return val;
- }
- return null;
-}
-
-pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, borrowed_symbol_name: []const u8, exported_decl: *Decl) !void {
- try self.ensureDeclAnalyzed(exported_decl);
- const typed_value = exported_decl.typed_value.most_recent.typed_value;
- switch (typed_value.ty.zigTypeTag()) {
- .Fn => {},
- else => return self.fail(scope, src, "unable to export type '{}'", .{typed_value.ty}),
- }
-
- try self.decl_exports.ensureCapacity(self.gpa, self.decl_exports.items().len + 1);
- try self.export_owners.ensureCapacity(self.gpa, self.export_owners.items().len + 1);
-
- const new_export = try self.gpa.create(Export);
- errdefer self.gpa.destroy(new_export);
-
- const symbol_name = try self.gpa.dupe(u8, borrowed_symbol_name);
- errdefer self.gpa.free(symbol_name);
-
- const owner_decl = scope.decl().?;
-
- new_export.* = .{
- .options = .{ .name = symbol_name },
- .src = src,
- .link = .{},
- .owner_decl = owner_decl,
- .exported_decl = exported_decl,
- .status = .in_progress,
- };
-
- // Add to export_owners table.
- const eo_gop = self.export_owners.getOrPutAssumeCapacity(owner_decl);
- if (!eo_gop.found_existing) {
- eo_gop.entry.value = &[0]*Export{};
- }
- eo_gop.entry.value = try self.gpa.realloc(eo_gop.entry.value, eo_gop.entry.value.len + 1);
- eo_gop.entry.value[eo_gop.entry.value.len - 1] = new_export;
- errdefer eo_gop.entry.value = self.gpa.shrink(eo_gop.entry.value, eo_gop.entry.value.len - 1);
-
- // Add to exported_decl table.
- const de_gop = self.decl_exports.getOrPutAssumeCapacity(exported_decl);
- if (!de_gop.found_existing) {
- de_gop.entry.value = &[0]*Export{};
- }
- de_gop.entry.value = try self.gpa.realloc(de_gop.entry.value, de_gop.entry.value.len + 1);
- de_gop.entry.value[de_gop.entry.value.len - 1] = new_export;
- errdefer de_gop.entry.value = self.gpa.shrink(de_gop.entry.value, de_gop.entry.value.len - 1);
-
- if (self.symbol_exports.get(symbol_name)) |_| {
- try self.failed_exports.ensureCapacity(self.gpa, self.failed_exports.items().len + 1);
- self.failed_exports.putAssumeCapacityNoClobber(new_export, try Compilation.ErrorMsg.create(
- self.gpa,
- src,
- "exported symbol collision: {}",
- .{symbol_name},
- ));
- // TODO: add a note
- new_export.status = .failed;
- return;
- }
-
- try self.symbol_exports.putNoClobber(self.gpa, symbol_name, new_export);
- self.comp.bin_file.updateDeclExports(self, exported_decl, de_gop.entry.value) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- else => {
- try self.failed_exports.ensureCapacity(self.gpa, self.failed_exports.items().len + 1);
- self.failed_exports.putAssumeCapacityNoClobber(new_export, try Compilation.ErrorMsg.create(
- self.gpa,
- src,
- "unable to export: {}",
- .{@errorName(err)},
- ));
- new_export.status = .failed_retryable;
- },
- };
-}
-
-pub fn addNoOp(
- self: *Module,
- block: *Scope.Block,
- src: usize,
- ty: Type,
- comptime tag: Inst.Tag,
-) !*Inst {
- const inst = try block.arena.create(tag.Type());
- inst.* = .{
- .base = .{
- .tag = tag,
- .ty = ty,
- .src = src,
- },
- };
- try block.instructions.append(self.gpa, &inst.base);
- return &inst.base;
-}
-
-pub fn addUnOp(
- self: *Module,
- block: *Scope.Block,
- src: usize,
- ty: Type,
- tag: Inst.Tag,
- operand: *Inst,
-) !*Inst {
- const inst = try block.arena.create(Inst.UnOp);
- inst.* = .{
- .base = .{
- .tag = tag,
- .ty = ty,
- .src = src,
- },
- .operand = operand,
- };
- try block.instructions.append(self.gpa, &inst.base);
- return &inst.base;
-}
-
-pub fn addBinOp(
- self: *Module,
- block: *Scope.Block,
- src: usize,
- ty: Type,
- tag: Inst.Tag,
- lhs: *Inst,
- rhs: *Inst,
-) !*Inst {
- const inst = try block.arena.create(Inst.BinOp);
- inst.* = .{
- .base = .{
- .tag = tag,
- .ty = ty,
- .src = src,
- },
- .lhs = lhs,
- .rhs = rhs,
- };
- try block.instructions.append(self.gpa, &inst.base);
- return &inst.base;
-}
-
-pub fn addArg(self: *Module, block: *Scope.Block, src: usize, ty: Type, name: [*:0]const u8) !*Inst {
- const inst = try block.arena.create(Inst.Arg);
- inst.* = .{
- .base = .{
- .tag = .arg,
- .ty = ty,
- .src = src,
- },
- .name = name,
- };
- try block.instructions.append(self.gpa, &inst.base);
- return &inst.base;
-}
-
-pub fn addBr(
- self: *Module,
- scope_block: *Scope.Block,
- src: usize,
- target_block: *Inst.Block,
- operand: *Inst,
-) !*Inst {
- const inst = try scope_block.arena.create(Inst.Br);
- inst.* = .{
- .base = .{
- .tag = .br,
- .ty = Type.initTag(.noreturn),
- .src = src,
- },
- .operand = operand,
- .block = target_block,
- };
- try scope_block.instructions.append(self.gpa, &inst.base);
- return &inst.base;
-}
-
-pub fn addCondBr(
- self: *Module,
- block: *Scope.Block,
- src: usize,
- condition: *Inst,
- then_body: ir.Body,
- else_body: ir.Body,
-) !*Inst {
- const inst = try block.arena.create(Inst.CondBr);
- inst.* = .{
- .base = .{
- .tag = .condbr,
- .ty = Type.initTag(.noreturn),
- .src = src,
- },
- .condition = condition,
- .then_body = then_body,
- .else_body = else_body,
- };
- try block.instructions.append(self.gpa, &inst.base);
- return &inst.base;
-}
-
-pub fn addCall(
- self: *Module,
- block: *Scope.Block,
- src: usize,
- ty: Type,
- func: *Inst,
- args: []const *Inst,
-) !*Inst {
- const inst = try block.arena.create(Inst.Call);
- inst.* = .{
- .base = .{
- .tag = .call,
- .ty = ty,
- .src = src,
- },
- .func = func,
- .args = args,
- };
- try block.instructions.append(self.gpa, &inst.base);
- return &inst.base;
-}
-
-pub fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*Inst {
- const const_inst = try scope.arena().create(Inst.Constant);
- const_inst.* = .{
- .base = .{
- .tag = Inst.Constant.base_tag,
- .ty = typed_value.ty,
- .src = src,
- },
- .val = typed_value.val,
- };
- return &const_inst.base;
-}
-
-pub fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
- return self.constInst(scope, src, .{
- .ty = Type.initTag(.type),
- .val = try ty.toValue(scope.arena()),
- });
-}
-
-pub fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst {
- return self.constInst(scope, src, .{
- .ty = Type.initTag(.void),
- .val = Value.initTag(.void_value),
- });
-}
-
-pub fn constNoReturn(self: *Module, scope: *Scope, src: usize) !*Inst {
- return self.constInst(scope, src, .{
- .ty = Type.initTag(.noreturn),
- .val = Value.initTag(.unreachable_value),
- });
-}
-
-pub fn constUndef(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
- return self.constInst(scope, src, .{
- .ty = ty,
- .val = Value.initTag(.undef),
- });
-}
-
-pub fn constBool(self: *Module, scope: *Scope, src: usize, v: bool) !*Inst {
- return self.constInst(scope, src, .{
- .ty = Type.initTag(.bool),
- .val = ([2]Value{ Value.initTag(.bool_false), Value.initTag(.bool_true) })[@boolToInt(v)],
- });
-}
-
-pub fn constIntUnsigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: u64) !*Inst {
- const int_payload = try scope.arena().create(Value.Payload.Int_u64);
- int_payload.* = .{ .int = int };
-
- return self.constInst(scope, src, .{
- .ty = ty,
- .val = Value.initPayload(&int_payload.base),
- });
-}
-
-pub fn constIntSigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: i64) !*Inst {
- const int_payload = try scope.arena().create(Value.Payload.Int_i64);
- int_payload.* = .{ .int = int };
-
- return self.constInst(scope, src, .{
- .ty = ty,
- .val = Value.initPayload(&int_payload.base),
- });
-}
-
-pub fn constIntBig(self: *Module, scope: *Scope, src: usize, ty: Type, big_int: BigIntConst) !*Inst {
- const val_payload = if (big_int.positive) blk: {
- if (big_int.to(u64)) |x| {
- return self.constIntUnsigned(scope, src, ty, x);
- } else |err| switch (err) {
- error.NegativeIntoUnsigned => unreachable,
- error.TargetTooSmall => {}, // handled below
- }
- const big_int_payload = try scope.arena().create(Value.Payload.IntBigPositive);
- big_int_payload.* = .{ .limbs = big_int.limbs };
- break :blk &big_int_payload.base;
- } else blk: {
- if (big_int.to(i64)) |x| {
- return self.constIntSigned(scope, src, ty, x);
- } else |err| switch (err) {
- error.NegativeIntoUnsigned => unreachable,
- error.TargetTooSmall => {}, // handled below
- }
- const big_int_payload = try scope.arena().create(Value.Payload.IntBigNegative);
- big_int_payload.* = .{ .limbs = big_int.limbs };
- break :blk &big_int_payload.base;
- };
-
- return self.constInst(scope, src, .{
- .ty = ty,
- .val = Value.initPayload(val_payload),
- });
-}
-
-pub fn createAnonymousDecl(
- self: *Module,
- scope: *Scope,
- decl_arena: *std.heap.ArenaAllocator,
- typed_value: TypedValue,
-) !*Decl {
- const name_index = self.getNextAnonNameIndex();
- const scope_decl = scope.decl().?;
- const name = try std.fmt.allocPrint(self.gpa, "{}__anon_{}", .{ scope_decl.name, name_index });
- defer self.gpa.free(name);
- const name_hash = scope.namespace().fullyQualifiedNameHash(name);
- const src_hash: std.zig.SrcHash = undefined;
- const new_decl = try self.createNewDecl(scope, name, scope_decl.src_index, name_hash, src_hash);
- const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
-
- decl_arena_state.* = decl_arena.state;
- new_decl.typed_value = .{
- .most_recent = .{
- .typed_value = typed_value,
- .arena = decl_arena_state,
- },
- };
- new_decl.analysis = .complete;
- new_decl.generation = self.generation;
-
- // TODO: This generates the Decl into the machine code file if it is of a type that is non-zero size.
- // We should be able to further improve the compiler to not omit Decls which are only referenced at
- // compile-time and not runtime.
- if (typed_value.ty.hasCodeGenBits()) {
- try self.comp.bin_file.allocateDeclIndexes(new_decl);
- try self.comp.work_queue.writeItem(.{ .codegen_decl = new_decl });
- }
-
- return new_decl;
-}
-
-fn getNextAnonNameIndex(self: *Module) usize {
- return @atomicRmw(usize, &self.next_anon_name_index, .Add, 1, .Monotonic);
-}
-
-pub fn lookupDeclName(self: *Module, scope: *Scope, ident_name: []const u8) ?*Decl {
- const namespace = scope.namespace();
- const name_hash = namespace.fullyQualifiedNameHash(ident_name);
- return self.decl_table.get(name_hash);
-}
-
-pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst {
- const scope_decl = scope.decl().?;
- try self.declareDeclDependency(scope_decl, decl);
- self.ensureDeclAnalyzed(decl) catch |err| {
- if (scope.cast(Scope.Block)) |block| {
- if (block.func) |func| {
- func.analysis = .dependency_failure;
- } else {
- block.decl.analysis = .dependency_failure;
- }
- } else {
- scope_decl.analysis = .dependency_failure;
- }
- return err;
- };
-
- const decl_tv = try decl.typedValue();
- if (decl_tv.val.tag() == .variable) {
- return self.analyzeVarRef(scope, src, decl_tv);
- }
- const ty = try self.simplePtrType(scope, src, decl_tv.ty, false, .One);
- const val_payload = try scope.arena().create(Value.Payload.DeclRef);
- val_payload.* = .{ .decl = decl };
-
- return self.constInst(scope, src, .{
- .ty = ty,
- .val = Value.initPayload(&val_payload.base),
- });
-}
-
-fn analyzeVarRef(self: *Module, scope: *Scope, src: usize, tv: TypedValue) InnerError!*Inst {
- const variable = tv.val.cast(Value.Payload.Variable).?.variable;
-
- const ty = try self.simplePtrType(scope, src, tv.ty, variable.is_mutable, .One);
- if (!variable.is_mutable and !variable.is_extern) {
- const val_payload = try scope.arena().create(Value.Payload.RefVal);
- val_payload.* = .{ .val = variable.init };
- return self.constInst(scope, src, .{
- .ty = ty,
- .val = Value.initPayload(&val_payload.base),
- });
- }
-
- const b = try self.requireRuntimeBlock(scope, src);
- const inst = try b.arena.create(Inst.VarPtr);
- inst.* = .{
- .base = .{
- .tag = .varptr,
- .ty = ty,
- .src = src,
- },
- .variable = variable,
- };
- try b.instructions.append(self.gpa, &inst.base);
- return &inst.base;
-}
-
-pub fn analyzeDeref(self: *Module, scope: *Scope, src: usize, ptr: *Inst, ptr_src: usize) InnerError!*Inst {
- const elem_ty = switch (ptr.ty.zigTypeTag()) {
- .Pointer => ptr.ty.elemType(),
- else => return self.fail(scope, ptr_src, "expected pointer, found '{}'", .{ptr.ty}),
- };
- if (ptr.value()) |val| {
- return self.constInst(scope, src, .{
- .ty = elem_ty,
- .val = try val.pointerDeref(scope.arena()),
- });
- }
-
- const b = try self.requireRuntimeBlock(scope, src);
- return self.addUnOp(b, src, elem_ty, .load, ptr);
-}
-
-pub fn analyzeDeclRefByName(self: *Module, scope: *Scope, src: usize, decl_name: []const u8) InnerError!*Inst {
- const decl = self.lookupDeclName(scope, decl_name) orelse
- return self.fail(scope, src, "decl '{}' not found", .{decl_name});
- return self.analyzeDeclRef(scope, src, decl);
-}
-
-pub fn wantSafety(self: *Module, scope: *Scope) bool {
- // TODO take into account scope's safety overrides
- return switch (self.optimizeMode()) {
- .Debug => true,
- .ReleaseSafe => true,
- .ReleaseFast => false,
- .ReleaseSmall => false,
- };
-}
-
-pub fn analyzeIsNull(
- self: *Module,
- scope: *Scope,
- src: usize,
- operand: *Inst,
- invert_logic: bool,
-) InnerError!*Inst {
- if (operand.value()) |opt_val| {
- const is_null = opt_val.isNull();
- const bool_value = if (invert_logic) !is_null else is_null;
- return self.constBool(scope, src, bool_value);
- }
- const b = try self.requireRuntimeBlock(scope, src);
- const inst_tag: Inst.Tag = if (invert_logic) .isnonnull else .isnull;
- return self.addUnOp(b, src, Type.initTag(.bool), inst_tag, operand);
-}
-
-pub fn analyzeIsErr(self: *Module, scope: *Scope, src: usize, operand: *Inst) InnerError!*Inst {
- return self.fail(scope, src, "TODO implement analysis of iserr", .{});
-}
-
-pub fn analyzeSlice(self: *Module, scope: *Scope, src: usize, array_ptr: *Inst, start: *Inst, end_opt: ?*Inst, sentinel_opt: ?*Inst) InnerError!*Inst {
- const ptr_child = switch (array_ptr.ty.zigTypeTag()) {
- .Pointer => array_ptr.ty.elemType(),
- else => return self.fail(scope, src, "expected pointer, found '{}'", .{array_ptr.ty}),
- };
-
- var array_type = ptr_child;
- const elem_type = switch (ptr_child.zigTypeTag()) {
- .Array => ptr_child.elemType(),
- .Pointer => blk: {
- if (ptr_child.isSinglePointer()) {
- if (ptr_child.elemType().zigTypeTag() == .Array) {
- array_type = ptr_child.elemType();
- break :blk ptr_child.elemType().elemType();
- }
-
- return self.fail(scope, src, "slice of single-item pointer", .{});
- }
- break :blk ptr_child.elemType();
- },
- else => return self.fail(scope, src, "slice of non-array type '{}'", .{ptr_child}),
- };
-
- const slice_sentinel = if (sentinel_opt) |sentinel| blk: {
- const casted = try self.coerce(scope, elem_type, sentinel);
- break :blk try self.resolveConstValue(scope, casted);
- } else null;
-
- var return_ptr_size: std.builtin.TypeInfo.Pointer.Size = .Slice;
- var return_elem_type = elem_type;
- if (end_opt) |end| {
- if (end.value()) |end_val| {
- if (start.value()) |start_val| {
- const start_u64 = start_val.toUnsignedInt();
- const end_u64 = end_val.toUnsignedInt();
- if (start_u64 > end_u64) {
- return self.fail(scope, src, "out of bounds slice", .{});
- }
-
- const len = end_u64 - start_u64;
- const array_sentinel = if (array_type.zigTypeTag() == .Array and end_u64 == array_type.arrayLen())
- array_type.sentinel()
- else
- slice_sentinel;
- return_elem_type = try self.arrayType(scope, len, array_sentinel, elem_type);
- return_ptr_size = .One;
- }
- }
- }
- const return_type = try self.ptrType(
- scope,
- src,
- return_elem_type,
- if (end_opt == null) slice_sentinel else null,
- 0, // TODO alignment
- 0,
- 0,
- !ptr_child.isConstPtr(),
- ptr_child.isAllowzeroPtr(),
- ptr_child.isVolatilePtr(),
- return_ptr_size,
- );
-
- return self.fail(scope, src, "TODO implement analysis of slice", .{});
-}
-
-/// Asserts that lhs and rhs types are both numeric.
-pub fn cmpNumeric(
- self: *Module,
- scope: *Scope,
- src: usize,
- lhs: *Inst,
- rhs: *Inst,
- op: std.math.CompareOperator,
-) !*Inst {
- assert(lhs.ty.isNumeric());
- assert(rhs.ty.isNumeric());
-
- const lhs_ty_tag = lhs.ty.zigTypeTag();
- const rhs_ty_tag = rhs.ty.zigTypeTag();
-
- if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) {
- if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) {
- return self.fail(scope, src, "vector length mismatch: {} and {}", .{
- lhs.ty.arrayLen(),
- rhs.ty.arrayLen(),
- });
- }
- return self.fail(scope, src, "TODO implement support for vectors in cmpNumeric", .{});
- } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) {
- return self.fail(scope, src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{
- lhs.ty,
- rhs.ty,
- });
- }
-
- if (lhs.value()) |lhs_val| {
- if (rhs.value()) |rhs_val| {
- return self.constBool(scope, src, Value.compare(lhs_val, op, rhs_val));
- }
- }
-
- // TODO handle comparisons against lazy zero values
- // Some values can be compared against zero without being runtime known or without forcing
- // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to
- // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout
- // of this function if we don't need to.
-
- // It must be a runtime comparison.
- const b = try self.requireRuntimeBlock(scope, src);
- // For floats, emit a float comparison instruction.
- const lhs_is_float = switch (lhs_ty_tag) {
- .Float, .ComptimeFloat => true,
- else => false,
- };
- const rhs_is_float = switch (rhs_ty_tag) {
- .Float, .ComptimeFloat => true,
- else => false,
- };
- if (lhs_is_float and rhs_is_float) {
- // Implicit cast the smaller one to the larger one.
- const dest_type = x: {
- if (lhs_ty_tag == .ComptimeFloat) {
- break :x rhs.ty;
- } else if (rhs_ty_tag == .ComptimeFloat) {
- break :x lhs.ty;
- }
- if (lhs.ty.floatBits(self.getTarget()) >= rhs.ty.floatBits(self.getTarget())) {
- break :x lhs.ty;
- } else {
- break :x rhs.ty;
- }
- };
- const casted_lhs = try self.coerce(scope, dest_type, lhs);
- const casted_rhs = try self.coerce(scope, dest_type, rhs);
- return self.addBinOp(b, src, dest_type, Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
- }
- // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.
- // For mixed signed and unsigned integers, implicit cast both operands to a signed
- // integer with + 1 bit.
- // For mixed floats and integers, extract the integer part from the float, cast that to
- // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float,
- // add/subtract 1.
- const lhs_is_signed = if (lhs.value()) |lhs_val|
- lhs_val.compareWithZero(.lt)
- else
- (lhs.ty.isFloat() or lhs.ty.isSignedInt());
- const rhs_is_signed = if (rhs.value()) |rhs_val|
- rhs_val.compareWithZero(.lt)
- else
- (rhs.ty.isFloat() or rhs.ty.isSignedInt());
- const dest_int_is_signed = lhs_is_signed or rhs_is_signed;
-
- var dest_float_type: ?Type = null;
-
- var lhs_bits: usize = undefined;
- if (lhs.value()) |lhs_val| {
- if (lhs_val.isUndef())
- return self.constUndef(scope, src, Type.initTag(.bool));
- const is_unsigned = if (lhs_is_float) x: {
- var bigint_space: Value.BigIntSpace = undefined;
- var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(self.gpa);
- defer bigint.deinit();
- const zcmp = lhs_val.orderAgainstZero();
- if (lhs_val.floatHasFraction()) {
- switch (op) {
- .eq => return self.constBool(scope, src, false),
- .neq => return self.constBool(scope, src, true),
- else => {},
- }
- if (zcmp == .lt) {
- try bigint.addScalar(bigint.toConst(), -1);
- } else {
- try bigint.addScalar(bigint.toConst(), 1);
- }
- }
- lhs_bits = bigint.toConst().bitCountTwosComp();
- break :x (zcmp != .lt);
- } else x: {
- lhs_bits = lhs_val.intBitCountTwosComp();
- break :x (lhs_val.orderAgainstZero() != .lt);
- };
- lhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
- } else if (lhs_is_float) {
- dest_float_type = lhs.ty;
- } else {
- const int_info = lhs.ty.intInfo(self.getTarget());
- lhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed);
- }
-
- var rhs_bits: usize = undefined;
- if (rhs.value()) |rhs_val| {
- if (rhs_val.isUndef())
- return self.constUndef(scope, src, Type.initTag(.bool));
- const is_unsigned = if (rhs_is_float) x: {
- var bigint_space: Value.BigIntSpace = undefined;
- var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(self.gpa);
- defer bigint.deinit();
- const zcmp = rhs_val.orderAgainstZero();
- if (rhs_val.floatHasFraction()) {
- switch (op) {
- .eq => return self.constBool(scope, src, false),
- .neq => return self.constBool(scope, src, true),
- else => {},
- }
- if (zcmp == .lt) {
- try bigint.addScalar(bigint.toConst(), -1);
- } else {
- try bigint.addScalar(bigint.toConst(), 1);
- }
- }
- rhs_bits = bigint.toConst().bitCountTwosComp();
- break :x (zcmp != .lt);
- } else x: {
- rhs_bits = rhs_val.intBitCountTwosComp();
- break :x (rhs_val.orderAgainstZero() != .lt);
- };
- rhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
- } else if (rhs_is_float) {
- dest_float_type = rhs.ty;
- } else {
- const int_info = rhs.ty.intInfo(self.getTarget());
- rhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed);
- }
-
- const dest_type = if (dest_float_type) |ft| ft else blk: {
- const max_bits = std.math.max(lhs_bits, rhs_bits);
- const casted_bits = std.math.cast(u16, max_bits) catch |err| switch (err) {
- error.Overflow => return self.fail(scope, src, "{} exceeds maximum integer bit count", .{max_bits}),
- };
- break :blk try self.makeIntType(scope, dest_int_is_signed, casted_bits);
- };
- const casted_lhs = try self.coerce(scope, dest_type, lhs);
- const casted_rhs = try self.coerce(scope, dest_type, rhs);
-
- return self.addBinOp(b, src, Type.initTag(.bool), Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
-}
-
-fn wrapOptional(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
- if (inst.value()) |val| {
- return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
- }
-
- const b = try self.requireRuntimeBlock(scope, inst.src);
- return self.addUnOp(b, inst.src, dest_type, .wrap_optional, inst);
-}
-
-fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type {
- if (signed) {
- const int_payload = try scope.arena().create(Type.Payload.IntSigned);
- int_payload.* = .{ .bits = bits };
- return Type.initPayload(&int_payload.base);
- } else {
- const int_payload = try scope.arena().create(Type.Payload.IntUnsigned);
- int_payload.* = .{ .bits = bits };
- return Type.initPayload(&int_payload.base);
- }
-}
-
-pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Type {
- if (instructions.len == 0)
- return Type.initTag(.noreturn);
-
- if (instructions.len == 1)
- return instructions[0].ty;
-
- var prev_inst = instructions[0];
- for (instructions[1..]) |next_inst| {
- if (next_inst.ty.eql(prev_inst.ty))
- continue;
- if (next_inst.ty.zigTypeTag() == .NoReturn)
- continue;
- if (prev_inst.ty.zigTypeTag() == .NoReturn) {
- prev_inst = next_inst;
- continue;
- }
- if (next_inst.ty.zigTypeTag() == .Undefined)
- continue;
- if (prev_inst.ty.zigTypeTag() == .Undefined) {
- prev_inst = next_inst;
- continue;
- }
- if (prev_inst.ty.isInt() and
- next_inst.ty.isInt() and
- prev_inst.ty.isSignedInt() == next_inst.ty.isSignedInt())
- {
- if (prev_inst.ty.intInfo(self.getTarget()).bits < next_inst.ty.intInfo(self.getTarget()).bits) {
- prev_inst = next_inst;
- }
- continue;
- }
- if (prev_inst.ty.isFloat() and next_inst.ty.isFloat()) {
- if (prev_inst.ty.floatBits(self.getTarget()) < next_inst.ty.floatBits(self.getTarget())) {
- prev_inst = next_inst;
- }
- continue;
- }
-
- // TODO error notes pointing out each type
- return self.fail(scope, next_inst.src, "incompatible types: '{}' and '{}'", .{ prev_inst.ty, next_inst.ty });
- }
-
- return prev_inst.ty;
-}
-
-pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
- // If the types are the same, we can return the operand.
- if (dest_type.eql(inst.ty))
- return inst;
-
- const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty);
- if (in_memory_result == .ok) {
- return self.bitcast(scope, dest_type, inst);
- }
-
- // undefined to anything
- if (inst.value()) |val| {
- if (val.isUndef() or inst.ty.zigTypeTag() == .Undefined) {
- return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
- }
- }
- assert(inst.ty.zigTypeTag() != .Undefined);
-
- // null to ?T
- if (dest_type.zigTypeTag() == .Optional and inst.ty.zigTypeTag() == .Null) {
- return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = Value.initTag(.null_value) });
- }
-
- // T to ?T
- if (dest_type.zigTypeTag() == .Optional) {
- var buf: Type.Payload.PointerSimple = undefined;
- const child_type = dest_type.optionalChild(&buf);
- if (child_type.eql(inst.ty)) {
- return self.wrapOptional(scope, dest_type, inst);
- } else if (try self.coerceNum(scope, child_type, inst)) |some| {
- return self.wrapOptional(scope, dest_type, some);
- }
- }
-
- // *[N]T to []T
- if (inst.ty.isSinglePointer() and dest_type.isSlice() and
- (!inst.ty.isConstPtr() or dest_type.isConstPtr()))
- {
- const array_type = inst.ty.elemType();
- const dst_elem_type = dest_type.elemType();
- if (array_type.zigTypeTag() == .Array and
- coerceInMemoryAllowed(dst_elem_type, array_type.elemType()) == .ok)
- {
- return self.coerceArrayPtrToSlice(scope, dest_type, inst);
- }
- }
-
- // comptime known number to other number
- if (try self.coerceNum(scope, dest_type, inst)) |some|
- return some;
-
- // integer widening
- if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) {
- assert(inst.value() == null); // handled above
-
- const src_info = inst.ty.intInfo(self.getTarget());
- const dst_info = dest_type.intInfo(self.getTarget());
- if ((src_info.signed == dst_info.signed and dst_info.bits >= src_info.bits) or
- // small enough unsigned ints can get casted to large enough signed ints
- (src_info.signed and !dst_info.signed and dst_info.bits > src_info.bits))
- {
- const b = try self.requireRuntimeBlock(scope, inst.src);
- return self.addUnOp(b, inst.src, dest_type, .intcast, inst);
- }
- }
-
- // float widening
- if (inst.ty.zigTypeTag() == .Float and dest_type.zigTypeTag() == .Float) {
- assert(inst.value() == null); // handled above
-
- const src_bits = inst.ty.floatBits(self.getTarget());
- const dst_bits = dest_type.floatBits(self.getTarget());
- if (dst_bits >= src_bits) {
- const b = try self.requireRuntimeBlock(scope, inst.src);
- return self.addUnOp(b, inst.src, dest_type, .floatcast, inst);
- }
- }
-
- return self.fail(scope, inst.src, "expected {}, found {}", .{ dest_type, inst.ty });
-}
-
-pub fn coerceNum(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !?*Inst {
- const val = inst.value() orelse return null;
- const src_zig_tag = inst.ty.zigTypeTag();
- const dst_zig_tag = dest_type.zigTypeTag();
-
- if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) {
- if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
- if (val.floatHasFraction()) {
- return self.fail(scope, inst.src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst.ty });
- }
- return self.fail(scope, inst.src, "TODO float to int", .{});
- } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
- if (!val.intFitsInType(dest_type, self.getTarget())) {
- return self.fail(scope, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val });
- }
- return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
- }
- } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) {
- if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
- const res = val.floatCast(scope.arena(), dest_type, self.getTarget()) catch |err| switch (err) {
- error.Overflow => return self.fail(
- scope,
- inst.src,
- "cast of value {} to type '{}' loses information",
- .{ val, dest_type },
- ),
- error.OutOfMemory => return error.OutOfMemory,
- };
- return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = res });
- } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
- return self.fail(scope, inst.src, "TODO int to float", .{});
- }
- }
- return null;
-}
-
-pub fn storePtr(self: *Module, scope: *Scope, src: usize, ptr: *Inst, uncasted_value: *Inst) !*Inst {
- if (ptr.ty.isConstPtr())
- return self.fail(scope, src, "cannot assign to constant", .{});
-
- const elem_ty = ptr.ty.elemType();
- const value = try self.coerce(scope, elem_ty, uncasted_value);
- if (elem_ty.onePossibleValue() != null)
- return self.constVoid(scope, src);
-
- // TODO handle comptime pointer writes
- // TODO handle if the element type requires comptime
-
- const b = try self.requireRuntimeBlock(scope, src);
- return self.addBinOp(b, src, Type.initTag(.void), .store, ptr, value);
-}
-
-pub fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
- if (inst.value()) |val| {
- // Keep the comptime Value representation; take the new type.
- return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
- }
- // TODO validate the type size and other compile errors
- const b = try self.requireRuntimeBlock(scope, inst.src);
- return self.addUnOp(b, inst.src, dest_type, .bitcast, inst);
-}
-
-fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
- if (inst.value()) |val| {
- // The comptime Value representation is compatible with both types.
- return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
- }
- return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});
-}
-
-pub fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: anytype) InnerError {
- @setCold(true);
- const err_msg = try Compilation.ErrorMsg.create(self.gpa, src, format, args);
- return self.failWithOwnedErrorMsg(scope, src, err_msg);
-}
-
-pub fn failTok(
- self: *Module,
- scope: *Scope,
- token_index: ast.TokenIndex,
- comptime format: []const u8,
- args: anytype,
-) InnerError {
- @setCold(true);
- const src = scope.tree().token_locs[token_index].start;
- return self.fail(scope, src, format, args);
-}
-
-pub fn failNode(
- self: *Module,
- scope: *Scope,
- ast_node: *ast.Node,
- comptime format: []const u8,
- args: anytype,
-) InnerError {
- @setCold(true);
- const src = scope.tree().token_locs[ast_node.firstToken()].start;
- return self.fail(scope, src, format, args);
-}
-
-fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Compilation.ErrorMsg) InnerError {
- {
- errdefer err_msg.destroy(self.gpa);
- try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
- try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
- }
- switch (scope.tag) {
- .decl => {
- const decl = scope.cast(Scope.DeclAnalysis).?.decl;
- decl.analysis = .sema_failure;
- decl.generation = self.generation;
- self.failed_decls.putAssumeCapacityNoClobber(decl, err_msg);
- },
- .block => {
- const block = scope.cast(Scope.Block).?;
- if (block.func) |func| {
- func.analysis = .sema_failure;
- } else {
- block.decl.analysis = .sema_failure;
- block.decl.generation = self.generation;
- }
- self.failed_decls.putAssumeCapacityNoClobber(block.decl, err_msg);
- },
- .gen_zir => {
- const gen_zir = scope.cast(Scope.GenZIR).?;
- gen_zir.decl.analysis = .sema_failure;
- gen_zir.decl.generation = self.generation;
- self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg);
- },
- .local_val => {
- const gen_zir = scope.cast(Scope.LocalVal).?.gen_zir;
- gen_zir.decl.analysis = .sema_failure;
- gen_zir.decl.generation = self.generation;
- self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg);
- },
- .local_ptr => {
- const gen_zir = scope.cast(Scope.LocalPtr).?.gen_zir;
- gen_zir.decl.analysis = .sema_failure;
- gen_zir.decl.generation = self.generation;
- self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg);
- },
- .zir_module => {
- const zir_module = scope.cast(Scope.ZIRModule).?;
- zir_module.status = .loaded_sema_failure;
- self.failed_files.putAssumeCapacityNoClobber(scope, err_msg);
- },
- .file => unreachable,
- .container => unreachable,
- }
- return error.AnalysisFail;
-}
-
-const InMemoryCoercionResult = enum {
- ok,
- no_match,
-};
-
-fn coerceInMemoryAllowed(dest_type: Type, src_type: Type) InMemoryCoercionResult {
- if (dest_type.eql(src_type))
- return .ok;
-
- // TODO: implement more of this function
-
- return .no_match;
-}
-
-fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool {
- return @bitCast(u128, a) == @bitCast(u128, b);
-}
-
-pub fn intAdd(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
- // TODO is this a performance issue? maybe we should try the operation without
- // resorting to BigInt first.
- var lhs_space: Value.BigIntSpace = undefined;
- var rhs_space: Value.BigIntSpace = undefined;
- const lhs_bigint = lhs.toBigInt(&lhs_space);
- const rhs_bigint = rhs.toBigInt(&rhs_space);
- const limbs = try allocator.alloc(
- std.math.big.Limb,
- std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
- );
- var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
- result_bigint.add(lhs_bigint, rhs_bigint);
- const result_limbs = result_bigint.limbs[0..result_bigint.len];
-
- const val_payload = if (result_bigint.positive) blk: {
- const val_payload = try allocator.create(Value.Payload.IntBigPositive);
- val_payload.* = .{ .limbs = result_limbs };
- break :blk &val_payload.base;
- } else blk: {
- const val_payload = try allocator.create(Value.Payload.IntBigNegative);
- val_payload.* = .{ .limbs = result_limbs };
- break :blk &val_payload.base;
- };
-
- return Value.initPayload(val_payload);
-}
-
-pub fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
- // TODO is this a performance issue? maybe we should try the operation without
- // resorting to BigInt first.
- var lhs_space: Value.BigIntSpace = undefined;
- var rhs_space: Value.BigIntSpace = undefined;
- const lhs_bigint = lhs.toBigInt(&lhs_space);
- const rhs_bigint = rhs.toBigInt(&rhs_space);
- const limbs = try allocator.alloc(
- std.math.big.Limb,
- std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
- );
- var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
- result_bigint.sub(lhs_bigint, rhs_bigint);
- const result_limbs = result_bigint.limbs[0..result_bigint.len];
-
- const val_payload = if (result_bigint.positive) blk: {
- const val_payload = try allocator.create(Value.Payload.IntBigPositive);
- val_payload.* = .{ .limbs = result_limbs };
- break :blk &val_payload.base;
- } else blk: {
- const val_payload = try allocator.create(Value.Payload.IntBigNegative);
- val_payload.* = .{ .limbs = result_limbs };
- break :blk &val_payload.base;
- };
-
- return Value.initPayload(val_payload);
-}
-
-pub fn floatAdd(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs: Value, rhs: Value) !Value {
- var bit_count = switch (float_type.tag()) {
- .comptime_float => 128,
- else => float_type.floatBits(self.getTarget()),
- };
-
- const allocator = scope.arena();
- const val_payload = switch (bit_count) {
- 16 => {
- return self.fail(scope, src, "TODO Implement addition for soft floats", .{});
- },
- 32 => blk: {
- const lhs_val = lhs.toFloat(f32);
- const rhs_val = rhs.toFloat(f32);
- const val_payload = try allocator.create(Value.Payload.Float_32);
- val_payload.* = .{ .val = lhs_val + rhs_val };
- break :blk &val_payload.base;
- },
- 64 => blk: {
- const lhs_val = lhs.toFloat(f64);
- const rhs_val = rhs.toFloat(f64);
- const val_payload = try allocator.create(Value.Payload.Float_64);
- val_payload.* = .{ .val = lhs_val + rhs_val };
- break :blk &val_payload.base;
- },
- 128 => {
- return self.fail(scope, src, "TODO Implement addition for big floats", .{});
- },
- else => unreachable,
- };
-
- return Value.initPayload(val_payload);
-}
-
-pub fn floatSub(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs: Value, rhs: Value) !Value {
- var bit_count = switch (float_type.tag()) {
- .comptime_float => 128,
- else => float_type.floatBits(self.getTarget()),
- };
-
- const allocator = scope.arena();
- const val_payload = switch (bit_count) {
- 16 => {
- return self.fail(scope, src, "TODO Implement substraction for soft floats", .{});
- },
- 32 => blk: {
- const lhs_val = lhs.toFloat(f32);
- const rhs_val = rhs.toFloat(f32);
- const val_payload = try allocator.create(Value.Payload.Float_32);
- val_payload.* = .{ .val = lhs_val - rhs_val };
- break :blk &val_payload.base;
- },
- 64 => blk: {
- const lhs_val = lhs.toFloat(f64);
- const rhs_val = rhs.toFloat(f64);
- const val_payload = try allocator.create(Value.Payload.Float_64);
- val_payload.* = .{ .val = lhs_val - rhs_val };
- break :blk &val_payload.base;
- },
- 128 => {
- return self.fail(scope, src, "TODO Implement substraction for big floats", .{});
- },
- else => unreachable,
- };
-
- return Value.initPayload(val_payload);
-}
-
-pub fn simplePtrType(self: *Module, scope: *Scope, src: usize, elem_ty: Type, mutable: bool, size: std.builtin.TypeInfo.Pointer.Size) Allocator.Error!Type {
- if (!mutable and size == .Slice and elem_ty.eql(Type.initTag(.u8))) {
- return Type.initTag(.const_slice_u8);
- }
- // TODO stage1 type inference bug
- const T = Type.Tag;
-
- const type_payload = try scope.arena().create(Type.Payload.PointerSimple);
- type_payload.* = .{
- .base = .{
- .tag = switch (size) {
- .One => if (mutable) T.single_mut_pointer else T.single_const_pointer,
- .Many => if (mutable) T.many_mut_pointer else T.many_const_pointer,
- .C => if (mutable) T.c_mut_pointer else T.c_const_pointer,
- .Slice => if (mutable) T.mut_slice else T.const_slice,
- },
- },
- .pointee_type = elem_ty,
- };
- return Type.initPayload(&type_payload.base);
-}
-
-pub fn ptrType(
- self: *Module,
- scope: *Scope,
- src: usize,
- elem_ty: Type,
- sentinel: ?Value,
- @"align": u32,
- bit_offset: u16,
- host_size: u16,
- mutable: bool,
- @"allowzero": bool,
- @"volatile": bool,
- size: std.builtin.TypeInfo.Pointer.Size,
-) Allocator.Error!Type {
- assert(host_size == 0 or bit_offset < host_size * 8);
-
- // TODO check if type can be represented by simplePtrType
- const type_payload = try scope.arena().create(Type.Payload.Pointer);
- type_payload.* = .{
- .pointee_type = elem_ty,
- .sentinel = sentinel,
- .@"align" = @"align",
- .bit_offset = bit_offset,
- .host_size = host_size,
- .@"allowzero" = @"allowzero",
- .mutable = mutable,
- .@"volatile" = @"volatile",
- .size = size,
- };
- return Type.initPayload(&type_payload.base);
-}
-
-pub fn optionalType(self: *Module, scope: *Scope, child_type: Type) Allocator.Error!Type {
- return Type.initPayload(switch (child_type.tag()) {
- .single_const_pointer => blk: {
- const payload = try scope.arena().create(Type.Payload.PointerSimple);
- payload.* = .{
- .base = .{ .tag = .optional_single_const_pointer },
- .pointee_type = child_type.elemType(),
- };
- break :blk &payload.base;
- },
- .single_mut_pointer => blk: {
- const payload = try scope.arena().create(Type.Payload.PointerSimple);
- payload.* = .{
- .base = .{ .tag = .optional_single_mut_pointer },
- .pointee_type = child_type.elemType(),
- };
- break :blk &payload.base;
- },
- else => blk: {
- const payload = try scope.arena().create(Type.Payload.Optional);
- payload.* = .{
- .child_type = child_type,
- };
- break :blk &payload.base;
- },
- });
-}
-
-pub fn arrayType(self: *Module, scope: *Scope, len: u64, sentinel: ?Value, elem_type: Type) Allocator.Error!Type {
- if (elem_type.eql(Type.initTag(.u8))) {
- if (sentinel) |some| {
- if (some.eql(Value.initTag(.zero))) {
- const payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0);
- payload.* = .{
- .len = len,
- };
- return Type.initPayload(&payload.base);
- }
- } else {
- const payload = try scope.arena().create(Type.Payload.Array_u8);
- payload.* = .{
- .len = len,
- };
- return Type.initPayload(&payload.base);
- }
- }
-
- if (sentinel) |some| {
- const payload = try scope.arena().create(Type.Payload.ArraySentinel);
- payload.* = .{
- .len = len,
- .sentinel = some,
- .elem_type = elem_type,
- };
- return Type.initPayload(&payload.base);
- }
-
- const payload = try scope.arena().create(Type.Payload.Array);
- payload.* = .{
- .len = len,
- .elem_type = elem_type,
- };
- return Type.initPayload(&payload.base);
-}
-
-pub fn errorUnionType(self: *Module, scope: *Scope, error_set: Type, payload: Type) Allocator.Error!Type {
- assert(error_set.zigTypeTag() == .ErrorSet);
- if (error_set.eql(Type.initTag(.anyerror)) and payload.eql(Type.initTag(.void))) {
- return Type.initTag(.anyerror_void_error_union);
- }
-
- const result = try scope.arena().create(Type.Payload.ErrorUnion);
- result.* = .{
- .error_set = error_set,
- .payload = payload,
- };
- return Type.initPayload(&result.base);
-}
-
-pub fn anyframeType(self: *Module, scope: *Scope, return_type: Type) Allocator.Error!Type {
- const result = try scope.arena().create(Type.Payload.AnyFrame);
- result.* = .{
- .return_type = return_type,
- };
- return Type.initPayload(&result.base);
-}
-
-pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {
- const zir_module = scope.namespace();
- const source = zir_module.getSource(self) catch @panic("dumpInst failed to get source");
- const loc = std.zig.findLineColumn(source, inst.src);
- if (inst.tag == .constant) {
- std.debug.print("constant ty={} val={} src={}:{}:{}\n", .{
- inst.ty,
- inst.castTag(.constant).?.val,
- zir_module.subFilePath(),
- loc.line + 1,
- loc.column + 1,
- });
- } else if (inst.deaths == 0) {
- std.debug.print("{} ty={} src={}:{}:{}\n", .{
- @tagName(inst.tag),
- inst.ty,
- zir_module.subFilePath(),
- loc.line + 1,
- loc.column + 1,
- });
- } else {
- std.debug.print("{} ty={} deaths={b} src={}:{}:{}\n", .{
- @tagName(inst.tag),
- inst.ty,
- inst.deaths,
- zir_module.subFilePath(),
- loc.line + 1,
- loc.column + 1,
- });
- }
-}
-
-pub const PanicId = enum {
- unreach,
- unwrap_null,
-};
-
-pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic_id: PanicId) !void {
- const block_inst = try parent_block.arena.create(Inst.Block);
- block_inst.* = .{
- .base = .{
- .tag = Inst.Block.base_tag,
- .ty = Type.initTag(.void),
- .src = ok.src,
- },
- .body = .{
- .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the condbr.
- },
- };
-
- const ok_body: ir.Body = .{
- .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the brvoid.
- };
- const brvoid = try parent_block.arena.create(Inst.BrVoid);
- brvoid.* = .{
- .base = .{
- .tag = .brvoid,
- .ty = Type.initTag(.noreturn),
- .src = ok.src,
- },
- .block = block_inst,
- };
- ok_body.instructions[0] = &brvoid.base;
-
- var fail_block: Scope.Block = .{
- .parent = parent_block,
- .func = parent_block.func,
- .decl = parent_block.decl,
- .instructions = .{},
- .arena = parent_block.arena,
- .is_comptime = parent_block.is_comptime,
- };
- defer fail_block.instructions.deinit(mod.gpa);
-
- _ = try mod.safetyPanic(&fail_block, ok.src, panic_id);
-
- const fail_body: ir.Body = .{ .instructions = try parent_block.arena.dupe(*Inst, fail_block.instructions.items) };
-
- const condbr = try parent_block.arena.create(Inst.CondBr);
- condbr.* = .{
- .base = .{
- .tag = .condbr,
- .ty = Type.initTag(.noreturn),
- .src = ok.src,
- },
- .condition = ok,
- .then_body = ok_body,
- .else_body = fail_body,
- };
- block_inst.body.instructions[0] = &condbr.base;
-
- try parent_block.instructions.append(mod.gpa, &block_inst.base);
-}
-
-pub fn safetyPanic(mod: *Module, block: *Scope.Block, src: usize, panic_id: PanicId) !*Inst {
- // TODO Once we have a panic function to call, call it here instead of breakpoint.
- _ = try mod.addNoOp(block, src, Type.initTag(.void), .breakpoint);
- return mod.addNoOp(block, src, Type.initTag(.noreturn), .unreach);
-}
-
-pub fn getTarget(self: Module) Target {
- return self.comp.bin_file.options.target;
-}
-
-pub fn optimizeMode(self: Module) std.builtin.Mode {
- return self.comp.bin_file.options.optimize_mode;
-}
diff --git a/src-self-hosted/Package.zig b/src-self-hosted/Package.zig
deleted file mode 100644
index 14be8b64d6fb1905be106a17ce83cd1749fc076d..0000000000000000000000000000000000000000
--- a/src-self-hosted/Package.zig
+++ /dev/null
@@ -1,61 +0,0 @@
-pub const Table = std.StringHashMapUnmanaged(*Package);
-
-root_src_directory: Compilation.Directory,
-/// Relative to `root_src_directory`. May contain path separators.
-root_src_path: []const u8,
-table: Table = .{},
-
-const std = @import("std");
-const mem = std.mem;
-const Allocator = std.mem.Allocator;
-const assert = std.debug.assert;
-const Package = @This();
-const Compilation = @import("Compilation.zig");
-
-/// No references to `root_src_dir` and `root_src_path` are kept.
-pub fn create(
- gpa: *Allocator,
- base_directory: Compilation.Directory,
- /// Relative to `base_directory`.
- root_src_dir: []const u8,
- /// Relative to `root_src_dir`.
- root_src_path: []const u8,
-) !*Package {
- const ptr = try gpa.create(Package);
- errdefer gpa.destroy(ptr);
-
- const root_src_dir_path = try base_directory.join(gpa, &[_][]const u8{root_src_dir});
- errdefer gpa.free(root_src_dir_path);
-
- const root_src_path_dupe = try mem.dupe(gpa, u8, root_src_path);
- errdefer gpa.free(root_src_path_dupe);
-
- ptr.* = .{
- .root_src_directory = .{
- .path = root_src_dir_path,
- .handle = try base_directory.handle.openDir(root_src_dir, .{}),
- },
- .root_src_path = root_src_path_dupe,
- };
- return ptr;
-}
-
-pub fn destroy(pkg: *Package, gpa: *Allocator) void {
- pkg.root_src_directory.handle.close();
- gpa.free(pkg.root_src_path);
- if (pkg.root_src_directory.path) |p| gpa.free(p);
- {
- var it = pkg.table.iterator();
- while (it.next()) |kv| {
- gpa.free(kv.key);
- }
- }
- pkg.table.deinit(gpa);
- gpa.destroy(pkg);
-}
-
-pub fn add(pkg: *Package, gpa: *Allocator, name: []const u8, package: *Package) !void {
- try pkg.table.ensureCapacity(gpa, pkg.table.items().len + 1);
- const name_dupe = try mem.dupe(gpa, u8, name);
- pkg.table.putAssumeCapacityNoClobber(name_dupe, package);
-}
diff --git a/src-self-hosted/TypedValue.zig b/src-self-hosted/TypedValue.zig
deleted file mode 100644
index 48b2c04970d15593a420f73d40967f003cbec9d9..0000000000000000000000000000000000000000
--- a/src-self-hosted/TypedValue.zig
+++ /dev/null
@@ -1,31 +0,0 @@
-const std = @import("std");
-const Type = @import("type.zig").Type;
-const Value = @import("value.zig").Value;
-const Allocator = std.mem.Allocator;
-const TypedValue = @This();
-
-ty: Type,
-val: Value,
-
-/// Memory management for TypedValue. The main purpose of this type
-/// is to be small and have a deinit() function to free associated resources.
-pub const Managed = struct {
- /// If the tag value is less than Tag.no_payload_count, then no pointer
- /// dereference is needed.
- typed_value: TypedValue,
- /// If this is `null` then there is no memory management needed.
- arena: ?*std.heap.ArenaAllocator.State = null,
-
- pub fn deinit(self: *Managed, allocator: *Allocator) void {
- if (self.arena) |a| a.promote(allocator).deinit();
- self.* = undefined;
- }
-};
-
-/// Assumes arena allocation. Does a recursive copy.
-pub fn copy(self: TypedValue, allocator: *Allocator) error{OutOfMemory}!TypedValue {
- return TypedValue{
- .ty = try self.ty.copy(allocator),
- .val = try self.val.copy(allocator),
- };
-}
diff --git a/src-self-hosted/astgen.zig b/src-self-hosted/astgen.zig
deleted file mode 100644
index 2c091a86eccd3cc157cb6fcbb8c2dce3e7473fd0..0000000000000000000000000000000000000000
--- a/src-self-hosted/astgen.zig
+++ /dev/null
@@ -1,2396 +0,0 @@
-const std = @import("std");
-const mem = std.mem;
-const Allocator = std.mem.Allocator;
-const Value = @import("value.zig").Value;
-const Type = @import("type.zig").Type;
-const TypedValue = @import("TypedValue.zig");
-const assert = std.debug.assert;
-const zir = @import("zir.zig");
-const Module = @import("Module.zig");
-const ast = std.zig.ast;
-const trace = @import("tracy.zig").trace;
-const Scope = Module.Scope;
-const InnerError = Module.InnerError;
-
-pub const ResultLoc = union(enum) {
- /// The expression is the right-hand side of assignment to `_`. Only the side-effects of the
- /// expression should be generated.
- discard,
- /// The expression has an inferred type, and it will be evaluated as an rvalue.
- none,
- /// The expression must generate a pointer rather than a value. For example, the left hand side
- /// of an assignment uses this kind of result location.
- ref,
- /// The expression will be type coerced into this type, but it will be evaluated as an rvalue.
- ty: *zir.Inst,
- /// The expression must store its result into this typed pointer.
- ptr: *zir.Inst,
- /// The expression must store its result into this allocation, which has an inferred type.
- inferred_ptr: *zir.Inst.Tag.alloc_inferred.Type(),
- /// The expression must store its result into this pointer, which is a typed pointer that
- /// has been bitcasted to whatever the expression's type is.
- bitcasted_ptr: *zir.Inst.UnOp,
- /// There is a pointer for the expression to store its result into, however, its type
- /// is inferred based on peer type resolution for a `zir.Inst.Block`.
- block_ptr: *zir.Inst.Block,
-};
-
-pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*zir.Inst {
- const type_src = scope.tree().token_locs[type_node.firstToken()].start;
- const type_type = try addZIRInstConst(mod, scope, type_src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.type_type),
- });
- const type_rl: ResultLoc = .{ .ty = type_type };
- return expr(mod, scope, type_rl, type_node);
-}
-
-fn lvalExpr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst {
- switch (node.tag) {
- .Root => unreachable,
- .Use => unreachable,
- .TestDecl => unreachable,
- .DocComment => unreachable,
- .VarDecl => unreachable,
- .SwitchCase => unreachable,
- .SwitchElse => unreachable,
- .Else => unreachable,
- .Payload => unreachable,
- .PointerPayload => unreachable,
- .PointerIndexPayload => unreachable,
- .ErrorTag => unreachable,
- .FieldInitializer => unreachable,
- .ContainerField => unreachable,
-
- .Assign,
- .AssignBitAnd,
- .AssignBitOr,
- .AssignBitShiftLeft,
- .AssignBitShiftRight,
- .AssignBitXor,
- .AssignDiv,
- .AssignSub,
- .AssignSubWrap,
- .AssignMod,
- .AssignAdd,
- .AssignAddWrap,
- .AssignMul,
- .AssignMulWrap,
- .Add,
- .AddWrap,
- .Sub,
- .SubWrap,
- .Mul,
- .MulWrap,
- .Div,
- .Mod,
- .BitAnd,
- .BitOr,
- .BitShiftLeft,
- .BitShiftRight,
- .BitXor,
- .BangEqual,
- .EqualEqual,
- .GreaterThan,
- .GreaterOrEqual,
- .LessThan,
- .LessOrEqual,
- .ArrayCat,
- .ArrayMult,
- .BoolAnd,
- .BoolOr,
- .Asm,
- .StringLiteral,
- .IntegerLiteral,
- .Call,
- .Unreachable,
- .Return,
- .If,
- .While,
- .BoolNot,
- .AddressOf,
- .FloatLiteral,
- .UndefinedLiteral,
- .BoolLiteral,
- .NullLiteral,
- .OptionalType,
- .Block,
- .LabeledBlock,
- .Break,
- .PtrType,
- .GroupedExpression,
- .ArrayType,
- .ArrayTypeSentinel,
- .EnumLiteral,
- .MultilineStringLiteral,
- .CharLiteral,
- .Defer,
- .Catch,
- .ErrorUnion,
- .MergeErrorSets,
- .Range,
- .OrElse,
- .Await,
- .BitNot,
- .Negation,
- .NegationWrap,
- .Resume,
- .Try,
- .SliceType,
- .Slice,
- .ArrayInitializer,
- .ArrayInitializerDot,
- .StructInitializer,
- .StructInitializerDot,
- .Switch,
- .For,
- .Suspend,
- .Continue,
- .AnyType,
- .ErrorType,
- .FnProto,
- .AnyFrameType,
- .ErrorSetDecl,
- .ContainerDecl,
- .Comptime,
- .Nosuspend,
- => return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}),
-
- // @field can be assigned to
- .BuiltinCall => {
- const call = node.castTag(.BuiltinCall).?;
- const tree = scope.tree();
- const builtin_name = tree.tokenSlice(call.builtin_token);
-
- if (!mem.eql(u8, builtin_name, "@field")) {
- return mod.failNode(scope, node, "invalid left-hand side to assignment", .{});
- }
- },
-
- // can be assigned to
- .UnwrapOptional, .Deref, .Period, .ArrayAccess, .Identifier => {},
- }
- return expr(mod, scope, .ref, node);
-}
-
-/// Turn Zig AST into untyped ZIR istructions.
-pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerError!*zir.Inst {
- switch (node.tag) {
- .Root => unreachable, // Top-level declaration.
- .Use => unreachable, // Top-level declaration.
- .TestDecl => unreachable, // Top-level declaration.
- .DocComment => unreachable, // Top-level declaration.
- .VarDecl => unreachable, // Handled in `blockExpr`.
- .SwitchCase => unreachable, // Handled in `switchExpr`.
- .SwitchElse => unreachable, // Handled in `switchExpr`.
- .Else => unreachable, // Handled explicitly the control flow expression functions.
- .Payload => unreachable, // Handled explicitly.
- .PointerPayload => unreachable, // Handled explicitly.
- .PointerIndexPayload => unreachable, // Handled explicitly.
- .ErrorTag => unreachable, // Handled explicitly.
- .FieldInitializer => unreachable, // Handled explicitly.
- .ContainerField => unreachable, // Handled explicitly.
-
- .Assign => return rlWrapVoid(mod, scope, rl, node, try assign(mod, scope, node.castTag(.Assign).?)),
- .AssignBitAnd => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitAnd).?, .bitand)),
- .AssignBitOr => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitOr).?, .bitor)),
- .AssignBitShiftLeft => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitShiftLeft).?, .shl)),
- .AssignBitShiftRight => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitShiftRight).?, .shr)),
- .AssignBitXor => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitXor).?, .xor)),
- .AssignDiv => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignDiv).?, .div)),
- .AssignSub => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignSub).?, .sub)),
- .AssignSubWrap => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignSubWrap).?, .subwrap)),
- .AssignMod => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMod).?, .mod_rem)),
- .AssignAdd => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignAdd).?, .add)),
- .AssignAddWrap => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignAddWrap).?, .addwrap)),
- .AssignMul => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMul).?, .mul)),
- .AssignMulWrap => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMulWrap).?, .mulwrap)),
-
- .Add => return simpleBinOp(mod, scope, rl, node.castTag(.Add).?, .add),
- .AddWrap => return simpleBinOp(mod, scope, rl, node.castTag(.AddWrap).?, .addwrap),
- .Sub => return simpleBinOp(mod, scope, rl, node.castTag(.Sub).?, .sub),
- .SubWrap => return simpleBinOp(mod, scope, rl, node.castTag(.SubWrap).?, .subwrap),
- .Mul => return simpleBinOp(mod, scope, rl, node.castTag(.Mul).?, .mul),
- .MulWrap => return simpleBinOp(mod, scope, rl, node.castTag(.MulWrap).?, .mulwrap),
- .Div => return simpleBinOp(mod, scope, rl, node.castTag(.Div).?, .div),
- .Mod => return simpleBinOp(mod, scope, rl, node.castTag(.Mod).?, .mod_rem),
- .BitAnd => return simpleBinOp(mod, scope, rl, node.castTag(.BitAnd).?, .bitand),
- .BitOr => return simpleBinOp(mod, scope, rl, node.castTag(.BitOr).?, .bitor),
- .BitShiftLeft => return simpleBinOp(mod, scope, rl, node.castTag(.BitShiftLeft).?, .shl),
- .BitShiftRight => return simpleBinOp(mod, scope, rl, node.castTag(.BitShiftRight).?, .shr),
- .BitXor => return simpleBinOp(mod, scope, rl, node.castTag(.BitXor).?, .xor),
-
- .BangEqual => return simpleBinOp(mod, scope, rl, node.castTag(.BangEqual).?, .cmp_neq),
- .EqualEqual => return simpleBinOp(mod, scope, rl, node.castTag(.EqualEqual).?, .cmp_eq),
- .GreaterThan => return simpleBinOp(mod, scope, rl, node.castTag(.GreaterThan).?, .cmp_gt),
- .GreaterOrEqual => return simpleBinOp(mod, scope, rl, node.castTag(.GreaterOrEqual).?, .cmp_gte),
- .LessThan => return simpleBinOp(mod, scope, rl, node.castTag(.LessThan).?, .cmp_lt),
- .LessOrEqual => return simpleBinOp(mod, scope, rl, node.castTag(.LessOrEqual).?, .cmp_lte),
-
- .ArrayCat => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayCat).?, .array_cat),
- .ArrayMult => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayMult).?, .array_mul),
-
- .BoolAnd => return boolBinOp(mod, scope, rl, node.castTag(.BoolAnd).?),
- .BoolOr => return boolBinOp(mod, scope, rl, node.castTag(.BoolOr).?),
-
- .BoolNot => return rlWrap(mod, scope, rl, try boolNot(mod, scope, node.castTag(.BoolNot).?)),
- .BitNot => return rlWrap(mod, scope, rl, try bitNot(mod, scope, node.castTag(.BitNot).?)),
- .Negation => return rlWrap(mod, scope, rl, try negation(mod, scope, node.castTag(.Negation).?, .sub)),
- .NegationWrap => return rlWrap(mod, scope, rl, try negation(mod, scope, node.castTag(.NegationWrap).?, .subwrap)),
-
- .Identifier => return try identifier(mod, scope, rl, node.castTag(.Identifier).?),
- .Asm => return rlWrap(mod, scope, rl, try assembly(mod, scope, node.castTag(.Asm).?)),
- .StringLiteral => return rlWrap(mod, scope, rl, try stringLiteral(mod, scope, node.castTag(.StringLiteral).?)),
- .IntegerLiteral => return rlWrap(mod, scope, rl, try integerLiteral(mod, scope, node.castTag(.IntegerLiteral).?)),
- .BuiltinCall => return builtinCall(mod, scope, rl, node.castTag(.BuiltinCall).?),
- .Call => return callExpr(mod, scope, rl, node.castTag(.Call).?),
- .Unreachable => return unreach(mod, scope, node.castTag(.Unreachable).?),
- .Return => return ret(mod, scope, node.castTag(.Return).?),
- .If => return ifExpr(mod, scope, rl, node.castTag(.If).?),
- .While => return whileExpr(mod, scope, rl, node.castTag(.While).?),
- .Period => return field(mod, scope, rl, node.castTag(.Period).?),
- .Deref => return rlWrap(mod, scope, rl, try deref(mod, scope, node.castTag(.Deref).?)),
- .AddressOf => return rlWrap(mod, scope, rl, try addressOf(mod, scope, node.castTag(.AddressOf).?)),
- .FloatLiteral => return rlWrap(mod, scope, rl, try floatLiteral(mod, scope, node.castTag(.FloatLiteral).?)),
- .UndefinedLiteral => return rlWrap(mod, scope, rl, try undefLiteral(mod, scope, node.castTag(.UndefinedLiteral).?)),
- .BoolLiteral => return rlWrap(mod, scope, rl, try boolLiteral(mod, scope, node.castTag(.BoolLiteral).?)),
- .NullLiteral => return rlWrap(mod, scope, rl, try nullLiteral(mod, scope, node.castTag(.NullLiteral).?)),
- .OptionalType => return rlWrap(mod, scope, rl, try optionalType(mod, scope, node.castTag(.OptionalType).?)),
- .UnwrapOptional => return unwrapOptional(mod, scope, rl, node.castTag(.UnwrapOptional).?),
- .Block => return rlWrapVoid(mod, scope, rl, node, try blockExpr(mod, scope, node.castTag(.Block).?)),
- .LabeledBlock => return labeledBlockExpr(mod, scope, rl, node.castTag(.LabeledBlock).?, .block),
- .Break => return rlWrap(mod, scope, rl, try breakExpr(mod, scope, node.castTag(.Break).?)),
- .PtrType => return rlWrap(mod, scope, rl, try ptrType(mod, scope, node.castTag(.PtrType).?)),
- .GroupedExpression => return expr(mod, scope, rl, node.castTag(.GroupedExpression).?.expr),
- .ArrayType => return rlWrap(mod, scope, rl, try arrayType(mod, scope, node.castTag(.ArrayType).?)),
- .ArrayTypeSentinel => return rlWrap(mod, scope, rl, try arrayTypeSentinel(mod, scope, node.castTag(.ArrayTypeSentinel).?)),
- .EnumLiteral => return rlWrap(mod, scope, rl, try enumLiteral(mod, scope, node.castTag(.EnumLiteral).?)),
- .MultilineStringLiteral => return rlWrap(mod, scope, rl, try multilineStrLiteral(mod, scope, node.castTag(.MultilineStringLiteral).?)),
- .CharLiteral => return rlWrap(mod, scope, rl, try charLiteral(mod, scope, node.castTag(.CharLiteral).?)),
- .SliceType => return rlWrap(mod, scope, rl, try sliceType(mod, scope, node.castTag(.SliceType).?)),
- .ErrorUnion => return rlWrap(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.ErrorUnion).?, .error_union_type)),
- .MergeErrorSets => return rlWrap(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.MergeErrorSets).?, .merge_error_sets)),
- .AnyFrameType => return rlWrap(mod, scope, rl, try anyFrameType(mod, scope, node.castTag(.AnyFrameType).?)),
- .ErrorSetDecl => return errorSetDecl(mod, scope, rl, node.castTag(.ErrorSetDecl).?),
- .ErrorType => return rlWrap(mod, scope, rl, try errorType(mod, scope, node.castTag(.ErrorType).?)),
- .For => return forExpr(mod, scope, rl, node.castTag(.For).?),
- .ArrayAccess => return arrayAccess(mod, scope, rl, node.castTag(.ArrayAccess).?),
- .Slice => return rlWrap(mod, scope, rl, try sliceExpr(mod, scope, node.castTag(.Slice).?)),
- .Catch => return catchExpr(mod, scope, rl, node.castTag(.Catch).?),
- .Comptime => return comptimeKeyword(mod, scope, rl, node.castTag(.Comptime).?),
- .OrElse => return orelseExpr(mod, scope, rl, node.castTag(.OrElse).?),
-
- .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}),
- .Range => return mod.failNode(scope, node, "TODO implement astgen.expr for .Range", .{}),
- .Await => return mod.failNode(scope, node, "TODO implement astgen.expr for .Await", .{}),
- .Resume => return mod.failNode(scope, node, "TODO implement astgen.expr for .Resume", .{}),
- .Try => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}),
- .ArrayInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializer", .{}),
- .ArrayInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializerDot", .{}),
- .StructInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializer", .{}),
- .StructInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializerDot", .{}),
- .Switch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Switch", .{}),
- .Suspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Suspend", .{}),
- .Continue => return mod.failNode(scope, node, "TODO implement astgen.expr for .Continue", .{}),
- .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}),
- .FnProto => return mod.failNode(scope, node, "TODO implement astgen.expr for .FnProto", .{}),
- .ContainerDecl => return mod.failNode(scope, node, "TODO implement astgen.expr for .ContainerDecl", .{}),
- .Nosuspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Nosuspend", .{}),
- }
-}
-
-fn comptimeKeyword(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Comptime) InnerError!*zir.Inst {
- const tracy = trace(@src());
- defer tracy.end();
-
- return comptimeExpr(mod, scope, rl, node.expr);
-}
-
-pub fn comptimeExpr(mod: *Module, parent_scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerError!*zir.Inst {
- const tree = parent_scope.tree();
- const src = tree.token_locs[node.firstToken()].start;
-
- // Optimization for labeled blocks: don't need to have 2 layers of blocks, we can reuse the existing one.
- if (node.castTag(.LabeledBlock)) |block_node| {
- return labeledBlockExpr(mod, parent_scope, rl, block_node, .block_comptime);
- }
-
- // Make a scope to collect generated instructions in the sub-expression.
- var block_scope: Scope.GenZIR = .{
- .parent = parent_scope,
- .decl = parent_scope.decl().?,
- .arena = parent_scope.arena(),
- .instructions = .{},
- };
- defer block_scope.instructions.deinit(mod.gpa);
-
- // No need to capture the result here because block_comptime_flat implies that the final
- // instruction is the block's result value.
- _ = try expr(mod, &block_scope.base, rl, node);
-
- const block = try addZIRInstBlock(mod, parent_scope, src, .block_comptime_flat, .{
- .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
- });
-
- return &block.base;
-}
-
-fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {
- const tree = parent_scope.tree();
- const src = tree.token_locs[node.ltoken].start;
-
- if (node.getLabel()) |break_label| {
- // Look for the label in the scope.
- var scope = parent_scope;
- while (true) {
- switch (scope.tag) {
- .gen_zir => {
- const gen_zir = scope.cast(Scope.GenZIR).?;
- if (gen_zir.label) |label| {
- if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) {
- if (node.getRHS()) |rhs| {
- // Most result location types can be forwarded directly; however
- // if we need to write to a pointer which has an inferred type,
- // proper type inference requires peer type resolution on the block's
- // break operand expressions.
- const branch_rl: ResultLoc = switch (label.result_loc) {
- .discard, .none, .ty, .ptr, .ref => label.result_loc,
- .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = label.block_inst },
- };
- const operand = try expr(mod, parent_scope, branch_rl, rhs);
- return try addZIRInst(mod, scope, src, zir.Inst.Break, .{
- .block = label.block_inst,
- .operand = operand,
- }, .{});
- } else {
- return try addZIRInst(mod, scope, src, zir.Inst.BreakVoid, .{
- .block = label.block_inst,
- }, .{});
- }
- }
- }
- scope = gen_zir.parent;
- },
- .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
- .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
- else => {
- const label_name = try identifierTokenString(mod, parent_scope, break_label);
- return mod.failTok(parent_scope, break_label, "label not found: '{}'", .{label_name});
- },
- }
- }
- } else {
- return mod.failNode(parent_scope, &node.base, "TODO implement break from loop", .{});
- }
-}
-
-pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block) InnerError!void {
- const tracy = trace(@src());
- defer tracy.end();
-
- try blockExprStmts(mod, parent_scope, &block_node.base, block_node.statements());
-}
-
-fn labeledBlockExpr(
- mod: *Module,
- parent_scope: *Scope,
- rl: ResultLoc,
- block_node: *ast.Node.LabeledBlock,
- zir_tag: zir.Inst.Tag,
-) InnerError!*zir.Inst {
- const tracy = trace(@src());
- defer tracy.end();
-
- assert(zir_tag == .block or zir_tag == .block_comptime);
-
- const tree = parent_scope.tree();
- const src = tree.token_locs[block_node.lbrace].start;
-
- // Create the Block ZIR instruction so that we can put it into the GenZIR struct
- // so that break statements can reference it.
- const gen_zir = parent_scope.getGenZIR();
- const block_inst = try gen_zir.arena.create(zir.Inst.Block);
- block_inst.* = .{
- .base = .{
- .tag = zir_tag,
- .src = src,
- },
- .positionals = .{
- .body = .{ .instructions = undefined },
- },
- .kw_args = .{},
- };
-
- var block_scope: Scope.GenZIR = .{
- .parent = parent_scope,
- .decl = parent_scope.decl().?,
- .arena = gen_zir.arena,
- .instructions = .{},
- // TODO @as here is working around a stage1 miscompilation bug :(
- .label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{
- .token = block_node.label,
- .block_inst = block_inst,
- .result_loc = rl,
- }),
- };
- defer block_scope.instructions.deinit(mod.gpa);
-
- try blockExprStmts(mod, &block_scope.base, &block_node.base, block_node.statements());
-
- block_inst.positionals.body.instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items);
- try gen_zir.instructions.append(mod.gpa, &block_inst.base);
-
- return &block_inst.base;
-}
-
-fn blockExprStmts(mod: *Module, parent_scope: *Scope, node: *ast.Node, statements: []*ast.Node) !void {
- const tree = parent_scope.tree();
-
- var block_arena = std.heap.ArenaAllocator.init(mod.gpa);
- defer block_arena.deinit();
-
- var scope = parent_scope;
- for (statements) |statement| {
- const src = tree.token_locs[statement.firstToken()].start;
- _ = try addZIRNoOp(mod, scope, src, .dbg_stmt);
- switch (statement.tag) {
- .VarDecl => {
- const var_decl_node = statement.castTag(.VarDecl).?;
- scope = try varDecl(mod, scope, var_decl_node, &block_arena.allocator);
- },
- .Assign => try assign(mod, scope, statement.castTag(.Assign).?),
- .AssignBitAnd => try assignOp(mod, scope, statement.castTag(.AssignBitAnd).?, .bitand),
- .AssignBitOr => try assignOp(mod, scope, statement.castTag(.AssignBitOr).?, .bitor),
- .AssignBitShiftLeft => try assignOp(mod, scope, statement.castTag(.AssignBitShiftLeft).?, .shl),
- .AssignBitShiftRight => try assignOp(mod, scope, statement.castTag(.AssignBitShiftRight).?, .shr),
- .AssignBitXor => try assignOp(mod, scope, statement.castTag(.AssignBitXor).?, .xor),
- .AssignDiv => try assignOp(mod, scope, statement.castTag(.AssignDiv).?, .div),
- .AssignSub => try assignOp(mod, scope, statement.castTag(.AssignSub).?, .sub),
- .AssignSubWrap => try assignOp(mod, scope, statement.castTag(.AssignSubWrap).?, .subwrap),
- .AssignMod => try assignOp(mod, scope, statement.castTag(.AssignMod).?, .mod_rem),
- .AssignAdd => try assignOp(mod, scope, statement.castTag(.AssignAdd).?, .add),
- .AssignAddWrap => try assignOp(mod, scope, statement.castTag(.AssignAddWrap).?, .addwrap),
- .AssignMul => try assignOp(mod, scope, statement.castTag(.AssignMul).?, .mul),
- .AssignMulWrap => try assignOp(mod, scope, statement.castTag(.AssignMulWrap).?, .mulwrap),
-
- else => {
- const possibly_unused_result = try expr(mod, scope, .none, statement);
- if (!possibly_unused_result.tag.isNoReturn()) {
- _ = try addZIRUnOp(mod, scope, src, .ensure_result_used, possibly_unused_result);
- }
- },
- }
- }
-}
-
-fn varDecl(
- mod: *Module,
- scope: *Scope,
- node: *ast.Node.VarDecl,
- block_arena: *Allocator,
-) InnerError!*Scope {
- // TODO implement detection of shadowing
- if (node.getComptimeToken()) |comptime_token| {
- return mod.failTok(scope, comptime_token, "TODO implement comptime locals", .{});
- }
- if (node.getAlignNode()) |align_node| {
- return mod.failNode(scope, align_node, "TODO implement alignment on locals", .{});
- }
- const tree = scope.tree();
- const name_src = tree.token_locs[node.name_token].start;
- const ident_name = try identifierTokenString(mod, scope, node.name_token);
- const init_node = node.getInitNode() orelse
- return mod.fail(scope, name_src, "variables must be initialized", .{});
-
- switch (tree.token_ids[node.mut_token]) {
- .Keyword_const => {
- // Depending on the type of AST the initialization expression is, we may need an lvalue
- // or an rvalue as a result location. If it is an rvalue, we can use the instruction as
- // the variable, no memory location needed.
- const result_loc = if (nodeMayNeedMemoryLocation(init_node)) r: {
- if (node.getTypeNode()) |type_node| {
- const type_inst = try typeExpr(mod, scope, type_node);
- const alloc = try addZIRUnOp(mod, scope, name_src, .alloc, type_inst);
- break :r ResultLoc{ .ptr = alloc };
- } else {
- const alloc = try addZIRNoOpT(mod, scope, name_src, .alloc_inferred);
- break :r ResultLoc{ .inferred_ptr = alloc };
- }
- } else r: {
- if (node.getTypeNode()) |type_node|
- break :r ResultLoc{ .ty = try typeExpr(mod, scope, type_node) }
- else
- break :r .none;
- };
- const init_inst = try expr(mod, scope, result_loc, init_node);
- const sub_scope = try block_arena.create(Scope.LocalVal);
- sub_scope.* = .{
- .parent = scope,
- .gen_zir = scope.getGenZIR(),
- .name = ident_name,
- .inst = init_inst,
- };
- return &sub_scope.base;
- },
- .Keyword_var => {
- const var_data: struct { result_loc: ResultLoc, alloc: *zir.Inst } = if (node.getTypeNode()) |type_node| a: {
- const type_inst = try typeExpr(mod, scope, type_node);
- const alloc = try addZIRUnOp(mod, scope, name_src, .alloc, type_inst);
- break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } };
- } else a: {
- const alloc = try addZIRNoOp(mod, scope, name_src, .alloc_inferred);
- break :a .{ .alloc = alloc, .result_loc = .{ .inferred_ptr = alloc.castTag(.alloc_inferred).? } };
- };
- const init_inst = try expr(mod, scope, var_data.result_loc, init_node);
- const sub_scope = try block_arena.create(Scope.LocalPtr);
- sub_scope.* = .{
- .parent = scope,
- .gen_zir = scope.getGenZIR(),
- .name = ident_name,
- .ptr = var_data.alloc,
- };
- return &sub_scope.base;
- },
- else => unreachable,
- }
-}
-
-fn assign(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) InnerError!void {
- if (infix_node.lhs.castTag(.Identifier)) |ident| {
- // This intentionally does not support @"_" syntax.
- const ident_name = scope.tree().tokenSlice(ident.token);
- if (mem.eql(u8, ident_name, "_")) {
- _ = try expr(mod, scope, .discard, infix_node.rhs);
- return;
- }
- }
- const lvalue = try lvalExpr(mod, scope, infix_node.lhs);
- _ = try expr(mod, scope, .{ .ptr = lvalue }, infix_node.rhs);
-}
-
-fn assignOp(
- mod: *Module,
- scope: *Scope,
- infix_node: *ast.Node.SimpleInfixOp,
- op_inst_tag: zir.Inst.Tag,
-) InnerError!void {
- const lhs_ptr = try lvalExpr(mod, scope, infix_node.lhs);
- const lhs = try addZIRUnOp(mod, scope, lhs_ptr.src, .deref, lhs_ptr);
- const lhs_type = try addZIRUnOp(mod, scope, lhs_ptr.src, .typeof, lhs);
- const rhs = try expr(mod, scope, .{ .ty = lhs_type }, infix_node.rhs);
-
- const tree = scope.tree();
- const src = tree.token_locs[infix_node.op_token].start;
-
- const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);
- _ = try addZIRBinOp(mod, scope, src, .store, lhs_ptr, result);
-}
-
-fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.op_token].start;
- const bool_type = try addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.bool_type),
- });
- const operand = try expr(mod, scope, .{ .ty = bool_type }, node.rhs);
- return addZIRUnOp(mod, scope, src, .boolnot, operand);
-}
-
-fn bitNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.op_token].start;
- const operand = try expr(mod, scope, .none, node.rhs);
- return addZIRUnOp(mod, scope, src, .bitnot, operand);
-}
-
-fn negation(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp, op_inst_tag: zir.Inst.Tag) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.op_token].start;
-
- const lhs = try addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.comptime_int),
- .val = Value.initTag(.zero),
- });
- const rhs = try expr(mod, scope, .none, node.rhs);
-
- return addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);
-}
-
-fn addressOf(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
- return expr(mod, scope, .ref, node.rhs);
-}
-
-fn optionalType(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.op_token].start;
- const operand = try typeExpr(mod, scope, node.rhs);
- return addZIRUnOp(mod, scope, src, .optional_type, operand);
-}
-
-fn sliceType(mod: *Module, scope: *Scope, node: *ast.Node.SliceType) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.op_token].start;
- return ptrSliceType(mod, scope, src, &node.ptr_info, node.rhs, .Slice);
-}
-
-fn ptrType(mod: *Module, scope: *Scope, node: *ast.Node.PtrType) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.op_token].start;
- return ptrSliceType(mod, scope, src, &node.ptr_info, node.rhs, switch (tree.token_ids[node.op_token]) {
- .Asterisk, .AsteriskAsterisk => .One,
- // TODO stage1 type inference bug
- .LBracket => @as(std.builtin.TypeInfo.Pointer.Size, switch (tree.token_ids[node.op_token + 2]) {
- .Identifier => .C,
- else => .Many,
- }),
- else => unreachable,
- });
-}
-
-fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo, rhs: *ast.Node, size: std.builtin.TypeInfo.Pointer.Size) InnerError!*zir.Inst {
- const simple = ptr_info.allowzero_token == null and
- ptr_info.align_info == null and
- ptr_info.volatile_token == null and
- ptr_info.sentinel == null;
-
- if (simple) {
- const child_type = try typeExpr(mod, scope, rhs);
- const mutable = ptr_info.const_token == null;
- // TODO stage1 type inference bug
- const T = zir.Inst.Tag;
- return addZIRUnOp(mod, scope, src, switch (size) {
- .One => if (mutable) T.single_mut_ptr_type else T.single_const_ptr_type,
- .Many => if (mutable) T.many_mut_ptr_type else T.many_const_ptr_type,
- .C => if (mutable) T.c_mut_ptr_type else T.c_const_ptr_type,
- .Slice => if (mutable) T.mut_slice_type else T.const_slice_type,
- }, child_type);
- }
-
- var kw_args: std.meta.fieldInfo(zir.Inst.PtrType, "kw_args").field_type = .{};
- kw_args.size = size;
- kw_args.@"allowzero" = ptr_info.allowzero_token != null;
- if (ptr_info.align_info) |some| {
- kw_args.@"align" = try expr(mod, scope, .none, some.node);
- if (some.bit_range) |bit_range| {
- kw_args.align_bit_start = try expr(mod, scope, .none, bit_range.start);
- kw_args.align_bit_end = try expr(mod, scope, .none, bit_range.end);
- }
- }
- kw_args.mutable = ptr_info.const_token == null;
- kw_args.@"volatile" = ptr_info.volatile_token != null;
- if (ptr_info.sentinel) |some| {
- kw_args.sentinel = try expr(mod, scope, .none, some);
- }
-
- const child_type = try typeExpr(mod, scope, rhs);
- if (kw_args.sentinel) |some| {
- kw_args.sentinel = try addZIRBinOp(mod, scope, some.src, .as, child_type, some);
- }
-
- return addZIRInst(mod, scope, src, zir.Inst.PtrType, .{ .child_type = child_type }, kw_args);
-}
-
-fn arrayType(mod: *Module, scope: *Scope, node: *ast.Node.ArrayType) !*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.op_token].start;
- const usize_type = try addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.usize_type),
- });
-
- // TODO check for [_]T
- const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr);
- const elem_type = try typeExpr(mod, scope, node.rhs);
-
- return addZIRBinOp(mod, scope, src, .array_type, len, elem_type);
-}
-
-fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.ArrayTypeSentinel) !*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.op_token].start;
- const usize_type = try addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.usize_type),
- });
-
- // TODO check for [_]T
- const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr);
- const sentinel_uncasted = try expr(mod, scope, .none, node.sentinel);
- const elem_type = try typeExpr(mod, scope, node.rhs);
- const sentinel = try addZIRBinOp(mod, scope, src, .as, elem_type, sentinel_uncasted);
-
- return addZIRInst(mod, scope, src, zir.Inst.ArrayTypeSentinel, .{
- .len = len,
- .sentinel = sentinel,
- .elem_type = elem_type,
- }, .{});
-}
-
-fn anyFrameType(mod: *Module, scope: *Scope, node: *ast.Node.AnyFrameType) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.anyframe_token].start;
- if (node.result) |some| {
- const return_type = try typeExpr(mod, scope, some.return_type);
- return addZIRUnOp(mod, scope, src, .anyframe_type, return_type);
- } else {
- return addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.anyframe_type),
- });
- }
-}
-
-fn typeInixOp(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp, op_inst_tag: zir.Inst.Tag) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.op_token].start;
- const error_set = try typeExpr(mod, scope, node.lhs);
- const payload = try typeExpr(mod, scope, node.rhs);
- return addZIRBinOp(mod, scope, src, op_inst_tag, error_set, payload);
-}
-
-fn enumLiteral(mod: *Module, scope: *Scope, node: *ast.Node.EnumLiteral) !*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.name].start;
- const name = try identifierTokenString(mod, scope, node.name);
-
- return addZIRInst(mod, scope, src, zir.Inst.EnumLiteral, .{ .name = name }, .{});
-}
-
-fn unwrapOptional(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.rtoken].start;
-
- const operand = try expr(mod, scope, .ref, node.lhs);
- return rlWrapPtr(mod, scope, rl, try addZIRUnOp(mod, scope, src, .unwrap_optional_safe, operand));
-}
-
-fn errorSetDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.ErrorSetDecl) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.error_token].start;
- const decls = node.decls();
- const fields = try scope.arena().alloc([]const u8, decls.len);
-
- for (decls) |decl, i| {
- const tag = decl.castTag(.ErrorTag).?;
- fields[i] = try identifierTokenString(mod, scope, tag.name_token);
- }
-
- // analyzing the error set results in a decl ref, so we might need to dereference it
- return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.ErrorSet, .{ .fields = fields }, .{}));
-}
-
-fn errorType(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.token].start;
- return addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.anyerror_type),
- });
-}
-
-fn catchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Catch) InnerError!*zir.Inst {
- return orelseCatchExpr(mod, scope, rl, node.lhs, node.op_token, .iserr, .unwrap_err_unsafe, node.rhs, node.payload);
-}
-
-fn orelseExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {
- return orelseCatchExpr(mod, scope, rl, node.lhs, node.op_token, .isnull, .unwrap_optional_unsafe, node.rhs, null);
-}
-
-fn orelseCatchExpr(
- mod: *Module,
- scope: *Scope,
- rl: ResultLoc,
- lhs: *ast.Node,
- op_token: ast.TokenIndex,
- cond_op: zir.Inst.Tag,
- unwrap_op: zir.Inst.Tag,
- rhs: *ast.Node,
- payload_node: ?*ast.Node,
-) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[op_token].start;
-
- const operand_ptr = try expr(mod, scope, .ref, lhs);
- // TODO we could avoid an unnecessary copy if .iserr, .isnull took a pointer
- const err_union = try addZIRUnOp(mod, scope, src, .deref, operand_ptr);
- const cond = try addZIRUnOp(mod, scope, src, cond_op, err_union);
-
- var block_scope: Scope.GenZIR = .{
- .parent = scope,
- .decl = scope.decl().?,
- .arena = scope.arena(),
- .instructions = .{},
- };
- defer block_scope.instructions.deinit(mod.gpa);
-
- const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{
- .condition = cond,
- .then_body = undefined, // populated below
- .else_body = undefined, // populated below
- }, .{});
-
- const block = try addZIRInstBlock(mod, scope, src, .block, .{
- .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
- });
-
- // Most result location types can be forwarded directly; however
- // if we need to write to a pointer which has an inferred type,
- // proper type inference requires peer type resolution on the if's
- // branches.
- const branch_rl: ResultLoc = switch (rl) {
- .discard, .none, .ty, .ptr, .ref => rl,
- .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
- };
-
- var then_scope: Scope.GenZIR = .{
- .parent = scope,
- .decl = block_scope.decl,
- .arena = block_scope.arena,
- .instructions = .{},
- };
- defer then_scope.instructions.deinit(mod.gpa);
-
- var err_val_scope: Scope.LocalVal = undefined;
- const then_sub_scope = blk: {
- const payload = payload_node orelse
- break :blk &then_scope.base;
-
- const err_name = tree.tokenSlice(payload.castTag(.Payload).?.error_symbol.firstToken());
- if (mem.eql(u8, err_name, "_"))
- break :blk &then_scope.base;
-
- const unwrapped_err_ptr = try addZIRUnOp(mod, &then_scope.base, src, .unwrap_err_code, operand_ptr);
- err_val_scope = .{
- .parent = &then_scope.base,
- .gen_zir = &then_scope,
- .name = err_name,
- .inst = try addZIRUnOp(mod, &then_scope.base, src, .deref, unwrapped_err_ptr),
- };
- break :blk &err_val_scope.base;
- };
-
- _ = try addZIRInst(mod, &then_scope.base, src, zir.Inst.Break, .{
- .block = block,
- .operand = try expr(mod, then_sub_scope, branch_rl, rhs),
- }, .{});
-
- var else_scope: Scope.GenZIR = .{
- .parent = scope,
- .decl = block_scope.decl,
- .arena = block_scope.arena,
- .instructions = .{},
- };
- defer else_scope.instructions.deinit(mod.gpa);
-
- const unwrapped_payload = try addZIRUnOp(mod, &else_scope.base, src, unwrap_op, operand_ptr);
- _ = try addZIRInst(mod, &else_scope.base, src, zir.Inst.Break, .{
- .block = block,
- .operand = unwrapped_payload,
- }, .{});
-
- condbr.positionals.then_body = .{ .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items) };
- condbr.positionals.else_body = .{ .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items) };
- return rlWrapPtr(mod, scope, rl, &block.base);
-}
-
-/// Return whether the identifier names of two tokens are equal. Resolves @"" tokens without allocating.
-/// OK in theory it could do it without allocating. This implementation allocates when the @"" form is used.
-fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool {
- const ident_name_1 = try identifierTokenString(mod, scope, token1);
- const ident_name_2 = try identifierTokenString(mod, scope, token2);
- return mem.eql(u8, ident_name_1, ident_name_2);
-}
-
-/// Identifier token -> String (allocated in scope.arena())
-fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 {
- const tree = scope.tree();
-
- const ident_name = tree.tokenSlice(token);
- if (mem.startsWith(u8, ident_name, "@")) {
- const raw_string = ident_name[1..];
- var bad_index: usize = undefined;
- return std.zig.parseStringLiteral(scope.arena(), raw_string, &bad_index) catch |err| switch (err) {
- error.InvalidCharacter => {
- const bad_byte = raw_string[bad_index];
- const src = tree.token_locs[token].start;
- return mod.fail(scope, src + 1 + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte});
- },
- else => |e| return e,
- };
- }
- return ident_name;
-}
-
-pub fn identifierStringInst(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.token].start;
-
- const ident_name = try identifierTokenString(mod, scope, node.token);
-
- return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = ident_name }, .{});
-}
-
-fn field(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.op_token].start;
-
- const lhs = try expr(mod, scope, .ref, node.lhs);
- const field_name = try identifierStringInst(mod, scope, node.rhs.castTag(.Identifier).?);
-
- return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.FieldPtr, .{ .object_ptr = lhs, .field_name = field_name }, .{}));
-}
-
-fn arrayAccess(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.ArrayAccess) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.rtoken].start;
-
- const array_ptr = try expr(mod, scope, .ref, node.lhs);
- const index = try expr(mod, scope, .none, node.index_expr);
-
- return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.ElemPtr, .{ .array_ptr = array_ptr, .index = index }, .{}));
-}
-
-fn sliceExpr(mod: *Module, scope: *Scope, node: *ast.Node.Slice) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.rtoken].start;
-
- const usize_type = try addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.usize_type),
- });
-
- const array_ptr = try expr(mod, scope, .ref, node.lhs);
- const start = try expr(mod, scope, .{ .ty = usize_type }, node.start);
-
- if (node.end == null and node.sentinel == null) {
- return try addZIRBinOp(mod, scope, src, .slice_start, array_ptr, start);
- }
-
- const end = if (node.end) |end| try expr(mod, scope, .{ .ty = usize_type }, end) else null;
- // we could get the child type here, but it is easier to just do it in semantic analysis.
- const sentinel = if (node.sentinel) |sentinel| try expr(mod, scope, .none, sentinel) else null;
-
- return try addZIRInst(
- mod,
- scope,
- src,
- zir.Inst.Slice,
- .{ .array_ptr = array_ptr, .start = start },
- .{ .end = end, .sentinel = sentinel },
- );
-}
-
-fn deref(mod: *Module, scope: *Scope, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.rtoken].start;
- const lhs = try expr(mod, scope, .none, node.lhs);
- return addZIRUnOp(mod, scope, src, .deref, lhs);
-}
-
-fn simpleBinOp(
- mod: *Module,
- scope: *Scope,
- rl: ResultLoc,
- infix_node: *ast.Node.SimpleInfixOp,
- op_inst_tag: zir.Inst.Tag,
-) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[infix_node.op_token].start;
-
- const lhs = try expr(mod, scope, .none, infix_node.lhs);
- const rhs = try expr(mod, scope, .none, infix_node.rhs);
-
- const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);
- return rlWrap(mod, scope, rl, result);
-}
-
-fn boolBinOp(
- mod: *Module,
- scope: *Scope,
- rl: ResultLoc,
- infix_node: *ast.Node.SimpleInfixOp,
-) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[infix_node.op_token].start;
- const bool_type = try addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.bool_type),
- });
-
- var block_scope: Scope.GenZIR = .{
- .parent = scope,
- .decl = scope.decl().?,
- .arena = scope.arena(),
- .instructions = .{},
- };
- defer block_scope.instructions.deinit(mod.gpa);
-
- const lhs = try expr(mod, scope, .{ .ty = bool_type }, infix_node.lhs);
- const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{
- .condition = lhs,
- .then_body = undefined, // populated below
- .else_body = undefined, // populated below
- }, .{});
-
- const block = try addZIRInstBlock(mod, scope, src, .block, .{
- .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
- });
-
- var rhs_scope: Scope.GenZIR = .{
- .parent = scope,
- .decl = block_scope.decl,
- .arena = block_scope.arena,
- .instructions = .{},
- };
- defer rhs_scope.instructions.deinit(mod.gpa);
-
- const rhs = try expr(mod, &rhs_scope.base, .{ .ty = bool_type }, infix_node.rhs);
- _ = try addZIRInst(mod, &rhs_scope.base, src, zir.Inst.Break, .{
- .block = block,
- .operand = rhs,
- }, .{});
-
- var const_scope: Scope.GenZIR = .{
- .parent = scope,
- .decl = block_scope.decl,
- .arena = block_scope.arena,
- .instructions = .{},
- };
- defer const_scope.instructions.deinit(mod.gpa);
-
- const is_bool_and = infix_node.base.tag == .BoolAnd;
- _ = try addZIRInst(mod, &const_scope.base, src, zir.Inst.Break, .{
- .block = block,
- .operand = try addZIRInstConst(mod, &const_scope.base, src, .{
- .ty = Type.initTag(.bool),
- .val = if (is_bool_and) Value.initTag(.bool_false) else Value.initTag(.bool_true),
- }),
- }, .{});
-
- if (is_bool_and) {
- // if lhs // AND
- // break rhs
- // else
- // break false
- condbr.positionals.then_body = .{ .instructions = try rhs_scope.arena.dupe(*zir.Inst, rhs_scope.instructions.items) };
- condbr.positionals.else_body = .{ .instructions = try const_scope.arena.dupe(*zir.Inst, const_scope.instructions.items) };
- } else {
- // if lhs // OR
- // break true
- // else
- // break rhs
- condbr.positionals.then_body = .{ .instructions = try const_scope.arena.dupe(*zir.Inst, const_scope.instructions.items) };
- condbr.positionals.else_body = .{ .instructions = try rhs_scope.arena.dupe(*zir.Inst, rhs_scope.instructions.items) };
- }
-
- return rlWrap(mod, scope, rl, &block.base);
-}
-
-const CondKind = union(enum) {
- bool,
- optional: ?*zir.Inst,
- err_union: ?*zir.Inst,
-
- fn cond(self: *CondKind, mod: *Module, block_scope: *Scope.GenZIR, src: usize, cond_node: *ast.Node) !*zir.Inst {
- switch (self.*) {
- .bool => {
- const bool_type = try addZIRInstConst(mod, &block_scope.base, src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.bool_type),
- });
- return try expr(mod, &block_scope.base, .{ .ty = bool_type }, cond_node);
- },
- .optional => {
- const cond_ptr = try expr(mod, &block_scope.base, .ref, cond_node);
- self.* = .{ .optional = cond_ptr };
- const result = try addZIRUnOp(mod, &block_scope.base, src, .deref, cond_ptr);
- return try addZIRUnOp(mod, &block_scope.base, src, .isnonnull, result);
- },
- .err_union => {
- const err_ptr = try expr(mod, &block_scope.base, .ref, cond_node);
- self.* = .{ .err_union = err_ptr };
- const result = try addZIRUnOp(mod, &block_scope.base, src, .deref, err_ptr);
- return try addZIRUnOp(mod, &block_scope.base, src, .iserr, result);
- },
- }
- }
-
- fn thenSubScope(self: CondKind, mod: *Module, then_scope: *Scope.GenZIR, src: usize, payload_node: ?*ast.Node) !*Scope {
- if (self == .bool) return &then_scope.base;
-
- const payload = payload_node.?.castTag(.PointerPayload) orelse {
- // condition is error union and payload is not explicitly ignored
- _ = try addZIRUnOp(mod, &then_scope.base, src, .ensure_err_payload_void, self.err_union.?);
- return &then_scope.base;
- };
- const is_ptr = payload.ptr_token != null;
- const ident_node = payload.value_symbol.castTag(.Identifier).?;
-
- // This intentionally does not support @"_" syntax.
- const ident_name = then_scope.base.tree().tokenSlice(ident_node.token);
- if (mem.eql(u8, ident_name, "_")) {
- if (is_ptr)
- return mod.failTok(&then_scope.base, payload.ptr_token.?, "pointer modifier invalid on discard", .{});
- return &then_scope.base;
- }
-
- return mod.failNode(&then_scope.base, payload.value_symbol, "TODO implement payload symbols", .{});
- }
-
- fn elseSubScope(self: CondKind, mod: *Module, else_scope: *Scope.GenZIR, src: usize, payload_node: ?*ast.Node) !*Scope {
- if (self != .err_union) return &else_scope.base;
-
- const payload_ptr = try addZIRUnOp(mod, &else_scope.base, src, .unwrap_err_unsafe, self.err_union.?);
-
- const payload = payload_node.?.castTag(.Payload).?;
- const ident_node = payload.error_symbol.castTag(.Identifier).?;
-
- // This intentionally does not support @"_" syntax.
- const ident_name = else_scope.base.tree().tokenSlice(ident_node.token);
- if (mem.eql(u8, ident_name, "_")) {
- return &else_scope.base;
- }
-
- return mod.failNode(&else_scope.base, payload.error_symbol, "TODO implement payload symbols", .{});
- }
-};
-
-fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) InnerError!*zir.Inst {
- var cond_kind: CondKind = .bool;
- if (if_node.payload) |_| cond_kind = .{ .optional = null };
- if (if_node.@"else") |else_node| {
- if (else_node.payload) |payload| {
- cond_kind = .{ .err_union = null };
- }
- }
- var block_scope: Scope.GenZIR = .{
- .parent = scope,
- .decl = scope.decl().?,
- .arena = scope.arena(),
- .instructions = .{},
- };
- defer block_scope.instructions.deinit(mod.gpa);
-
- const tree = scope.tree();
- const if_src = tree.token_locs[if_node.if_token].start;
- const cond = try cond_kind.cond(mod, &block_scope, if_src, if_node.condition);
-
- const condbr = try addZIRInstSpecial(mod, &block_scope.base, if_src, zir.Inst.CondBr, .{
- .condition = cond,
- .then_body = undefined, // populated below
- .else_body = undefined, // populated below
- }, .{});
-
- const block = try addZIRInstBlock(mod, scope, if_src, .block, .{
- .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
- });
-
- const then_src = tree.token_locs[if_node.body.lastToken()].start;
- var then_scope: Scope.GenZIR = .{
- .parent = scope,
- .decl = block_scope.decl,
- .arena = block_scope.arena,
- .instructions = .{},
- };
- defer then_scope.instructions.deinit(mod.gpa);
-
- // declare payload to the then_scope
- const then_sub_scope = try cond_kind.thenSubScope(mod, &then_scope, then_src, if_node.payload);
-
- // Most result location types can be forwarded directly; however
- // if we need to write to a pointer which has an inferred type,
- // proper type inference requires peer type resolution on the if's
- // branches.
- const branch_rl: ResultLoc = switch (rl) {
- .discard, .none, .ty, .ptr, .ref => rl,
- .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
- };
-
- const then_result = try expr(mod, then_sub_scope, branch_rl, if_node.body);
- if (!then_result.tag.isNoReturn()) {
- _ = try addZIRInst(mod, then_sub_scope, then_src, zir.Inst.Break, .{
- .block = block,
- .operand = then_result,
- }, .{});
- }
- condbr.positionals.then_body = .{
- .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),
- };
-
- var else_scope: Scope.GenZIR = .{
- .parent = scope,
- .decl = block_scope.decl,
- .arena = block_scope.arena,
- .instructions = .{},
- };
- defer else_scope.instructions.deinit(mod.gpa);
-
- if (if_node.@"else") |else_node| {
- const else_src = tree.token_locs[else_node.body.lastToken()].start;
- // declare payload to the then_scope
- const else_sub_scope = try cond_kind.elseSubScope(mod, &else_scope, else_src, else_node.payload);
-
- const else_result = try expr(mod, else_sub_scope, branch_rl, else_node.body);
- if (!else_result.tag.isNoReturn()) {
- _ = try addZIRInst(mod, else_sub_scope, else_src, zir.Inst.Break, .{
- .block = block,
- .operand = else_result,
- }, .{});
- }
- } else {
- // TODO Optimization opportunity: we can avoid an allocation and a memcpy here
- // by directly allocating the body for this one instruction.
- const else_src = tree.token_locs[if_node.lastToken()].start;
- _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.BreakVoid, .{
- .block = block,
- }, .{});
- }
- condbr.positionals.else_body = .{
- .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
- };
-
- return &block.base;
-}
-
-fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.While) InnerError!*zir.Inst {
- var cond_kind: CondKind = .bool;
- if (while_node.payload) |_| cond_kind = .{ .optional = null };
- if (while_node.@"else") |else_node| {
- if (else_node.payload) |payload| {
- cond_kind = .{ .err_union = null };
- }
- }
-
- if (while_node.label) |tok|
- return mod.failTok(scope, tok, "TODO labeled while", .{});
-
- if (while_node.inline_token) |tok|
- return mod.failTok(scope, tok, "TODO inline while", .{});
-
- var expr_scope: Scope.GenZIR = .{
- .parent = scope,
- .decl = scope.decl().?,
- .arena = scope.arena(),
- .instructions = .{},
- };
- defer expr_scope.instructions.deinit(mod.gpa);
-
- var loop_scope: Scope.GenZIR = .{
- .parent = &expr_scope.base,
- .decl = expr_scope.decl,
- .arena = expr_scope.arena,
- .instructions = .{},
- };
- defer loop_scope.instructions.deinit(mod.gpa);
-
- var continue_scope: Scope.GenZIR = .{
- .parent = &loop_scope.base,
- .decl = loop_scope.decl,
- .arena = loop_scope.arena,
- .instructions = .{},
- };
- defer continue_scope.instructions.deinit(mod.gpa);
-
- const tree = scope.tree();
- const while_src = tree.token_locs[while_node.while_token].start;
- const void_type = try addZIRInstConst(mod, scope, while_src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.void_type),
- });
- const cond = try cond_kind.cond(mod, &continue_scope, while_src, while_node.condition);
-
- const condbr = try addZIRInstSpecial(mod, &continue_scope.base, while_src, zir.Inst.CondBr, .{
- .condition = cond,
- .then_body = undefined, // populated below
- .else_body = undefined, // populated below
- }, .{});
- const cond_block = try addZIRInstBlock(mod, &loop_scope.base, while_src, .block, .{
- .instructions = try loop_scope.arena.dupe(*zir.Inst, continue_scope.instructions.items),
- });
- // TODO avoid emitting the continue expr when there
- // are no jumps to it. This happens when the last statement of a while body is noreturn
- // and there are no `continue` statements.
- // The "repeat" at the end of a loop body is implied.
- if (while_node.continue_expr) |cont_expr| {
- _ = try expr(mod, &loop_scope.base, .{ .ty = void_type }, cont_expr);
- }
- const loop = try addZIRInstLoop(mod, &expr_scope.base, while_src, .{
- .instructions = try expr_scope.arena.dupe(*zir.Inst, loop_scope.instructions.items),
- });
- const while_block = try addZIRInstBlock(mod, scope, while_src, .block, .{
- .instructions = try expr_scope.arena.dupe(*zir.Inst, expr_scope.instructions.items),
- });
-
- const then_src = tree.token_locs[while_node.body.lastToken()].start;
- var then_scope: Scope.GenZIR = .{
- .parent = &continue_scope.base,
- .decl = continue_scope.decl,
- .arena = continue_scope.arena,
- .instructions = .{},
- };
- defer then_scope.instructions.deinit(mod.gpa);
-
- // declare payload to the then_scope
- const then_sub_scope = try cond_kind.thenSubScope(mod, &then_scope, then_src, while_node.payload);
-
- // Most result location types can be forwarded directly; however
- // if we need to write to a pointer which has an inferred type,
- // proper type inference requires peer type resolution on the while's
- // branches.
- const branch_rl: ResultLoc = switch (rl) {
- .discard, .none, .ty, .ptr, .ref => rl,
- .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = while_block },
- };
-
- const then_result = try expr(mod, then_sub_scope, branch_rl, while_node.body);
- if (!then_result.tag.isNoReturn()) {
- _ = try addZIRInst(mod, then_sub_scope, then_src, zir.Inst.Break, .{
- .block = cond_block,
- .operand = then_result,
- }, .{});
- }
- condbr.positionals.then_body = .{
- .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),
- };
-
- var else_scope: Scope.GenZIR = .{
- .parent = &continue_scope.base,
- .decl = continue_scope.decl,
- .arena = continue_scope.arena,
- .instructions = .{},
- };
- defer else_scope.instructions.deinit(mod.gpa);
-
- if (while_node.@"else") |else_node| {
- const else_src = tree.token_locs[else_node.body.lastToken()].start;
- // declare payload to the then_scope
- const else_sub_scope = try cond_kind.elseSubScope(mod, &else_scope, else_src, else_node.payload);
-
- const else_result = try expr(mod, else_sub_scope, branch_rl, else_node.body);
- if (!else_result.tag.isNoReturn()) {
- _ = try addZIRInst(mod, else_sub_scope, else_src, zir.Inst.Break, .{
- .block = while_block,
- .operand = else_result,
- }, .{});
- }
- } else {
- const else_src = tree.token_locs[while_node.lastToken()].start;
- _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.BreakVoid, .{
- .block = while_block,
- }, .{});
- }
- condbr.positionals.else_body = .{
- .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
- };
- return &while_block.base;
-}
-
-fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For) InnerError!*zir.Inst {
- if (for_node.label) |tok|
- return mod.failTok(scope, tok, "TODO labeled for", .{});
-
- if (for_node.inline_token) |tok|
- return mod.failTok(scope, tok, "TODO inline for", .{});
-
- var for_scope: Scope.GenZIR = .{
- .parent = scope,
- .decl = scope.decl().?,
- .arena = scope.arena(),
- .instructions = .{},
- };
- defer for_scope.instructions.deinit(mod.gpa);
-
- // setup variables and constants
- const tree = scope.tree();
- const for_src = tree.token_locs[for_node.for_token].start;
- const index_ptr = blk: {
- const usize_type = try addZIRInstConst(mod, &for_scope.base, for_src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.usize_type),
- });
- const index_ptr = try addZIRUnOp(mod, &for_scope.base, for_src, .alloc, usize_type);
- // initialize to zero
- const zero = try addZIRInstConst(mod, &for_scope.base, for_src, .{
- .ty = Type.initTag(.usize),
- .val = Value.initTag(.zero),
- });
- _ = try addZIRBinOp(mod, &for_scope.base, for_src, .store, index_ptr, zero);
- break :blk index_ptr;
- };
- const array_ptr = try expr(mod, &for_scope.base, .ref, for_node.array_expr);
- _ = try addZIRUnOp(mod, &for_scope.base, for_node.array_expr.firstToken(), .ensure_indexable, array_ptr);
- const cond_src = tree.token_locs[for_node.array_expr.firstToken()].start;
- const len_ptr = try addZIRInst(mod, &for_scope.base, cond_src, zir.Inst.FieldPtr, .{
- .object_ptr = array_ptr,
- .field_name = try addZIRInst(mod, &for_scope.base, cond_src, zir.Inst.Str, .{ .bytes = "len" }, .{}),
- }, .{});
-
- var loop_scope: Scope.GenZIR = .{
- .parent = &for_scope.base,
- .decl = for_scope.decl,
- .arena = for_scope.arena,
- .instructions = .{},
- };
- defer loop_scope.instructions.deinit(mod.gpa);
-
- var cond_scope: Scope.GenZIR = .{
- .parent = &loop_scope.base,
- .decl = loop_scope.decl,
- .arena = loop_scope.arena,
- .instructions = .{},
- };
- defer cond_scope.instructions.deinit(mod.gpa);
-
- // check condition i < array_expr.len
- const index = try addZIRUnOp(mod, &cond_scope.base, cond_src, .deref, index_ptr);
- const len = try addZIRUnOp(mod, &cond_scope.base, cond_src, .deref, len_ptr);
- const cond = try addZIRBinOp(mod, &cond_scope.base, cond_src, .cmp_lt, index, len);
-
- const condbr = try addZIRInstSpecial(mod, &cond_scope.base, for_src, zir.Inst.CondBr, .{
- .condition = cond,
- .then_body = undefined, // populated below
- .else_body = undefined, // populated below
- }, .{});
- const cond_block = try addZIRInstBlock(mod, &loop_scope.base, for_src, .block, .{
- .instructions = try loop_scope.arena.dupe(*zir.Inst, cond_scope.instructions.items),
- });
-
- // increment index variable
- const one = try addZIRInstConst(mod, &loop_scope.base, for_src, .{
- .ty = Type.initTag(.usize),
- .val = Value.initTag(.one),
- });
- const index_2 = try addZIRUnOp(mod, &loop_scope.base, cond_src, .deref, index_ptr);
- const index_plus_one = try addZIRBinOp(mod, &loop_scope.base, for_src, .add, index_2, one);
- _ = try addZIRBinOp(mod, &loop_scope.base, for_src, .store, index_ptr, index_plus_one);
-
- // looping stuff
- const loop = try addZIRInstLoop(mod, &for_scope.base, for_src, .{
- .instructions = try for_scope.arena.dupe(*zir.Inst, loop_scope.instructions.items),
- });
- const for_block = try addZIRInstBlock(mod, scope, for_src, .block, .{
- .instructions = try for_scope.arena.dupe(*zir.Inst, for_scope.instructions.items),
- });
-
- // while body
- const then_src = tree.token_locs[for_node.body.lastToken()].start;
- var then_scope: Scope.GenZIR = .{
- .parent = &cond_scope.base,
- .decl = cond_scope.decl,
- .arena = cond_scope.arena,
- .instructions = .{},
- };
- defer then_scope.instructions.deinit(mod.gpa);
-
- // Most result location types can be forwarded directly; however
- // if we need to write to a pointer which has an inferred type,
- // proper type inference requires peer type resolution on the while's
- // branches.
- const branch_rl: ResultLoc = switch (rl) {
- .discard, .none, .ty, .ptr, .ref => rl,
- .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = for_block },
- };
-
- var index_scope: Scope.LocalPtr = undefined;
- const then_sub_scope = blk: {
- const payload = for_node.payload.castTag(.PointerIndexPayload).?;
- const is_ptr = payload.ptr_token != null;
- const value_name = tree.tokenSlice(payload.value_symbol.firstToken());
- if (!mem.eql(u8, value_name, "_")) {
- return mod.failNode(&then_scope.base, payload.value_symbol, "TODO implement for value payload", .{});
- } else if (is_ptr) {
- return mod.failTok(&then_scope.base, payload.ptr_token.?, "pointer modifier invalid on discard", .{});
- }
-
- const index_symbol_node = payload.index_symbol orelse
- break :blk &then_scope.base;
-
- const index_name = tree.tokenSlice(index_symbol_node.firstToken());
- if (mem.eql(u8, index_name, "_")) {
- break :blk &then_scope.base;
- }
- // TODO make this const without an extra copy?
- index_scope = .{
- .parent = &then_scope.base,
- .gen_zir = &then_scope,
- .name = index_name,
- .ptr = index_ptr,
- };
- break :blk &index_scope.base;
- };
-
- const then_result = try expr(mod, then_sub_scope, branch_rl, for_node.body);
- if (!then_result.tag.isNoReturn()) {
- _ = try addZIRInst(mod, then_sub_scope, then_src, zir.Inst.Break, .{
- .block = cond_block,
- .operand = then_result,
- }, .{});
- }
- condbr.positionals.then_body = .{
- .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),
- };
-
- // else branch
- var else_scope: Scope.GenZIR = .{
- .parent = &cond_scope.base,
- .decl = cond_scope.decl,
- .arena = cond_scope.arena,
- .instructions = .{},
- };
- defer else_scope.instructions.deinit(mod.gpa);
-
- if (for_node.@"else") |else_node| {
- const else_src = tree.token_locs[else_node.body.lastToken()].start;
- const else_result = try expr(mod, &else_scope.base, branch_rl, else_node.body);
- if (!else_result.tag.isNoReturn()) {
- _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.Break, .{
- .block = for_block,
- .operand = else_result,
- }, .{});
- }
- } else {
- const else_src = tree.token_locs[for_node.lastToken()].start;
- _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.BreakVoid, .{
- .block = for_block,
- }, .{});
- }
- condbr.positionals.else_body = .{
- .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
- };
- return &for_block.base;
-}
-
-fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[cfe.ltoken].start;
- if (cfe.getRHS()) |rhs_node| {
- if (nodeMayNeedMemoryLocation(rhs_node)) {
- const ret_ptr = try addZIRNoOp(mod, scope, src, .ret_ptr);
- const operand = try expr(mod, scope, .{ .ptr = ret_ptr }, rhs_node);
- return addZIRUnOp(mod, scope, src, .@"return", operand);
- } else {
- const fn_ret_ty = try addZIRNoOp(mod, scope, src, .ret_type);
- const operand = try expr(mod, scope, .{ .ty = fn_ret_ty }, rhs_node);
- return addZIRUnOp(mod, scope, src, .@"return", operand);
- }
- } else {
- return addZIRNoOp(mod, scope, src, .returnvoid);
- }
-}
-
-fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneToken) InnerError!*zir.Inst {
- const tracy = trace(@src());
- defer tracy.end();
-
- const tree = scope.tree();
- const ident_name = try identifierTokenString(mod, scope, ident.token);
- const src = tree.token_locs[ident.token].start;
- if (mem.eql(u8, ident_name, "_")) {
- return mod.failNode(scope, &ident.base, "TODO implement '_' identifier", .{});
- }
-
- if (getSimplePrimitiveValue(ident_name)) |typed_value| {
- const result = try addZIRInstConst(mod, scope, src, typed_value);
- return rlWrap(mod, scope, rl, result);
- }
-
- if (ident_name.len >= 2) integer: {
- const first_c = ident_name[0];
- if (first_c == 'i' or first_c == 'u') {
- const is_signed = first_c == 'i';
- const bit_count = std.fmt.parseInt(u16, ident_name[1..], 10) catch |err| switch (err) {
- error.Overflow => return mod.failNode(
- scope,
- &ident.base,
- "primitive integer type '{}' exceeds maximum bit width of 65535",
- .{ident_name},
- ),
- error.InvalidCharacter => break :integer,
- };
- const val = switch (bit_count) {
- 8 => if (is_signed) Value.initTag(.i8_type) else Value.initTag(.u8_type),
- 16 => if (is_signed) Value.initTag(.i16_type) else Value.initTag(.u16_type),
- 32 => if (is_signed) Value.initTag(.i32_type) else Value.initTag(.u32_type),
- 64 => if (is_signed) Value.initTag(.i64_type) else Value.initTag(.u64_type),
- else => {
- const int_type_payload = try scope.arena().create(Value.Payload.IntType);
- int_type_payload.* = .{ .signed = is_signed, .bits = bit_count };
- const result = try addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.type),
- .val = Value.initPayload(&int_type_payload.base),
- });
- return rlWrap(mod, scope, rl, result);
- },
- };
- const result = try addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.type),
- .val = val,
- });
- return rlWrap(mod, scope, rl, result);
- }
- }
-
- // Local variables, including function parameters.
- {
- var s = scope;
- while (true) switch (s.tag) {
- .local_val => {
- const local_val = s.cast(Scope.LocalVal).?;
- if (mem.eql(u8, local_val.name, ident_name)) {
- return rlWrap(mod, scope, rl, local_val.inst);
- }
- s = local_val.parent;
- },
- .local_ptr => {
- const local_ptr = s.cast(Scope.LocalPtr).?;
- if (mem.eql(u8, local_ptr.name, ident_name)) {
- return rlWrapPtr(mod, scope, rl, local_ptr.ptr);
- }
- s = local_ptr.parent;
- },
- .gen_zir => s = s.cast(Scope.GenZIR).?.parent,
- else => break,
- };
- }
-
- if (mod.lookupDeclName(scope, ident_name)) |decl| {
- return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{}));
- }
-
- return mod.failNode(scope, &ident.base, "use of undeclared identifier '{}'", .{ident_name});
-}
-
-fn stringLiteral(mod: *Module, scope: *Scope, str_lit: *ast.Node.OneToken) InnerError!*zir.Inst {
- const tree = scope.tree();
- const unparsed_bytes = tree.tokenSlice(str_lit.token);
- const arena = scope.arena();
-
- var bad_index: usize = undefined;
- const bytes = std.zig.parseStringLiteral(arena, unparsed_bytes, &bad_index) catch |err| switch (err) {
- error.InvalidCharacter => {
- const bad_byte = unparsed_bytes[bad_index];
- const src = tree.token_locs[str_lit.token].start;
- return mod.fail(scope, src + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte});
- },
- else => |e| return e,
- };
-
- const src = tree.token_locs[str_lit.token].start;
- return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{});
-}
-
-fn multilineStrLiteral(mod: *Module, scope: *Scope, node: *ast.Node.MultilineStringLiteral) !*zir.Inst {
- const tree = scope.tree();
- const lines = node.linesConst();
- const src = tree.token_locs[lines[0]].start;
-
- // line lengths and new lines
- var len = lines.len - 1;
- for (lines) |line| {
- // 2 for the '//' + 1 for '\n'
- len += tree.tokenSlice(line).len - 3;
- }
-
- const bytes = try scope.arena().alloc(u8, len);
- var i: usize = 0;
- for (lines) |line, line_i| {
- if (line_i != 0) {
- bytes[i] = '\n';
- i += 1;
- }
- const slice = tree.tokenSlice(line);
- mem.copy(u8, bytes[i..], slice[2 .. slice.len - 1]);
- i += slice.len - 3;
- }
-
- return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{});
-}
-
-fn charLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) !*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[node.token].start;
- const slice = tree.tokenSlice(node.token);
-
- var bad_index: usize = undefined;
- const value = std.zig.parseCharLiteral(slice, &bad_index) catch |err| switch (err) {
- error.InvalidCharacter => {
- const bad_byte = slice[bad_index];
- return mod.fail(scope, src + bad_index, "invalid character: '{c}'\n", .{bad_byte});
- },
- };
-
- const int_payload = try scope.arena().create(Value.Payload.Int_u64);
- int_payload.* = .{ .int = value };
- return addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.comptime_int),
- .val = Value.initPayload(&int_payload.base),
- });
-}
-
-fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.OneToken) InnerError!*zir.Inst {
- const arena = scope.arena();
- const tree = scope.tree();
- const prefixed_bytes = tree.tokenSlice(int_lit.token);
- const base = if (mem.startsWith(u8, prefixed_bytes, "0x"))
- 16
- else if (mem.startsWith(u8, prefixed_bytes, "0o"))
- 8
- else if (mem.startsWith(u8, prefixed_bytes, "0b"))
- 2
- else
- @as(u8, 10);
-
- const bytes = if (base == 10)
- prefixed_bytes
- else
- prefixed_bytes[2..];
-
- if (std.fmt.parseInt(u64, bytes, base)) |small_int| {
- const int_payload = try arena.create(Value.Payload.Int_u64);
- int_payload.* = .{ .int = small_int };
- const src = tree.token_locs[int_lit.token].start;
- return addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.comptime_int),
- .val = Value.initPayload(&int_payload.base),
- });
- } else |err| {
- return mod.failTok(scope, int_lit.token, "TODO implement int literals that don't fit in a u64", .{});
- }
-}
-
-fn floatLiteral(mod: *Module, scope: *Scope, float_lit: *ast.Node.OneToken) InnerError!*zir.Inst {
- const arena = scope.arena();
- const tree = scope.tree();
- const bytes = tree.tokenSlice(float_lit.token);
- if (bytes.len > 2 and bytes[1] == 'x') {
- return mod.failTok(scope, float_lit.token, "TODO hex floats", .{});
- }
-
- const val = std.fmt.parseFloat(f128, bytes) catch |e| switch (e) {
- error.InvalidCharacter => unreachable, // validated by tokenizer
- };
- const float_payload = try arena.create(Value.Payload.Float_128);
- float_payload.* = .{ .val = val };
- const src = tree.token_locs[float_lit.token].start;
- return addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.comptime_float),
- .val = Value.initPayload(&float_payload.base),
- });
-}
-
-fn undefLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
- const arena = scope.arena();
- const tree = scope.tree();
- const src = tree.token_locs[node.token].start;
- return addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.@"undefined"),
- .val = Value.initTag(.undef),
- });
-}
-
-fn boolLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
- const arena = scope.arena();
- const tree = scope.tree();
- const src = tree.token_locs[node.token].start;
- return addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.bool),
- .val = switch (tree.token_ids[node.token]) {
- .Keyword_true => Value.initTag(.bool_true),
- .Keyword_false => Value.initTag(.bool_false),
- else => unreachable,
- },
- });
-}
-
-fn nullLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
- const arena = scope.arena();
- const tree = scope.tree();
- const src = tree.token_locs[node.token].start;
- return addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.@"null"),
- .val = Value.initTag(.null_value),
- });
-}
-
-fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zir.Inst {
- if (asm_node.outputs.len != 0) {
- return mod.failNode(scope, &asm_node.base, "TODO implement asm with an output", .{});
- }
- const arena = scope.arena();
- const tree = scope.tree();
-
- const inputs = try arena.alloc(*zir.Inst, asm_node.inputs.len);
- const args = try arena.alloc(*zir.Inst, asm_node.inputs.len);
-
- const src = tree.token_locs[asm_node.asm_token].start;
-
- const str_type = try addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.const_slice_u8_type),
- });
- const str_type_rl: ResultLoc = .{ .ty = str_type };
-
- for (asm_node.inputs) |input, i| {
- // TODO semantically analyze constraints
- inputs[i] = try expr(mod, scope, str_type_rl, input.constraint);
- args[i] = try expr(mod, scope, .none, input.expr);
- }
-
- const return_type = try addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.void_type),
- });
- const asm_inst = try addZIRInst(mod, scope, src, zir.Inst.Asm, .{
- .asm_source = try expr(mod, scope, str_type_rl, asm_node.template),
- .return_type = return_type,
- }, .{
- .@"volatile" = asm_node.volatile_token != null,
- //.clobbers = TODO handle clobbers
- .inputs = inputs,
- .args = args,
- });
- return asm_inst;
-}
-
-fn ensureBuiltinParamCount(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall, count: u32) !void {
- if (call.params_len == count)
- return;
-
- const s = if (count == 1) "" else "s";
- return mod.failTok(scope, call.builtin_token, "expected {} parameter{}, found {}", .{ count, s, call.params_len });
-}
-
-fn simpleCast(
- mod: *Module,
- scope: *Scope,
- rl: ResultLoc,
- call: *ast.Node.BuiltinCall,
- inst_tag: zir.Inst.Tag,
-) InnerError!*zir.Inst {
- try ensureBuiltinParamCount(mod, scope, call, 2);
- const tree = scope.tree();
- const src = tree.token_locs[call.builtin_token].start;
- const params = call.params();
- const dest_type = try typeExpr(mod, scope, params[0]);
- const rhs = try expr(mod, scope, .none, params[1]);
- const result = try addZIRBinOp(mod, scope, src, inst_tag, dest_type, rhs);
- return rlWrap(mod, scope, rl, result);
-}
-
-fn ptrToInt(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
- try ensureBuiltinParamCount(mod, scope, call, 1);
- const operand = try expr(mod, scope, .none, call.params()[0]);
- const tree = scope.tree();
- const src = tree.token_locs[call.builtin_token].start;
- return addZIRUnOp(mod, scope, src, .ptrtoint, operand);
-}
-
-fn as(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
- try ensureBuiltinParamCount(mod, scope, call, 2);
- const tree = scope.tree();
- const src = tree.token_locs[call.builtin_token].start;
- const params = call.params();
- const dest_type = try typeExpr(mod, scope, params[0]);
- switch (rl) {
- .none => return try expr(mod, scope, .{ .ty = dest_type }, params[1]),
- .discard => {
- const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]);
- _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
- return result;
- },
- .ref => {
- const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]);
- return addZIRUnOp(mod, scope, result.src, .ref, result);
- },
- .ty => |result_ty| {
- const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]);
- return addZIRBinOp(mod, scope, src, .as, result_ty, result);
- },
- .ptr => |result_ptr| {
- const casted_result_ptr = try addZIRBinOp(mod, scope, src, .coerce_result_ptr, dest_type, result_ptr);
- return expr(mod, scope, .{ .ptr = casted_result_ptr }, params[1]);
- },
- .bitcasted_ptr => |bitcasted_ptr| {
- // TODO here we should be able to resolve the inference; we now have a type for the result.
- return mod.failTok(scope, call.builtin_token, "TODO implement @as with result location @bitCast", .{});
- },
- .inferred_ptr => |result_alloc| {
- // TODO here we should be able to resolve the inference; we now have a type for the result.
- return mod.failTok(scope, call.builtin_token, "TODO implement @as with inferred-type result location pointer", .{});
- },
- .block_ptr => |block_ptr| {
- const casted_block_ptr = try addZIRInst(mod, scope, src, zir.Inst.CoerceResultBlockPtr, .{
- .dest_type = dest_type,
- .block = block_ptr,
- }, .{});
- return expr(mod, scope, .{ .ptr = casted_block_ptr }, params[1]);
- },
- }
-}
-
-fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
- try ensureBuiltinParamCount(mod, scope, call, 2);
- const tree = scope.tree();
- const src = tree.token_locs[call.builtin_token].start;
- const params = call.params();
- const dest_type = try typeExpr(mod, scope, params[0]);
- switch (rl) {
- .none => {
- const operand = try expr(mod, scope, .none, params[1]);
- return addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand);
- },
- .discard => {
- const operand = try expr(mod, scope, .none, params[1]);
- const result = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand);
- _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
- return result;
- },
- .ref => {
- const operand = try expr(mod, scope, .ref, params[1]);
- const result = try addZIRBinOp(mod, scope, src, .bitcast_ref, dest_type, operand);
- return result;
- },
- .ty => |result_ty| {
- const result = try expr(mod, scope, .none, params[1]);
- const bitcasted = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, result);
- return addZIRBinOp(mod, scope, src, .as, result_ty, bitcasted);
- },
- .ptr => |result_ptr| {
- const casted_result_ptr = try addZIRUnOp(mod, scope, src, .bitcast_result_ptr, result_ptr);
- return expr(mod, scope, .{ .bitcasted_ptr = casted_result_ptr.castTag(.bitcast_result_ptr).? }, params[1]);
- },
- .bitcasted_ptr => |bitcasted_ptr| {
- return mod.failTok(scope, call.builtin_token, "TODO implement @bitCast with result location another @bitCast", .{});
- },
- .block_ptr => |block_ptr| {
- return mod.failTok(scope, call.builtin_token, "TODO implement @bitCast with result location inferred peer types", .{});
- },
- .inferred_ptr => |result_alloc| {
- // TODO here we should be able to resolve the inference; we now have a type for the result.
- return mod.failTok(scope, call.builtin_token, "TODO implement @bitCast with inferred-type result location pointer", .{});
- },
- }
-}
-
-fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
- const tree = scope.tree();
- const builtin_name = tree.tokenSlice(call.builtin_token);
-
- // We handle the different builtins manually because they have different semantics depending
- // on the function. For example, `@as` and others participate in result location semantics,
- // and `@cImport` creates a special scope that collects a .c source code text buffer.
- // Also, some builtins have a variable number of parameters.
-
- if (mem.eql(u8, builtin_name, "@ptrToInt")) {
- return rlWrap(mod, scope, rl, try ptrToInt(mod, scope, call));
- } else if (mem.eql(u8, builtin_name, "@as")) {
- return as(mod, scope, rl, call);
- } else if (mem.eql(u8, builtin_name, "@floatCast")) {
- return simpleCast(mod, scope, rl, call, .floatcast);
- } else if (mem.eql(u8, builtin_name, "@intCast")) {
- return simpleCast(mod, scope, rl, call, .intcast);
- } else if (mem.eql(u8, builtin_name, "@bitCast")) {
- return bitCast(mod, scope, rl, call);
- } else if (mem.eql(u8, builtin_name, "@breakpoint")) {
- const src = tree.token_locs[call.builtin_token].start;
- return rlWrap(mod, scope, rl, try addZIRNoOp(mod, scope, src, .breakpoint));
- } else {
- return mod.failTok(scope, call.builtin_token, "invalid builtin function: '{}'", .{builtin_name});
- }
-}
-
-fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) InnerError!*zir.Inst {
- const tree = scope.tree();
- const lhs = try expr(mod, scope, .none, node.lhs);
-
- const param_nodes = node.params();
- const args = try scope.getGenZIR().arena.alloc(*zir.Inst, param_nodes.len);
- for (param_nodes) |param_node, i| {
- const param_src = tree.token_locs[param_node.firstToken()].start;
- const param_type = try addZIRInst(mod, scope, param_src, zir.Inst.ParamType, .{
- .func = lhs,
- .arg_index = i,
- }, .{});
- args[i] = try expr(mod, scope, .{ .ty = param_type }, param_node);
- }
-
- const src = tree.token_locs[node.lhs.firstToken()].start;
- const result = try addZIRInst(mod, scope, src, zir.Inst.Call, .{
- .func = lhs,
- .args = args,
- }, .{});
- // TODO function call with result location
- return rlWrap(mod, scope, rl, result);
-}
-
-fn unreach(mod: *Module, scope: *Scope, unreach_node: *ast.Node.OneToken) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[unreach_node.token].start;
- return addZIRNoOp(mod, scope, src, .@"unreachable");
-}
-
-fn getSimplePrimitiveValue(name: []const u8) ?TypedValue {
- const simple_types = std.ComptimeStringMap(Value.Tag, .{
- .{ "u8", .u8_type },
- .{ "i8", .i8_type },
- .{ "isize", .isize_type },
- .{ "usize", .usize_type },
- .{ "c_short", .c_short_type },
- .{ "c_ushort", .c_ushort_type },
- .{ "c_int", .c_int_type },
- .{ "c_uint", .c_uint_type },
- .{ "c_long", .c_long_type },
- .{ "c_ulong", .c_ulong_type },
- .{ "c_longlong", .c_longlong_type },
- .{ "c_ulonglong", .c_ulonglong_type },
- .{ "c_longdouble", .c_longdouble_type },
- .{ "f16", .f16_type },
- .{ "f32", .f32_type },
- .{ "f64", .f64_type },
- .{ "f128", .f128_type },
- .{ "c_void", .c_void_type },
- .{ "bool", .bool_type },
- .{ "void", .void_type },
- .{ "type", .type_type },
- .{ "anyerror", .anyerror_type },
- .{ "comptime_int", .comptime_int_type },
- .{ "comptime_float", .comptime_float_type },
- .{ "noreturn", .noreturn_type },
- });
- if (simple_types.get(name)) |tag| {
- return TypedValue{
- .ty = Type.initTag(.type),
- .val = Value.initTag(tag),
- };
- }
- return null;
-}
-
-fn nodeMayNeedMemoryLocation(start_node: *ast.Node) bool {
- var node = start_node;
- while (true) {
- switch (node.tag) {
- .Root,
- .Use,
- .TestDecl,
- .DocComment,
- .SwitchCase,
- .SwitchElse,
- .Else,
- .Payload,
- .PointerPayload,
- .PointerIndexPayload,
- .ContainerField,
- .ErrorTag,
- .FieldInitializer,
- => unreachable,
-
- .Return,
- .Break,
- .Continue,
- .BitNot,
- .BoolNot,
- .VarDecl,
- .Defer,
- .AddressOf,
- .OptionalType,
- .Negation,
- .NegationWrap,
- .Resume,
- .ArrayType,
- .ArrayTypeSentinel,
- .PtrType,
- .SliceType,
- .Suspend,
- .AnyType,
- .ErrorType,
- .FnProto,
- .AnyFrameType,
- .IntegerLiteral,
- .FloatLiteral,
- .EnumLiteral,
- .StringLiteral,
- .MultilineStringLiteral,
- .CharLiteral,
- .BoolLiteral,
- .NullLiteral,
- .UndefinedLiteral,
- .Unreachable,
- .Identifier,
- .ErrorSetDecl,
- .ContainerDecl,
- .Asm,
- .Add,
- .AddWrap,
- .ArrayCat,
- .ArrayMult,
- .Assign,
- .AssignBitAnd,
- .AssignBitOr,
- .AssignBitShiftLeft,
- .AssignBitShiftRight,
- .AssignBitXor,
- .AssignDiv,
- .AssignSub,
- .AssignSubWrap,
- .AssignMod,
- .AssignAdd,
- .AssignAddWrap,
- .AssignMul,
- .AssignMulWrap,
- .BangEqual,
- .BitAnd,
- .BitOr,
- .BitShiftLeft,
- .BitShiftRight,
- .BitXor,
- .BoolAnd,
- .BoolOr,
- .Div,
- .EqualEqual,
- .ErrorUnion,
- .GreaterOrEqual,
- .GreaterThan,
- .LessOrEqual,
- .LessThan,
- .MergeErrorSets,
- .Mod,
- .Mul,
- .MulWrap,
- .Range,
- .Period,
- .Sub,
- .SubWrap,
- .Slice,
- .Deref,
- .ArrayAccess,
- .Block,
- => return false,
-
- // Forward the question to a sub-expression.
- .GroupedExpression => node = node.castTag(.GroupedExpression).?.expr,
- .Try => node = node.castTag(.Try).?.rhs,
- .Await => node = node.castTag(.Await).?.rhs,
- .Catch => node = node.castTag(.Catch).?.rhs,
- .OrElse => node = node.castTag(.OrElse).?.rhs,
- .Comptime => node = node.castTag(.Comptime).?.expr,
- .Nosuspend => node = node.castTag(.Nosuspend).?.expr,
- .UnwrapOptional => node = node.castTag(.UnwrapOptional).?.lhs,
-
- // True because these are exactly the expressions we need memory locations for.
- .ArrayInitializer,
- .ArrayInitializerDot,
- .StructInitializer,
- .StructInitializerDot,
- => return true,
-
- // True because depending on comptime conditions, sub-expressions
- // may be the kind that need memory locations.
- .While,
- .For,
- .Switch,
- .Call,
- .BuiltinCall, // TODO some of these can return false
- .LabeledBlock,
- => return true,
-
- // Depending on AST properties, they may need memory locations.
- .If => return node.castTag(.If).?.@"else" != null,
- }
- }
-}
-
-/// Applies `rl` semantics to `inst`. Expressions which do not do their own handling of
-/// result locations must call this function on their result.
-/// As an example, if the `ResultLoc` is `ptr`, it will write the result to the pointer.
-/// If the `ResultLoc` is `ty`, it will coerce the result to the type.
-fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerError!*zir.Inst {
- switch (rl) {
- .none => return result,
- .discard => {
- // Emit a compile error for discarding error values.
- _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
- return result;
- },
- .ref => {
- // We need a pointer but we have a value.
- return addZIRUnOp(mod, scope, result.src, .ref, result);
- },
- .ty => |ty_inst| return addZIRBinOp(mod, scope, result.src, .as, ty_inst, result),
- .ptr => |ptr_inst| {
- const casted_result = try addZIRInst(mod, scope, result.src, zir.Inst.CoerceToPtrElem, .{
- .ptr = ptr_inst,
- .value = result,
- }, .{});
- _ = try addZIRBinOp(mod, scope, result.src, .store, ptr_inst, casted_result);
- return casted_result;
- },
- .bitcasted_ptr => |bitcasted_ptr| {
- return mod.fail(scope, result.src, "TODO implement rlWrap .bitcasted_ptr", .{});
- },
- .inferred_ptr => |alloc| {
- return mod.fail(scope, result.src, "TODO implement rlWrap .inferred_ptr", .{});
- },
- .block_ptr => |block_ptr| {
- return mod.fail(scope, result.src, "TODO implement rlWrap .block_ptr", .{});
- },
- }
-}
-
-fn rlWrapVoid(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node, result: void) InnerError!*zir.Inst {
- const src = scope.tree().token_locs[node.firstToken()].start;
- const void_inst = try addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.void),
- .val = Value.initTag(.void_value),
- });
- return rlWrap(mod, scope, rl, void_inst);
-}
-
-fn rlWrapPtr(mod: *Module, scope: *Scope, rl: ResultLoc, ptr: *zir.Inst) InnerError!*zir.Inst {
- if (rl == .ref) return ptr;
-
- return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, ptr.src, .deref, ptr));
-}
-
-pub fn addZIRInstSpecial(
- mod: *Module,
- scope: *Scope,
- src: usize,
- comptime T: type,
- positionals: std.meta.fieldInfo(T, "positionals").field_type,
- kw_args: std.meta.fieldInfo(T, "kw_args").field_type,
-) !*T {
- const gen_zir = scope.getGenZIR();
- try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
- const inst = try gen_zir.arena.create(T);
- inst.* = .{
- .base = .{
- .tag = T.base_tag,
- .src = src,
- },
- .positionals = positionals,
- .kw_args = kw_args,
- };
- gen_zir.instructions.appendAssumeCapacity(&inst.base);
- return inst;
-}
-
-pub fn addZIRNoOpT(mod: *Module, scope: *Scope, src: usize, tag: zir.Inst.Tag) !*zir.Inst.NoOp {
- const gen_zir = scope.getGenZIR();
- try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
- const inst = try gen_zir.arena.create(zir.Inst.NoOp);
- inst.* = .{
- .base = .{
- .tag = tag,
- .src = src,
- },
- .positionals = .{},
- .kw_args = .{},
- };
- gen_zir.instructions.appendAssumeCapacity(&inst.base);
- return inst;
-}
-
-pub fn addZIRNoOp(mod: *Module, scope: *Scope, src: usize, tag: zir.Inst.Tag) !*zir.Inst {
- const inst = try addZIRNoOpT(mod, scope, src, tag);
- return &inst.base;
-}
-
-pub fn addZIRUnOp(
- mod: *Module,
- scope: *Scope,
- src: usize,
- tag: zir.Inst.Tag,
- operand: *zir.Inst,
-) !*zir.Inst {
- const gen_zir = scope.getGenZIR();
- try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
- const inst = try gen_zir.arena.create(zir.Inst.UnOp);
- inst.* = .{
- .base = .{
- .tag = tag,
- .src = src,
- },
- .positionals = .{
- .operand = operand,
- },
- .kw_args = .{},
- };
- gen_zir.instructions.appendAssumeCapacity(&inst.base);
- return &inst.base;
-}
-
-pub fn addZIRBinOp(
- mod: *Module,
- scope: *Scope,
- src: usize,
- tag: zir.Inst.Tag,
- lhs: *zir.Inst,
- rhs: *zir.Inst,
-) !*zir.Inst {
- const gen_zir = scope.getGenZIR();
- try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
- const inst = try gen_zir.arena.create(zir.Inst.BinOp);
- inst.* = .{
- .base = .{
- .tag = tag,
- .src = src,
- },
- .positionals = .{
- .lhs = lhs,
- .rhs = rhs,
- },
- .kw_args = .{},
- };
- gen_zir.instructions.appendAssumeCapacity(&inst.base);
- return &inst.base;
-}
-
-pub fn addZIRInstBlock(
- mod: *Module,
- scope: *Scope,
- src: usize,
- tag: zir.Inst.Tag,
- body: zir.Module.Body,
-) !*zir.Inst.Block {
- const gen_zir = scope.getGenZIR();
- try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
- const inst = try gen_zir.arena.create(zir.Inst.Block);
- inst.* = .{
- .base = .{
- .tag = tag,
- .src = src,
- },
- .positionals = .{
- .body = body,
- },
- .kw_args = .{},
- };
- gen_zir.instructions.appendAssumeCapacity(&inst.base);
- return inst;
-}
-
-pub fn addZIRInst(
- mod: *Module,
- scope: *Scope,
- src: usize,
- comptime T: type,
- positionals: std.meta.fieldInfo(T, "positionals").field_type,
- kw_args: std.meta.fieldInfo(T, "kw_args").field_type,
-) !*zir.Inst {
- const inst_special = try addZIRInstSpecial(mod, scope, src, T, positionals, kw_args);
- return &inst_special.base;
-}
-
-/// TODO The existence of this function is a workaround for a bug in stage1.
-pub fn addZIRInstConst(mod: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*zir.Inst {
- const P = std.meta.fieldInfo(zir.Inst.Const, "positionals").field_type;
- return addZIRInst(mod, scope, src, zir.Inst.Const, P{ .typed_value = typed_value }, .{});
-}
-
-/// TODO The existence of this function is a workaround for a bug in stage1.
-pub fn addZIRInstLoop(mod: *Module, scope: *Scope, src: usize, body: zir.Module.Body) !*zir.Inst.Loop {
- const P = std.meta.fieldInfo(zir.Inst.Loop, "positionals").field_type;
- return addZIRInstSpecial(mod, scope, src, zir.Inst.Loop, P{ .body = body }, .{});
-}
diff --git a/src-self-hosted/clang.zig b/src-self-hosted/clang.zig
deleted file mode 100644
index 255182908499a3b7c55484392bc7cf7a4b77965b..0000000000000000000000000000000000000000
--- a/src-self-hosted/clang.zig
+++ /dev/null
@@ -1,1197 +0,0 @@
-const builtin = @import("builtin");
-
-pub const struct_ZigClangConditionalOperator = @Type(.Opaque);
-pub const struct_ZigClangBinaryConditionalOperator = @Type(.Opaque);
-pub const struct_ZigClangAbstractConditionalOperator = @Type(.Opaque);
-pub const struct_ZigClangAPInt = @Type(.Opaque);
-pub const struct_ZigClangAPSInt = @Type(.Opaque);
-pub const struct_ZigClangAPFloat = @Type(.Opaque);
-pub const struct_ZigClangASTContext = @Type(.Opaque);
-pub const struct_ZigClangASTUnit = @Type(.Opaque);
-pub const struct_ZigClangArraySubscriptExpr = @Type(.Opaque);
-pub const struct_ZigClangArrayType = @Type(.Opaque);
-pub const struct_ZigClangAttributedType = @Type(.Opaque);
-pub const struct_ZigClangBinaryOperator = @Type(.Opaque);
-pub const struct_ZigClangBreakStmt = @Type(.Opaque);
-pub const struct_ZigClangBuiltinType = @Type(.Opaque);
-pub const struct_ZigClangCStyleCastExpr = @Type(.Opaque);
-pub const struct_ZigClangCallExpr = @Type(.Opaque);
-pub const struct_ZigClangCaseStmt = @Type(.Opaque);
-pub const struct_ZigClangCompoundAssignOperator = @Type(.Opaque);
-pub const struct_ZigClangCompoundStmt = @Type(.Opaque);
-pub const struct_ZigClangConstantArrayType = @Type(.Opaque);
-pub const struct_ZigClangContinueStmt = @Type(.Opaque);
-pub const struct_ZigClangDecayedType = @Type(.Opaque);
-pub const ZigClangDecl = @Type(.Opaque);
-pub const struct_ZigClangDeclRefExpr = @Type(.Opaque);
-pub const struct_ZigClangDeclStmt = @Type(.Opaque);
-pub const struct_ZigClangDefaultStmt = @Type(.Opaque);
-pub const struct_ZigClangDiagnosticOptions = @Type(.Opaque);
-pub const struct_ZigClangDiagnosticsEngine = @Type(.Opaque);
-pub const struct_ZigClangDoStmt = @Type(.Opaque);
-pub const struct_ZigClangElaboratedType = @Type(.Opaque);
-pub const struct_ZigClangEnumConstantDecl = @Type(.Opaque);
-pub const struct_ZigClangEnumDecl = @Type(.Opaque);
-pub const struct_ZigClangEnumType = @Type(.Opaque);
-pub const struct_ZigClangExpr = @Type(.Opaque);
-pub const struct_ZigClangFieldDecl = @Type(.Opaque);
-pub const struct_ZigClangFileID = @Type(.Opaque);
-pub const struct_ZigClangForStmt = @Type(.Opaque);
-pub const struct_ZigClangFullSourceLoc = @Type(.Opaque);
-pub const struct_ZigClangFunctionDecl = @Type(.Opaque);
-pub const struct_ZigClangFunctionProtoType = @Type(.Opaque);
-pub const struct_ZigClangIfStmt = @Type(.Opaque);
-pub const struct_ZigClangImplicitCastExpr = @Type(.Opaque);
-pub const struct_ZigClangIncompleteArrayType = @Type(.Opaque);
-pub const struct_ZigClangIntegerLiteral = @Type(.Opaque);
-pub const struct_ZigClangMacroDefinitionRecord = @Type(.Opaque);
-pub const struct_ZigClangMacroExpansion = @Type(.Opaque);
-pub const struct_ZigClangMacroQualifiedType = @Type(.Opaque);
-pub const struct_ZigClangMemberExpr = @Type(.Opaque);
-pub const struct_ZigClangNamedDecl = @Type(.Opaque);
-pub const struct_ZigClangNone = @Type(.Opaque);
-pub const struct_ZigClangOpaqueValueExpr = @Type(.Opaque);
-pub const struct_ZigClangPCHContainerOperations = @Type(.Opaque);
-pub const struct_ZigClangParenExpr = @Type(.Opaque);
-pub const struct_ZigClangParenType = @Type(.Opaque);
-pub const struct_ZigClangParmVarDecl = @Type(.Opaque);
-pub const struct_ZigClangPointerType = @Type(.Opaque);
-pub const struct_ZigClangPreprocessedEntity = @Type(.Opaque);
-pub const struct_ZigClangRecordDecl = @Type(.Opaque);
-pub const struct_ZigClangRecordType = @Type(.Opaque);
-pub const struct_ZigClangReturnStmt = @Type(.Opaque);
-pub const struct_ZigClangSkipFunctionBodiesScope = @Type(.Opaque);
-pub const struct_ZigClangSourceManager = @Type(.Opaque);
-pub const struct_ZigClangSourceRange = @Type(.Opaque);
-pub const ZigClangStmt = @Type(.Opaque);
-pub const struct_ZigClangStringLiteral = @Type(.Opaque);
-pub const struct_ZigClangStringRef = @Type(.Opaque);
-pub const struct_ZigClangSwitchStmt = @Type(.Opaque);
-pub const struct_ZigClangTagDecl = @Type(.Opaque);
-pub const struct_ZigClangType = @Type(.Opaque);
-pub const struct_ZigClangTypedefNameDecl = @Type(.Opaque);
-pub const struct_ZigClangTypedefType = @Type(.Opaque);
-pub const struct_ZigClangUnaryExprOrTypeTraitExpr = @Type(.Opaque);
-pub const struct_ZigClangUnaryOperator = @Type(.Opaque);
-pub const struct_ZigClangValueDecl = @Type(.Opaque);
-pub const struct_ZigClangVarDecl = @Type(.Opaque);
-pub const struct_ZigClangWhileStmt = @Type(.Opaque);
-pub const struct_ZigClangFunctionType = @Type(.Opaque);
-pub const struct_ZigClangPredefinedExpr = @Type(.Opaque);
-pub const struct_ZigClangInitListExpr = @Type(.Opaque);
-pub const ZigClangPreprocessingRecord = @Type(.Opaque);
-pub const ZigClangFloatingLiteral = @Type(.Opaque);
-pub const ZigClangConstantExpr = @Type(.Opaque);
-pub const ZigClangCharacterLiteral = @Type(.Opaque);
-pub const ZigClangStmtExpr = @Type(.Opaque);
-
-pub const ZigClangBO = extern enum {
- PtrMemD,
- PtrMemI,
- Mul,
- Div,
- Rem,
- Add,
- Sub,
- Shl,
- Shr,
- Cmp,
- LT,
- GT,
- LE,
- GE,
- EQ,
- NE,
- And,
- Xor,
- Or,
- LAnd,
- LOr,
- Assign,
- MulAssign,
- DivAssign,
- RemAssign,
- AddAssign,
- SubAssign,
- ShlAssign,
- ShrAssign,
- AndAssign,
- XorAssign,
- OrAssign,
- Comma,
-};
-
-pub const ZigClangUO = extern enum {
- PostInc,
- PostDec,
- PreInc,
- PreDec,
- AddrOf,
- Deref,
- Plus,
- Minus,
- Not,
- LNot,
- Real,
- Imag,
- Extension,
- Coawait,
-};
-
-pub const ZigClangTypeClass = extern enum {
- Adjusted,
- Decayed,
- ConstantArray,
- DependentSizedArray,
- IncompleteArray,
- VariableArray,
- Atomic,
- Attributed,
- BlockPointer,
- Builtin,
- Complex,
- Decltype,
- Auto,
- DeducedTemplateSpecialization,
- DependentAddressSpace,
- DependentName,
- DependentSizedExtVector,
- DependentTemplateSpecialization,
- DependentVector,
- Elaborated,
- FunctionNoProto,
- FunctionProto,
- InjectedClassName,
- MacroQualified,
- MemberPointer,
- ObjCObjectPointer,
- ObjCObject,
- ObjCInterface,
- ObjCTypeParam,
- PackExpansion,
- Paren,
- Pipe,
- Pointer,
- LValueReference,
- RValueReference,
- SubstTemplateTypeParmPack,
- SubstTemplateTypeParm,
- Enum,
- Record,
- TemplateSpecialization,
- TemplateTypeParm,
- TypeOfExpr,
- TypeOf,
- Typedef,
- UnaryTransform,
- UnresolvedUsing,
- Vector,
- ExtVector,
-};
-
-const ZigClangStmtClass = extern enum {
- NoStmtClass,
- GCCAsmStmtClass,
- MSAsmStmtClass,
- BreakStmtClass,
- CXXCatchStmtClass,
- CXXForRangeStmtClass,
- CXXTryStmtClass,
- CapturedStmtClass,
- CompoundStmtClass,
- ContinueStmtClass,
- CoreturnStmtClass,
- CoroutineBodyStmtClass,
- DeclStmtClass,
- DoStmtClass,
- ForStmtClass,
- GotoStmtClass,
- IfStmtClass,
- IndirectGotoStmtClass,
- MSDependentExistsStmtClass,
- NullStmtClass,
- OMPAtomicDirectiveClass,
- OMPBarrierDirectiveClass,
- OMPCancelDirectiveClass,
- OMPCancellationPointDirectiveClass,
- OMPCriticalDirectiveClass,
- OMPFlushDirectiveClass,
- OMPDistributeDirectiveClass,
- OMPDistributeParallelForDirectiveClass,
- OMPDistributeParallelForSimdDirectiveClass,
- OMPDistributeSimdDirectiveClass,
- OMPForDirectiveClass,
- OMPForSimdDirectiveClass,
- OMPMasterTaskLoopDirectiveClass,
- OMPMasterTaskLoopSimdDirectiveClass,
- OMPParallelForDirectiveClass,
- OMPParallelForSimdDirectiveClass,
- OMPParallelMasterTaskLoopDirectiveClass,
- OMPParallelMasterTaskLoopSimdDirectiveClass,
- OMPSimdDirectiveClass,
- OMPTargetParallelForSimdDirectiveClass,
- OMPTargetSimdDirectiveClass,
- OMPTargetTeamsDistributeDirectiveClass,
- OMPTargetTeamsDistributeParallelForDirectiveClass,
- OMPTargetTeamsDistributeParallelForSimdDirectiveClass,
- OMPTargetTeamsDistributeSimdDirectiveClass,
- OMPTaskLoopDirectiveClass,
- OMPTaskLoopSimdDirectiveClass,
- OMPTeamsDistributeDirectiveClass,
- OMPTeamsDistributeParallelForDirectiveClass,
- OMPTeamsDistributeParallelForSimdDirectiveClass,
- OMPTeamsDistributeSimdDirectiveClass,
- OMPMasterDirectiveClass,
- OMPOrderedDirectiveClass,
- OMPParallelDirectiveClass,
- OMPParallelMasterDirectiveClass,
- OMPParallelSectionsDirectiveClass,
- OMPSectionDirectiveClass,
- OMPSectionsDirectiveClass,
- OMPSingleDirectiveClass,
- OMPTargetDataDirectiveClass,
- OMPTargetDirectiveClass,
- OMPTargetEnterDataDirectiveClass,
- OMPTargetExitDataDirectiveClass,
- OMPTargetParallelDirectiveClass,
- OMPTargetParallelForDirectiveClass,
- OMPTargetTeamsDirectiveClass,
- OMPTargetUpdateDirectiveClass,
- OMPTaskDirectiveClass,
- OMPTaskgroupDirectiveClass,
- OMPTaskwaitDirectiveClass,
- OMPTaskyieldDirectiveClass,
- OMPTeamsDirectiveClass,
- ObjCAtCatchStmtClass,
- ObjCAtFinallyStmtClass,
- ObjCAtSynchronizedStmtClass,
- ObjCAtThrowStmtClass,
- ObjCAtTryStmtClass,
- ObjCAutoreleasePoolStmtClass,
- ObjCForCollectionStmtClass,
- ReturnStmtClass,
- SEHExceptStmtClass,
- SEHFinallyStmtClass,
- SEHLeaveStmtClass,
- SEHTryStmtClass,
- CaseStmtClass,
- DefaultStmtClass,
- SwitchStmtClass,
- AttributedStmtClass,
- BinaryConditionalOperatorClass,
- ConditionalOperatorClass,
- AddrLabelExprClass,
- ArrayInitIndexExprClass,
- ArrayInitLoopExprClass,
- ArraySubscriptExprClass,
- ArrayTypeTraitExprClass,
- AsTypeExprClass,
- AtomicExprClass,
- BinaryOperatorClass,
- CompoundAssignOperatorClass,
- BlockExprClass,
- CXXBindTemporaryExprClass,
- CXXBoolLiteralExprClass,
- CXXConstructExprClass,
- CXXTemporaryObjectExprClass,
- CXXDefaultArgExprClass,
- CXXDefaultInitExprClass,
- CXXDeleteExprClass,
- CXXDependentScopeMemberExprClass,
- CXXFoldExprClass,
- CXXInheritedCtorInitExprClass,
- CXXNewExprClass,
- CXXNoexceptExprClass,
- CXXNullPtrLiteralExprClass,
- CXXPseudoDestructorExprClass,
- CXXRewrittenBinaryOperatorClass,
- CXXScalarValueInitExprClass,
- CXXStdInitializerListExprClass,
- CXXThisExprClass,
- CXXThrowExprClass,
- CXXTypeidExprClass,
- CXXUnresolvedConstructExprClass,
- CXXUuidofExprClass,
- CallExprClass,
- CUDAKernelCallExprClass,
- CXXMemberCallExprClass,
- CXXOperatorCallExprClass,
- UserDefinedLiteralClass,
- BuiltinBitCastExprClass,
- CStyleCastExprClass,
- CXXFunctionalCastExprClass,
- CXXConstCastExprClass,
- CXXDynamicCastExprClass,
- CXXReinterpretCastExprClass,
- CXXStaticCastExprClass,
- ObjCBridgedCastExprClass,
- ImplicitCastExprClass,
- CharacterLiteralClass,
- ChooseExprClass,
- CompoundLiteralExprClass,
- ConceptSpecializationExprClass,
- ConvertVectorExprClass,
- CoawaitExprClass,
- CoyieldExprClass,
- DeclRefExprClass,
- DependentCoawaitExprClass,
- DependentScopeDeclRefExprClass,
- DesignatedInitExprClass,
- DesignatedInitUpdateExprClass,
- ExpressionTraitExprClass,
- ExtVectorElementExprClass,
- FixedPointLiteralClass,
- FloatingLiteralClass,
- ConstantExprClass,
- ExprWithCleanupsClass,
- FunctionParmPackExprClass,
- GNUNullExprClass,
- GenericSelectionExprClass,
- ImaginaryLiteralClass,
- ImplicitValueInitExprClass,
- InitListExprClass,
- IntegerLiteralClass,
- LambdaExprClass,
- MSPropertyRefExprClass,
- MSPropertySubscriptExprClass,
- MaterializeTemporaryExprClass,
- MemberExprClass,
- NoInitExprClass,
- OMPArraySectionExprClass,
- ObjCArrayLiteralClass,
- ObjCAvailabilityCheckExprClass,
- ObjCBoolLiteralExprClass,
- ObjCBoxedExprClass,
- ObjCDictionaryLiteralClass,
- ObjCEncodeExprClass,
- ObjCIndirectCopyRestoreExprClass,
- ObjCIsaExprClass,
- ObjCIvarRefExprClass,
- ObjCMessageExprClass,
- ObjCPropertyRefExprClass,
- ObjCProtocolExprClass,
- ObjCSelectorExprClass,
- ObjCStringLiteralClass,
- ObjCSubscriptRefExprClass,
- OffsetOfExprClass,
- OpaqueValueExprClass,
- UnresolvedLookupExprClass,
- UnresolvedMemberExprClass,
- PackExpansionExprClass,
- ParenExprClass,
- ParenListExprClass,
- PredefinedExprClass,
- PseudoObjectExprClass,
- RequiresExprClass,
- ShuffleVectorExprClass,
- SizeOfPackExprClass,
- SourceLocExprClass,
- StmtExprClass,
- StringLiteralClass,
- SubstNonTypeTemplateParmExprClass,
- SubstNonTypeTemplateParmPackExprClass,
- TypeTraitExprClass,
- TypoExprClass,
- UnaryExprOrTypeTraitExprClass,
- UnaryOperatorClass,
- VAArgExprClass,
- LabelStmtClass,
- WhileStmtClass,
-};
-
-pub const ZigClangCK = extern enum {
- Dependent,
- BitCast,
- LValueBitCast,
- LValueToRValueBitCast,
- LValueToRValue,
- NoOp,
- BaseToDerived,
- DerivedToBase,
- UncheckedDerivedToBase,
- Dynamic,
- ToUnion,
- ArrayToPointerDecay,
- FunctionToPointerDecay,
- NullToPointer,
- NullToMemberPointer,
- BaseToDerivedMemberPointer,
- DerivedToBaseMemberPointer,
- MemberPointerToBoolean,
- ReinterpretMemberPointer,
- UserDefinedConversion,
- ConstructorConversion,
- IntegralToPointer,
- PointerToIntegral,
- PointerToBoolean,
- ToVoid,
- VectorSplat,
- IntegralCast,
- IntegralToBoolean,
- IntegralToFloating,
- FixedPointCast,
- FixedPointToIntegral,
- IntegralToFixedPoint,
- FixedPointToBoolean,
- FloatingToIntegral,
- FloatingToBoolean,
- BooleanToSignedIntegral,
- FloatingCast,
- CPointerToObjCPointerCast,
- BlockPointerToObjCPointerCast,
- AnyPointerToBlockPointerCast,
- ObjCObjectLValueCast,
- FloatingRealToComplex,
- FloatingComplexToReal,
- FloatingComplexToBoolean,
- FloatingComplexCast,
- FloatingComplexToIntegralComplex,
- IntegralRealToComplex,
- IntegralComplexToReal,
- IntegralComplexToBoolean,
- IntegralComplexCast,
- IntegralComplexToFloatingComplex,
- ARCProduceObject,
- ARCConsumeObject,
- ARCReclaimReturnedObject,
- ARCExtendBlockObject,
- AtomicToNonAtomic,
- NonAtomicToAtomic,
- CopyAndAutoreleaseBlockObject,
- BuiltinFnToFnPtr,
- ZeroToOCLOpaqueType,
- AddressSpaceConversion,
- IntToOCLSampler,
-};
-
-pub const ZigClangAPValueKind = extern enum {
- None,
- Indeterminate,
- Int,
- Float,
- FixedPoint,
- ComplexInt,
- ComplexFloat,
- LValue,
- Vector,
- Array,
- Struct,
- Union,
- MemberPointer,
- AddrLabelDiff,
-};
-
-pub const ZigClangDeclKind = extern enum {
- AccessSpec,
- Block,
- Captured,
- ClassScopeFunctionSpecialization,
- Empty,
- Export,
- ExternCContext,
- FileScopeAsm,
- Friend,
- FriendTemplate,
- Import,
- LifetimeExtendedTemporary,
- LinkageSpec,
- Label,
- Namespace,
- NamespaceAlias,
- ObjCCompatibleAlias,
- ObjCCategory,
- ObjCCategoryImpl,
- ObjCImplementation,
- ObjCInterface,
- ObjCProtocol,
- ObjCMethod,
- ObjCProperty,
- BuiltinTemplate,
- Concept,
- ClassTemplate,
- FunctionTemplate,
- TypeAliasTemplate,
- VarTemplate,
- TemplateTemplateParm,
- Enum,
- Record,
- CXXRecord,
- ClassTemplateSpecialization,
- ClassTemplatePartialSpecialization,
- TemplateTypeParm,
- ObjCTypeParam,
- TypeAlias,
- Typedef,
- UnresolvedUsingTypename,
- Using,
- UsingDirective,
- UsingPack,
- UsingShadow,
- ConstructorUsingShadow,
- Binding,
- Field,
- ObjCAtDefsField,
- ObjCIvar,
- Function,
- CXXDeductionGuide,
- CXXMethod,
- CXXConstructor,
- CXXConversion,
- CXXDestructor,
- MSProperty,
- NonTypeTemplateParm,
- Var,
- Decomposition,
- ImplicitParam,
- OMPCapturedExpr,
- ParmVar,
- VarTemplateSpecialization,
- VarTemplatePartialSpecialization,
- EnumConstant,
- IndirectField,
- OMPDeclareMapper,
- OMPDeclareReduction,
- UnresolvedUsingValue,
- OMPAllocate,
- OMPRequires,
- OMPThreadPrivate,
- ObjCPropertyImpl,
- PragmaComment,
- PragmaDetectMismatch,
- RequiresExprBody,
- StaticAssert,
- TranslationUnit,
-};
-
-pub const ZigClangBuiltinTypeKind = extern enum {
- OCLImage1dRO,
- OCLImage1dArrayRO,
- OCLImage1dBufferRO,
- OCLImage2dRO,
- OCLImage2dArrayRO,
- OCLImage2dDepthRO,
- OCLImage2dArrayDepthRO,
- OCLImage2dMSAARO,
- OCLImage2dArrayMSAARO,
- OCLImage2dMSAADepthRO,
- OCLImage2dArrayMSAADepthRO,
- OCLImage3dRO,
- OCLImage1dWO,
- OCLImage1dArrayWO,
- OCLImage1dBufferWO,
- OCLImage2dWO,
- OCLImage2dArrayWO,
- OCLImage2dDepthWO,
- OCLImage2dArrayDepthWO,
- OCLImage2dMSAAWO,
- OCLImage2dArrayMSAAWO,
- OCLImage2dMSAADepthWO,
- OCLImage2dArrayMSAADepthWO,
- OCLImage3dWO,
- OCLImage1dRW,
- OCLImage1dArrayRW,
- OCLImage1dBufferRW,
- OCLImage2dRW,
- OCLImage2dArrayRW,
- OCLImage2dDepthRW,
- OCLImage2dArrayDepthRW,
- OCLImage2dMSAARW,
- OCLImage2dArrayMSAARW,
- OCLImage2dMSAADepthRW,
- OCLImage2dArrayMSAADepthRW,
- OCLImage3dRW,
- OCLIntelSubgroupAVCMcePayload,
- OCLIntelSubgroupAVCImePayload,
- OCLIntelSubgroupAVCRefPayload,
- OCLIntelSubgroupAVCSicPayload,
- OCLIntelSubgroupAVCMceResult,
- OCLIntelSubgroupAVCImeResult,
- OCLIntelSubgroupAVCRefResult,
- OCLIntelSubgroupAVCSicResult,
- OCLIntelSubgroupAVCImeResultSingleRefStreamout,
- OCLIntelSubgroupAVCImeResultDualRefStreamout,
- OCLIntelSubgroupAVCImeSingleRefStreamin,
- OCLIntelSubgroupAVCImeDualRefStreamin,
- SveInt8,
- SveInt16,
- SveInt32,
- SveInt64,
- SveUint8,
- SveUint16,
- SveUint32,
- SveUint64,
- SveFloat16,
- SveFloat32,
- SveFloat64,
- SveBool,
- Void,
- Bool,
- Char_U,
- UChar,
- WChar_U,
- Char8,
- Char16,
- Char32,
- UShort,
- UInt,
- ULong,
- ULongLong,
- UInt128,
- Char_S,
- SChar,
- WChar_S,
- Short,
- Int,
- Long,
- LongLong,
- Int128,
- ShortAccum,
- Accum,
- LongAccum,
- UShortAccum,
- UAccum,
- ULongAccum,
- ShortFract,
- Fract,
- LongFract,
- UShortFract,
- UFract,
- ULongFract,
- SatShortAccum,
- SatAccum,
- SatLongAccum,
- SatUShortAccum,
- SatUAccum,
- SatULongAccum,
- SatShortFract,
- SatFract,
- SatLongFract,
- SatUShortFract,
- SatUFract,
- SatULongFract,
- Half,
- Float,
- Double,
- LongDouble,
- Float16,
- Float128,
- NullPtr,
- ObjCId,
- ObjCClass,
- ObjCSel,
- OCLSampler,
- OCLEvent,
- OCLClkEvent,
- OCLQueue,
- OCLReserveID,
- Dependent,
- Overload,
- BoundMember,
- PseudoObject,
- UnknownAny,
- BuiltinFn,
- ARCUnbridgedCast,
- OMPArraySection,
-};
-
-pub const ZigClangCallingConv = extern enum {
- C,
- X86StdCall,
- X86FastCall,
- X86ThisCall,
- X86VectorCall,
- X86Pascal,
- Win64,
- X86_64SysV,
- X86RegCall,
- AAPCS,
- AAPCS_VFP,
- IntelOclBicc,
- SpirFunction,
- OpenCLKernel,
- Swift,
- PreserveMost,
- PreserveAll,
- AArch64VectorCall,
-};
-
-pub const ZigClangStorageClass = extern enum {
- None,
- Extern,
- Static,
- PrivateExtern,
- Auto,
- Register,
-};
-
-pub const ZigClangAPFloat_roundingMode = extern enum {
- NearestTiesToEven,
- TowardPositive,
- TowardNegative,
- TowardZero,
- NearestTiesToAway,
-};
-
-pub const ZigClangStringLiteral_StringKind = extern enum {
- Ascii,
- Wide,
- UTF8,
- UTF16,
- UTF32,
-};
-
-pub const ZigClangCharacterLiteral_CharacterKind = extern enum {
- Ascii,
- Wide,
- UTF8,
- UTF16,
- UTF32,
-};
-
-pub const ZigClangRecordDecl_field_iterator = extern struct {
- opaque: *c_void,
-};
-
-pub const ZigClangEnumDecl_enumerator_iterator = extern struct {
- opaque: *c_void,
-};
-
-pub const ZigClangPreprocessingRecord_iterator = extern struct {
- I: c_int,
- Self: *ZigClangPreprocessingRecord,
-};
-
-pub const ZigClangPreprocessedEntity_EntityKind = extern enum {
- InvalidKind,
- MacroExpansionKind,
- MacroDefinitionKind,
- InclusionDirectiveKind,
-};
-
-pub const ZigClangExpr_ConstExprUsage = extern enum {
- EvaluateForCodeGen,
- EvaluateForMangling,
-};
-
-pub const ZigClangUnaryExprOrTypeTrait_Kind = extern enum {
- SizeOf,
- AlignOf,
- VecStep,
- OpenMPRequiredSimdAlign,
- PreferredAlignOf,
-};
-
-pub extern fn ZigClangSourceManager_getSpellingLoc(self: ?*const struct_ZigClangSourceManager, Loc: struct_ZigClangSourceLocation) struct_ZigClangSourceLocation;
-pub extern fn ZigClangSourceManager_getFilename(self: *const struct_ZigClangSourceManager, SpellingLoc: struct_ZigClangSourceLocation) ?[*:0]const u8;
-pub extern fn ZigClangSourceManager_getSpellingLineNumber(self: ?*const struct_ZigClangSourceManager, Loc: struct_ZigClangSourceLocation) c_uint;
-pub extern fn ZigClangSourceManager_getSpellingColumnNumber(self: ?*const struct_ZigClangSourceManager, Loc: struct_ZigClangSourceLocation) c_uint;
-pub extern fn ZigClangSourceManager_getCharacterData(self: ?*const struct_ZigClangSourceManager, SL: struct_ZigClangSourceLocation) [*:0]const u8;
-pub extern fn ZigClangASTContext_getPointerType(self: ?*const struct_ZigClangASTContext, T: struct_ZigClangQualType) struct_ZigClangQualType;
-pub extern fn ZigClangASTUnit_getASTContext(self: ?*struct_ZigClangASTUnit) ?*struct_ZigClangASTContext;
-pub extern fn ZigClangASTUnit_getSourceManager(self: *struct_ZigClangASTUnit) *struct_ZigClangSourceManager;
-pub extern fn ZigClangASTUnit_visitLocalTopLevelDecls(self: *struct_ZigClangASTUnit, context: ?*c_void, Fn: ?fn (?*c_void, *const ZigClangDecl) callconv(.C) bool) bool;
-pub extern fn ZigClangRecordType_getDecl(record_ty: ?*const struct_ZigClangRecordType) *const struct_ZigClangRecordDecl;
-pub extern fn ZigClangTagDecl_isThisDeclarationADefinition(self: *const ZigClangTagDecl) bool;
-pub extern fn ZigClangEnumType_getDecl(record_ty: ?*const struct_ZigClangEnumType) *const struct_ZigClangEnumDecl;
-pub extern fn ZigClangRecordDecl_getCanonicalDecl(record_decl: ?*const struct_ZigClangRecordDecl) ?*const struct_ZigClangTagDecl;
-pub extern fn ZigClangFieldDecl_getCanonicalDecl(field_decl: ?*const struct_ZigClangFieldDecl) ?*const struct_ZigClangFieldDecl;
-pub extern fn ZigClangFieldDecl_getAlignedAttribute(field_decl: ?*const struct_ZigClangFieldDecl, *const ZigClangASTContext) c_uint;
-pub extern fn ZigClangEnumDecl_getCanonicalDecl(self: ?*const struct_ZigClangEnumDecl) ?*const struct_ZigClangTagDecl;
-pub extern fn ZigClangTypedefNameDecl_getCanonicalDecl(self: ?*const struct_ZigClangTypedefNameDecl) ?*const struct_ZigClangTypedefNameDecl;
-pub extern fn ZigClangFunctionDecl_getCanonicalDecl(self: ?*const struct_ZigClangFunctionDecl) ?*const struct_ZigClangFunctionDecl;
-pub extern fn ZigClangParmVarDecl_getOriginalType(self: ?*const struct_ZigClangParmVarDecl) struct_ZigClangQualType;
-pub extern fn ZigClangVarDecl_getCanonicalDecl(self: ?*const struct_ZigClangVarDecl) ?*const struct_ZigClangVarDecl;
-pub extern fn ZigClangVarDecl_getSectionAttribute(self: *const ZigClangVarDecl, len: *usize) ?[*]const u8;
-pub extern fn ZigClangFunctionDecl_getAlignedAttribute(self: *const ZigClangFunctionDecl, *const ZigClangASTContext) c_uint;
-pub extern fn ZigClangVarDecl_getAlignedAttribute(self: *const ZigClangVarDecl, *const ZigClangASTContext) c_uint;
-pub extern fn ZigClangRecordDecl_getPackedAttribute(self: ?*const struct_ZigClangRecordDecl) bool;
-pub extern fn ZigClangRecordDecl_getDefinition(self: ?*const struct_ZigClangRecordDecl) ?*const struct_ZigClangRecordDecl;
-pub extern fn ZigClangEnumDecl_getDefinition(self: ?*const struct_ZigClangEnumDecl) ?*const struct_ZigClangEnumDecl;
-pub extern fn ZigClangRecordDecl_getLocation(self: ?*const struct_ZigClangRecordDecl) struct_ZigClangSourceLocation;
-pub extern fn ZigClangEnumDecl_getLocation(self: ?*const struct_ZigClangEnumDecl) struct_ZigClangSourceLocation;
-pub extern fn ZigClangTypedefNameDecl_getLocation(self: ?*const struct_ZigClangTypedefNameDecl) struct_ZigClangSourceLocation;
-pub extern fn ZigClangDecl_getLocation(self: *const ZigClangDecl) ZigClangSourceLocation;
-pub extern fn ZigClangRecordDecl_isUnion(record_decl: ?*const struct_ZigClangRecordDecl) bool;
-pub extern fn ZigClangRecordDecl_isStruct(record_decl: ?*const struct_ZigClangRecordDecl) bool;
-pub extern fn ZigClangRecordDecl_isAnonymousStructOrUnion(record_decl: ?*const struct_ZigClangRecordDecl) bool;
-pub extern fn ZigClangRecordDecl_field_begin(*const struct_ZigClangRecordDecl) ZigClangRecordDecl_field_iterator;
-pub extern fn ZigClangRecordDecl_field_end(*const struct_ZigClangRecordDecl) ZigClangRecordDecl_field_iterator;
-pub extern fn ZigClangRecordDecl_field_iterator_next(ZigClangRecordDecl_field_iterator) ZigClangRecordDecl_field_iterator;
-pub extern fn ZigClangRecordDecl_field_iterator_deref(ZigClangRecordDecl_field_iterator) *const struct_ZigClangFieldDecl;
-pub extern fn ZigClangRecordDecl_field_iterator_neq(ZigClangRecordDecl_field_iterator, ZigClangRecordDecl_field_iterator) bool;
-pub extern fn ZigClangEnumDecl_getIntegerType(self: ?*const struct_ZigClangEnumDecl) struct_ZigClangQualType;
-pub extern fn ZigClangEnumDecl_enumerator_begin(*const ZigClangEnumDecl) ZigClangEnumDecl_enumerator_iterator;
-pub extern fn ZigClangEnumDecl_enumerator_end(*const ZigClangEnumDecl) ZigClangEnumDecl_enumerator_iterator;
-pub extern fn ZigClangEnumDecl_enumerator_iterator_next(ZigClangEnumDecl_enumerator_iterator) ZigClangEnumDecl_enumerator_iterator;
-pub extern fn ZigClangEnumDecl_enumerator_iterator_deref(ZigClangEnumDecl_enumerator_iterator) *const ZigClangEnumConstantDecl;
-pub extern fn ZigClangEnumDecl_enumerator_iterator_neq(ZigClangEnumDecl_enumerator_iterator, ZigClangEnumDecl_enumerator_iterator) bool;
-pub extern fn ZigClangDecl_castToNamedDecl(decl: *const ZigClangDecl) ?*const ZigClangNamedDecl;
-pub extern fn ZigClangNamedDecl_getName_bytes_begin(decl: ?*const struct_ZigClangNamedDecl) [*:0]const u8;
-pub extern fn ZigClangSourceLocation_eq(a: struct_ZigClangSourceLocation, b: struct_ZigClangSourceLocation) bool;
-pub extern fn ZigClangTypedefType_getDecl(self: ?*const struct_ZigClangTypedefType) *const struct_ZigClangTypedefNameDecl;
-pub extern fn ZigClangTypedefNameDecl_getUnderlyingType(self: ?*const struct_ZigClangTypedefNameDecl) struct_ZigClangQualType;
-pub extern fn ZigClangQualType_getCanonicalType(self: struct_ZigClangQualType) struct_ZigClangQualType;
-pub extern fn ZigClangQualType_getTypeClass(self: struct_ZigClangQualType) ZigClangTypeClass;
-pub extern fn ZigClangQualType_getTypePtr(self: struct_ZigClangQualType) *const struct_ZigClangType;
-pub extern fn ZigClangQualType_addConst(self: *struct_ZigClangQualType) void;
-pub extern fn ZigClangQualType_eq(self: struct_ZigClangQualType, arg1: struct_ZigClangQualType) bool;
-pub extern fn ZigClangQualType_isConstQualified(self: struct_ZigClangQualType) bool;
-pub extern fn ZigClangQualType_isVolatileQualified(self: struct_ZigClangQualType) bool;
-pub extern fn ZigClangQualType_isRestrictQualified(self: struct_ZigClangQualType) bool;
-pub extern fn ZigClangType_getTypeClass(self: ?*const struct_ZigClangType) ZigClangTypeClass;
-pub extern fn ZigClangType_getPointeeType(self: ?*const struct_ZigClangType) struct_ZigClangQualType;
-pub extern fn ZigClangType_isVoidType(self: ?*const struct_ZigClangType) bool;
-pub extern fn ZigClangType_isConstantArrayType(self: ?*const struct_ZigClangType) bool;
-pub extern fn ZigClangType_isRecordType(self: ?*const struct_ZigClangType) bool;
-pub extern fn ZigClangType_isIncompleteOrZeroLengthArrayType(self: ?*const struct_ZigClangType, *const ZigClangASTContext) bool;
-pub extern fn ZigClangType_isArrayType(self: ?*const struct_ZigClangType) bool;
-pub extern fn ZigClangType_isBooleanType(self: ?*const struct_ZigClangType) bool;
-pub extern fn ZigClangType_getTypeClassName(self: *const struct_ZigClangType) [*:0]const u8;
-pub extern fn ZigClangType_getAsArrayTypeUnsafe(self: *const ZigClangType) *const ZigClangArrayType;
-pub extern fn ZigClangType_getAsRecordType(self: *const ZigClangType) ?*const ZigClangRecordType;
-pub extern fn ZigClangType_getAsUnionType(self: *const ZigClangType) ?*const ZigClangRecordType;
-pub extern fn ZigClangStmt_getBeginLoc(self: *const ZigClangStmt) struct_ZigClangSourceLocation;
-pub extern fn ZigClangStmt_getStmtClass(self: ?*const ZigClangStmt) ZigClangStmtClass;
-pub extern fn ZigClangStmt_classof_Expr(self: ?*const ZigClangStmt) bool;
-pub extern fn ZigClangExpr_getStmtClass(self: *const struct_ZigClangExpr) ZigClangStmtClass;
-pub extern fn ZigClangExpr_getType(self: *const struct_ZigClangExpr) struct_ZigClangQualType;
-pub extern fn ZigClangExpr_getBeginLoc(self: *const struct_ZigClangExpr) struct_ZigClangSourceLocation;
-pub extern fn ZigClangInitListExpr_getInit(self: ?*const struct_ZigClangInitListExpr, i: c_uint) *const ZigClangExpr;
-pub extern fn ZigClangInitListExpr_getArrayFiller(self: ?*const struct_ZigClangInitListExpr) *const ZigClangExpr;
-pub extern fn ZigClangInitListExpr_getNumInits(self: ?*const struct_ZigClangInitListExpr) c_uint;
-pub extern fn ZigClangInitListExpr_getInitializedFieldInUnion(self: ?*const struct_ZigClangInitListExpr) ?*ZigClangFieldDecl;
-pub extern fn ZigClangAPValue_getKind(self: ?*const struct_ZigClangAPValue) ZigClangAPValueKind;
-pub extern fn ZigClangAPValue_getInt(self: ?*const struct_ZigClangAPValue) *const struct_ZigClangAPSInt;
-pub extern fn ZigClangAPValue_getArrayInitializedElts(self: ?*const struct_ZigClangAPValue) c_uint;
-pub extern fn ZigClangAPValue_getArraySize(self: ?*const struct_ZigClangAPValue) c_uint;
-pub extern fn ZigClangAPValue_getLValueBase(self: ?*const struct_ZigClangAPValue) struct_ZigClangAPValueLValueBase;
-pub extern fn ZigClangAPSInt_isSigned(self: *const struct_ZigClangAPSInt) bool;
-pub extern fn ZigClangAPSInt_isNegative(self: *const struct_ZigClangAPSInt) bool;
-pub extern fn ZigClangAPSInt_negate(self: *const struct_ZigClangAPSInt) *const struct_ZigClangAPSInt;
-pub extern fn ZigClangAPSInt_free(self: *const struct_ZigClangAPSInt) void;
-pub extern fn ZigClangAPSInt_getRawData(self: *const struct_ZigClangAPSInt) [*:0]const u64;
-pub extern fn ZigClangAPSInt_getNumWords(self: *const struct_ZigClangAPSInt) c_uint;
-
-pub extern fn ZigClangAPInt_getLimitedValue(self: *const struct_ZigClangAPInt, limit: u64) u64;
-pub extern fn ZigClangAPValueLValueBase_dyn_cast_Expr(self: struct_ZigClangAPValueLValueBase) ?*const struct_ZigClangExpr;
-pub extern fn ZigClangASTUnit_delete(self: ?*struct_ZigClangASTUnit) void;
-
-pub extern fn ZigClangFunctionDecl_getType(self: *const ZigClangFunctionDecl) struct_ZigClangQualType;
-pub extern fn ZigClangFunctionDecl_getLocation(self: *const ZigClangFunctionDecl) struct_ZigClangSourceLocation;
-pub extern fn ZigClangFunctionDecl_hasBody(self: *const ZigClangFunctionDecl) bool;
-pub extern fn ZigClangFunctionDecl_getStorageClass(self: *const ZigClangFunctionDecl) ZigClangStorageClass;
-pub extern fn ZigClangFunctionDecl_getParamDecl(self: *const ZigClangFunctionDecl, i: c_uint) *const struct_ZigClangParmVarDecl;
-pub extern fn ZigClangFunctionDecl_getBody(self: *const ZigClangFunctionDecl) *const ZigClangStmt;
-pub extern fn ZigClangFunctionDecl_doesDeclarationForceExternallyVisibleDefinition(self: *const ZigClangFunctionDecl) bool;
-pub extern fn ZigClangFunctionDecl_isThisDeclarationADefinition(self: *const ZigClangFunctionDecl) bool;
-pub extern fn ZigClangFunctionDecl_doesThisDeclarationHaveABody(self: *const ZigClangFunctionDecl) bool;
-pub extern fn ZigClangFunctionDecl_isInlineSpecified(self: *const ZigClangFunctionDecl) bool;
-pub extern fn ZigClangFunctionDecl_isDefined(self: *const ZigClangFunctionDecl) bool;
-pub extern fn ZigClangFunctionDecl_getDefinition(self: *const ZigClangFunctionDecl) ?*const struct_ZigClangFunctionDecl;
-pub extern fn ZigClangFunctionDecl_getSectionAttribute(self: *const ZigClangFunctionDecl, len: *usize) ?[*]const u8;
-
-pub extern fn ZigClangBuiltinType_getKind(self: *const struct_ZigClangBuiltinType) ZigClangBuiltinTypeKind;
-
-pub extern fn ZigClangFunctionType_getNoReturnAttr(self: *const ZigClangFunctionType) bool;
-pub extern fn ZigClangFunctionType_getCallConv(self: *const ZigClangFunctionType) ZigClangCallingConv;
-pub extern fn ZigClangFunctionType_getReturnType(self: *const ZigClangFunctionType) ZigClangQualType;
-
-pub extern fn ZigClangFunctionProtoType_isVariadic(self: *const struct_ZigClangFunctionProtoType) bool;
-pub extern fn ZigClangFunctionProtoType_getNumParams(self: *const struct_ZigClangFunctionProtoType) c_uint;
-pub extern fn ZigClangFunctionProtoType_getParamType(self: *const struct_ZigClangFunctionProtoType, i: c_uint) ZigClangQualType;
-pub extern fn ZigClangFunctionProtoType_getReturnType(self: *const ZigClangFunctionProtoType) ZigClangQualType;
-
-pub const ZigClangSourceLocation = struct_ZigClangSourceLocation;
-pub const ZigClangQualType = struct_ZigClangQualType;
-pub const ZigClangConditionalOperator = struct_ZigClangConditionalOperator;
-pub const ZigClangBinaryConditionalOperator = struct_ZigClangBinaryConditionalOperator;
-pub const ZigClangAbstractConditionalOperator = struct_ZigClangAbstractConditionalOperator;
-pub const ZigClangAPValueLValueBase = struct_ZigClangAPValueLValueBase;
-pub const ZigClangAPValue = struct_ZigClangAPValue;
-pub const ZigClangAPSInt = struct_ZigClangAPSInt;
-pub const ZigClangAPFloat = struct_ZigClangAPFloat;
-pub const ZigClangASTContext = struct_ZigClangASTContext;
-pub const ZigClangASTUnit = struct_ZigClangASTUnit;
-pub const ZigClangArraySubscriptExpr = struct_ZigClangArraySubscriptExpr;
-pub const ZigClangArrayType = struct_ZigClangArrayType;
-pub const ZigClangAttributedType = struct_ZigClangAttributedType;
-pub const ZigClangBinaryOperator = struct_ZigClangBinaryOperator;
-pub const ZigClangBreakStmt = struct_ZigClangBreakStmt;
-pub const ZigClangBuiltinType = struct_ZigClangBuiltinType;
-pub const ZigClangCStyleCastExpr = struct_ZigClangCStyleCastExpr;
-pub const ZigClangCallExpr = struct_ZigClangCallExpr;
-pub const ZigClangCaseStmt = struct_ZigClangCaseStmt;
-pub const ZigClangCompoundAssignOperator = struct_ZigClangCompoundAssignOperator;
-pub const ZigClangCompoundStmt = struct_ZigClangCompoundStmt;
-pub const ZigClangConstantArrayType = struct_ZigClangConstantArrayType;
-pub const ZigClangContinueStmt = struct_ZigClangContinueStmt;
-pub const ZigClangDecayedType = struct_ZigClangDecayedType;
-pub const ZigClangDeclRefExpr = struct_ZigClangDeclRefExpr;
-pub const ZigClangDeclStmt = struct_ZigClangDeclStmt;
-pub const ZigClangDefaultStmt = struct_ZigClangDefaultStmt;
-pub const ZigClangDiagnosticOptions = struct_ZigClangDiagnosticOptions;
-pub const ZigClangDiagnosticsEngine = struct_ZigClangDiagnosticsEngine;
-pub const ZigClangDoStmt = struct_ZigClangDoStmt;
-pub const ZigClangElaboratedType = struct_ZigClangElaboratedType;
-pub const ZigClangEnumConstantDecl = struct_ZigClangEnumConstantDecl;
-pub const ZigClangEnumDecl = struct_ZigClangEnumDecl;
-pub const ZigClangEnumType = struct_ZigClangEnumType;
-pub const ZigClangExpr = struct_ZigClangExpr;
-pub const ZigClangFieldDecl = struct_ZigClangFieldDecl;
-pub const ZigClangFileID = struct_ZigClangFileID;
-pub const ZigClangForStmt = struct_ZigClangForStmt;
-pub const ZigClangFullSourceLoc = struct_ZigClangFullSourceLoc;
-pub const ZigClangFunctionDecl = struct_ZigClangFunctionDecl;
-pub const ZigClangFunctionProtoType = struct_ZigClangFunctionProtoType;
-pub const ZigClangIfStmt = struct_ZigClangIfStmt;
-pub const ZigClangImplicitCastExpr = struct_ZigClangImplicitCastExpr;
-pub const ZigClangIncompleteArrayType = struct_ZigClangIncompleteArrayType;
-pub const ZigClangIntegerLiteral = struct_ZigClangIntegerLiteral;
-pub const ZigClangMacroDefinitionRecord = struct_ZigClangMacroDefinitionRecord;
-pub const ZigClangMacroExpansion = struct_ZigClangMacroExpansion;
-pub const ZigClangMacroQualifiedType = struct_ZigClangMacroQualifiedType;
-pub const ZigClangMemberExpr = struct_ZigClangMemberExpr;
-pub const ZigClangNamedDecl = struct_ZigClangNamedDecl;
-pub const ZigClangNone = struct_ZigClangNone;
-pub const ZigClangOpaqueValueExpr = struct_ZigClangOpaqueValueExpr;
-pub const ZigClangPCHContainerOperations = struct_ZigClangPCHContainerOperations;
-pub const ZigClangParenExpr = struct_ZigClangParenExpr;
-pub const ZigClangParenType = struct_ZigClangParenType;
-pub const ZigClangParmVarDecl = struct_ZigClangParmVarDecl;
-pub const ZigClangPointerType = struct_ZigClangPointerType;
-pub const ZigClangPreprocessedEntity = struct_ZigClangPreprocessedEntity;
-pub const ZigClangRecordDecl = struct_ZigClangRecordDecl;
-pub const ZigClangRecordType = struct_ZigClangRecordType;
-pub const ZigClangReturnStmt = struct_ZigClangReturnStmt;
-pub const ZigClangSkipFunctionBodiesScope = struct_ZigClangSkipFunctionBodiesScope;
-pub const ZigClangSourceManager = struct_ZigClangSourceManager;
-pub const ZigClangSourceRange = struct_ZigClangSourceRange;
-pub const ZigClangStringLiteral = struct_ZigClangStringLiteral;
-pub const ZigClangStringRef = struct_ZigClangStringRef;
-pub const ZigClangSwitchStmt = struct_ZigClangSwitchStmt;
-pub const ZigClangTagDecl = struct_ZigClangTagDecl;
-pub const ZigClangType = struct_ZigClangType;
-pub const ZigClangTypedefNameDecl = struct_ZigClangTypedefNameDecl;
-pub const ZigClangTypedefType = struct_ZigClangTypedefType;
-pub const ZigClangUnaryExprOrTypeTraitExpr = struct_ZigClangUnaryExprOrTypeTraitExpr;
-pub const ZigClangUnaryOperator = struct_ZigClangUnaryOperator;
-pub const ZigClangValueDecl = struct_ZigClangValueDecl;
-pub const ZigClangVarDecl = struct_ZigClangVarDecl;
-pub const ZigClangWhileStmt = struct_ZigClangWhileStmt;
-pub const ZigClangFunctionType = struct_ZigClangFunctionType;
-pub const ZigClangPredefinedExpr = struct_ZigClangPredefinedExpr;
-pub const ZigClangInitListExpr = struct_ZigClangInitListExpr;
-
-pub const struct_ZigClangSourceLocation = extern struct {
- ID: c_uint,
-};
-
-pub const Stage2ErrorMsg = extern struct {
- filename_ptr: ?[*]const u8,
- filename_len: usize,
- msg_ptr: [*]const u8,
- msg_len: usize,
- // valid until the ASTUnit is freed
- source: ?[*]const u8,
- // 0 based
- line: c_uint,
- // 0 based
- column: c_uint,
- // byte offset into source
- offset: c_uint,
-};
-
-pub const struct_ZigClangQualType = extern struct {
- ptr: ?*c_void,
-};
-
-pub const struct_ZigClangAPValueLValueBase = extern struct {
- Ptr: ?*c_void,
- CallIndex: c_uint,
- Version: c_uint,
-};
-
-pub extern fn ZigClangErrorMsg_delete(ptr: [*]Stage2ErrorMsg, len: usize) void;
-
-pub extern fn ZigClangLoadFromCommandLine(
- args_begin: [*]?[*]const u8,
- args_end: [*]?[*]const u8,
- errors_ptr: *[*]Stage2ErrorMsg,
- errors_len: *usize,
- resources_path: [*:0]const u8,
-) ?*ZigClangASTUnit;
-
-pub extern fn ZigClangDecl_getKind(decl: *const ZigClangDecl) ZigClangDeclKind;
-pub extern fn ZigClangDecl_getDeclKindName(decl: *const ZigClangDecl) [*:0]const u8;
-
-pub const ZigClangCompoundStmt_const_body_iterator = [*]const *ZigClangStmt;
-
-pub extern fn ZigClangCompoundStmt_body_begin(self: *const ZigClangCompoundStmt) ZigClangCompoundStmt_const_body_iterator;
-pub extern fn ZigClangCompoundStmt_body_end(self: *const ZigClangCompoundStmt) ZigClangCompoundStmt_const_body_iterator;
-
-pub const ZigClangDeclStmt_const_decl_iterator = [*]const *ZigClangDecl;
-
-pub extern fn ZigClangDeclStmt_decl_begin(self: *const ZigClangDeclStmt) ZigClangDeclStmt_const_decl_iterator;
-pub extern fn ZigClangDeclStmt_decl_end(self: *const ZigClangDeclStmt) ZigClangDeclStmt_const_decl_iterator;
-
-pub extern fn ZigClangVarDecl_getLocation(self: *const struct_ZigClangVarDecl) ZigClangSourceLocation;
-pub extern fn ZigClangVarDecl_hasInit(self: *const struct_ZigClangVarDecl) bool;
-pub extern fn ZigClangVarDecl_getStorageClass(self: *const ZigClangVarDecl) ZigClangStorageClass;
-pub extern fn ZigClangVarDecl_getType(self: ?*const struct_ZigClangVarDecl) struct_ZigClangQualType;
-pub extern fn ZigClangVarDecl_getInit(*const ZigClangVarDecl) ?*const ZigClangExpr;
-pub extern fn ZigClangVarDecl_getTLSKind(self: ?*const struct_ZigClangVarDecl) ZigClangVarDecl_TLSKind;
-pub const ZigClangVarDecl_TLSKind = extern enum {
- None,
- Static,
- Dynamic,
-};
-
-pub extern fn ZigClangImplicitCastExpr_getBeginLoc(*const ZigClangImplicitCastExpr) ZigClangSourceLocation;
-pub extern fn ZigClangImplicitCastExpr_getCastKind(*const ZigClangImplicitCastExpr) ZigClangCK;
-pub extern fn ZigClangImplicitCastExpr_getSubExpr(*const ZigClangImplicitCastExpr) *const ZigClangExpr;
-
-pub extern fn ZigClangArrayType_getElementType(*const ZigClangArrayType) ZigClangQualType;
-pub extern fn ZigClangIncompleteArrayType_getElementType(*const ZigClangIncompleteArrayType) ZigClangQualType;
-
-pub extern fn ZigClangConstantArrayType_getElementType(self: *const struct_ZigClangConstantArrayType) ZigClangQualType;
-pub extern fn ZigClangConstantArrayType_getSize(self: *const struct_ZigClangConstantArrayType) *const struct_ZigClangAPInt;
-pub extern fn ZigClangDeclRefExpr_getDecl(*const ZigClangDeclRefExpr) *const ZigClangValueDecl;
-pub extern fn ZigClangDeclRefExpr_getFoundDecl(*const ZigClangDeclRefExpr) *const ZigClangNamedDecl;
-
-pub extern fn ZigClangParenType_getInnerType(*const ZigClangParenType) ZigClangQualType;
-
-pub extern fn ZigClangElaboratedType_getNamedType(*const ZigClangElaboratedType) ZigClangQualType;
-
-pub extern fn ZigClangAttributedType_getEquivalentType(*const ZigClangAttributedType) ZigClangQualType;
-
-pub extern fn ZigClangMacroQualifiedType_getModifiedType(*const ZigClangMacroQualifiedType) ZigClangQualType;
-
-pub extern fn ZigClangCStyleCastExpr_getBeginLoc(*const ZigClangCStyleCastExpr) ZigClangSourceLocation;
-pub extern fn ZigClangCStyleCastExpr_getSubExpr(*const ZigClangCStyleCastExpr) *const ZigClangExpr;
-pub extern fn ZigClangCStyleCastExpr_getType(*const ZigClangCStyleCastExpr) ZigClangQualType;
-
-pub const ZigClangExprEvalResult = struct_ZigClangExprEvalResult;
-pub const struct_ZigClangExprEvalResult = extern struct {
- HasSideEffects: bool,
- HasUndefinedBehavior: bool,
- SmallVectorImpl: ?*c_void,
- Val: ZigClangAPValue,
-};
-
-pub const struct_ZigClangAPValue = extern struct {
- Kind: ZigClangAPValueKind,
- Data: if (builtin.os.tag == .windows and builtin.abi == .msvc) [52]u8 else [68]u8,
-};
-pub extern fn ZigClangVarDecl_getTypeSourceInfo_getType(self: *const struct_ZigClangVarDecl) struct_ZigClangQualType;
-
-pub extern fn ZigClangIntegerLiteral_EvaluateAsInt(*const ZigClangIntegerLiteral, *ZigClangExprEvalResult, *const ZigClangASTContext) bool;
-pub extern fn ZigClangIntegerLiteral_getBeginLoc(*const ZigClangIntegerLiteral) ZigClangSourceLocation;
-pub extern fn ZigClangIntegerLiteral_isZero(*const ZigClangIntegerLiteral, *bool, *const ZigClangASTContext) bool;
-
-pub extern fn ZigClangReturnStmt_getRetValue(*const ZigClangReturnStmt) ?*const ZigClangExpr;
-
-pub extern fn ZigClangBinaryOperator_getOpcode(*const ZigClangBinaryOperator) ZigClangBO;
-pub extern fn ZigClangBinaryOperator_getBeginLoc(*const ZigClangBinaryOperator) ZigClangSourceLocation;
-pub extern fn ZigClangBinaryOperator_getLHS(*const ZigClangBinaryOperator) *const ZigClangExpr;
-pub extern fn ZigClangBinaryOperator_getRHS(*const ZigClangBinaryOperator) *const ZigClangExpr;
-pub extern fn ZigClangBinaryOperator_getType(*const ZigClangBinaryOperator) ZigClangQualType;
-
-pub extern fn ZigClangDecayedType_getDecayedType(*const ZigClangDecayedType) ZigClangQualType;
-
-pub extern fn ZigClangStringLiteral_getKind(*const ZigClangStringLiteral) ZigClangStringLiteral_StringKind;
-pub extern fn ZigClangStringLiteral_getString_bytes_begin_size(*const ZigClangStringLiteral, *usize) [*]const u8;
-
-pub extern fn ZigClangParenExpr_getSubExpr(*const ZigClangParenExpr) *const ZigClangExpr;
-
-pub extern fn ZigClangFieldDecl_isAnonymousStructOrUnion(*const struct_ZigClangFieldDecl) bool;
-pub extern fn ZigClangFieldDecl_isBitField(*const struct_ZigClangFieldDecl) bool;
-pub extern fn ZigClangFieldDecl_getType(*const struct_ZigClangFieldDecl) struct_ZigClangQualType;
-pub extern fn ZigClangFieldDecl_getLocation(*const struct_ZigClangFieldDecl) struct_ZigClangSourceLocation;
-
-pub extern fn ZigClangEnumConstantDecl_getInitExpr(*const ZigClangEnumConstantDecl) ?*const ZigClangExpr;
-pub extern fn ZigClangEnumConstantDecl_getInitVal(*const ZigClangEnumConstantDecl) *const ZigClangAPSInt;
-
-pub extern fn ZigClangASTUnit_getLocalPreprocessingEntities_begin(*ZigClangASTUnit) ZigClangPreprocessingRecord_iterator;
-pub extern fn ZigClangASTUnit_getLocalPreprocessingEntities_end(*ZigClangASTUnit) ZigClangPreprocessingRecord_iterator;
-pub extern fn ZigClangPreprocessingRecord_iterator_deref(ZigClangPreprocessingRecord_iterator) *ZigClangPreprocessedEntity;
-pub extern fn ZigClangPreprocessedEntity_getKind(*const ZigClangPreprocessedEntity) ZigClangPreprocessedEntity_EntityKind;
-
-pub extern fn ZigClangMacroDefinitionRecord_getName_getNameStart(*const ZigClangMacroDefinitionRecord) [*:0]const u8;
-pub extern fn ZigClangMacroDefinitionRecord_getSourceRange_getBegin(*const ZigClangMacroDefinitionRecord) ZigClangSourceLocation;
-pub extern fn ZigClangMacroDefinitionRecord_getSourceRange_getEnd(*const ZigClangMacroDefinitionRecord) ZigClangSourceLocation;
-
-pub extern fn ZigClangMacroExpansion_getDefinition(*const ZigClangMacroExpansion) *const ZigClangMacroDefinitionRecord;
-
-pub extern fn ZigClangIfStmt_getThen(*const ZigClangIfStmt) *const ZigClangStmt;
-pub extern fn ZigClangIfStmt_getElse(*const ZigClangIfStmt) ?*const ZigClangStmt;
-pub extern fn ZigClangIfStmt_getCond(*const ZigClangIfStmt) *const ZigClangStmt;
-
-pub extern fn ZigClangWhileStmt_getCond(*const ZigClangWhileStmt) *const ZigClangExpr;
-pub extern fn ZigClangWhileStmt_getBody(*const ZigClangWhileStmt) *const ZigClangStmt;
-
-pub extern fn ZigClangDoStmt_getCond(*const ZigClangDoStmt) *const ZigClangExpr;
-pub extern fn ZigClangDoStmt_getBody(*const ZigClangDoStmt) *const ZigClangStmt;
-
-pub extern fn ZigClangForStmt_getInit(*const ZigClangForStmt) ?*const ZigClangStmt;
-pub extern fn ZigClangForStmt_getCond(*const ZigClangForStmt) ?*const ZigClangExpr;
-pub extern fn ZigClangForStmt_getInc(*const ZigClangForStmt) ?*const ZigClangExpr;
-pub extern fn ZigClangForStmt_getBody(*const ZigClangForStmt) *const ZigClangStmt;
-
-pub extern fn ZigClangAPFloat_toString(self: *const ZigClangAPFloat, precision: c_uint, maxPadding: c_uint, truncateZero: bool) [*:0]const u8;
-pub extern fn ZigClangAPFloat_getValueAsApproximateDouble(*const ZigClangFloatingLiteral) f64;
-
-pub extern fn ZigClangAbstractConditionalOperator_getCond(*const ZigClangAbstractConditionalOperator) *const ZigClangExpr;
-pub extern fn ZigClangAbstractConditionalOperator_getTrueExpr(*const ZigClangAbstractConditionalOperator) *const ZigClangExpr;
-pub extern fn ZigClangAbstractConditionalOperator_getFalseExpr(*const ZigClangAbstractConditionalOperator) *const ZigClangExpr;
-
-pub extern fn ZigClangSwitchStmt_getConditionVariableDeclStmt(*const ZigClangSwitchStmt) ?*const ZigClangDeclStmt;
-pub extern fn ZigClangSwitchStmt_getCond(*const ZigClangSwitchStmt) *const ZigClangExpr;
-pub extern fn ZigClangSwitchStmt_getBody(*const ZigClangSwitchStmt) *const ZigClangStmt;
-pub extern fn ZigClangSwitchStmt_isAllEnumCasesCovered(*const ZigClangSwitchStmt) bool;
-
-pub extern fn ZigClangCaseStmt_getLHS(*const ZigClangCaseStmt) *const ZigClangExpr;
-pub extern fn ZigClangCaseStmt_getRHS(*const ZigClangCaseStmt) ?*const ZigClangExpr;
-pub extern fn ZigClangCaseStmt_getBeginLoc(*const ZigClangCaseStmt) ZigClangSourceLocation;
-pub extern fn ZigClangCaseStmt_getSubStmt(*const ZigClangCaseStmt) *const ZigClangStmt;
-
-pub extern fn ZigClangDefaultStmt_getSubStmt(*const ZigClangDefaultStmt) *const ZigClangStmt;
-
-pub extern fn ZigClangExpr_EvaluateAsConstantExpr(*const ZigClangExpr, *ZigClangExprEvalResult, ZigClangExpr_ConstExprUsage, *const ZigClangASTContext) bool;
-
-pub extern fn ZigClangPredefinedExpr_getFunctionName(*const ZigClangPredefinedExpr) *const ZigClangStringLiteral;
-
-pub extern fn ZigClangCharacterLiteral_getBeginLoc(*const ZigClangCharacterLiteral) ZigClangSourceLocation;
-pub extern fn ZigClangCharacterLiteral_getKind(*const ZigClangCharacterLiteral) ZigClangCharacterLiteral_CharacterKind;
-pub extern fn ZigClangCharacterLiteral_getValue(*const ZigClangCharacterLiteral) c_uint;
-
-pub extern fn ZigClangStmtExpr_getSubStmt(*const ZigClangStmtExpr) *const ZigClangCompoundStmt;
-
-pub extern fn ZigClangMemberExpr_getBase(*const ZigClangMemberExpr) *const ZigClangExpr;
-pub extern fn ZigClangMemberExpr_isArrow(*const ZigClangMemberExpr) bool;
-pub extern fn ZigClangMemberExpr_getMemberDecl(*const ZigClangMemberExpr) *const ZigClangValueDecl;
-
-pub extern fn ZigClangArraySubscriptExpr_getBase(*const ZigClangArraySubscriptExpr) *const ZigClangExpr;
-pub extern fn ZigClangArraySubscriptExpr_getIdx(*const ZigClangArraySubscriptExpr) *const ZigClangExpr;
-
-pub extern fn ZigClangCallExpr_getCallee(*const ZigClangCallExpr) *const ZigClangExpr;
-pub extern fn ZigClangCallExpr_getNumArgs(*const ZigClangCallExpr) c_uint;
-pub extern fn ZigClangCallExpr_getArgs(*const ZigClangCallExpr) [*]const *const ZigClangExpr;
-
-pub extern fn ZigClangUnaryExprOrTypeTraitExpr_getTypeOfArgument(*const ZigClangUnaryExprOrTypeTraitExpr) ZigClangQualType;
-pub extern fn ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(*const ZigClangUnaryExprOrTypeTraitExpr) ZigClangSourceLocation;
-pub extern fn ZigClangUnaryExprOrTypeTraitExpr_getKind(*const ZigClangUnaryExprOrTypeTraitExpr) ZigClangUnaryExprOrTypeTrait_Kind;
-
-pub extern fn ZigClangUnaryOperator_getOpcode(*const ZigClangUnaryOperator) ZigClangUO;
-pub extern fn ZigClangUnaryOperator_getType(*const ZigClangUnaryOperator) ZigClangQualType;
-pub extern fn ZigClangUnaryOperator_getSubExpr(*const ZigClangUnaryOperator) *const ZigClangExpr;
-pub extern fn ZigClangUnaryOperator_getBeginLoc(*const ZigClangUnaryOperator) ZigClangSourceLocation;
-
-pub extern fn ZigClangOpaqueValueExpr_getSourceExpr(*const ZigClangOpaqueValueExpr) ?*const ZigClangExpr;
-
-pub extern fn ZigClangCompoundAssignOperator_getType(*const ZigClangCompoundAssignOperator) ZigClangQualType;
-pub extern fn ZigClangCompoundAssignOperator_getComputationLHSType(*const ZigClangCompoundAssignOperator) ZigClangQualType;
-pub extern fn ZigClangCompoundAssignOperator_getComputationResultType(*const ZigClangCompoundAssignOperator) ZigClangQualType;
-pub extern fn ZigClangCompoundAssignOperator_getBeginLoc(*const ZigClangCompoundAssignOperator) ZigClangSourceLocation;
-pub extern fn ZigClangCompoundAssignOperator_getOpcode(*const ZigClangCompoundAssignOperator) ZigClangBO;
-pub extern fn ZigClangCompoundAssignOperator_getLHS(*const ZigClangCompoundAssignOperator) *const ZigClangExpr;
-pub extern fn ZigClangCompoundAssignOperator_getRHS(*const ZigClangCompoundAssignOperator) *const ZigClangExpr;
diff --git a/src-self-hosted/clang_options.zig b/src-self-hosted/clang_options.zig
deleted file mode 100644
index 42bfecb74622c888c4f399b7e00d24e109c243b0..0000000000000000000000000000000000000000
--- a/src-self-hosted/clang_options.zig
+++ /dev/null
@@ -1,134 +0,0 @@
-const std = @import("std");
-const mem = std.mem;
-
-pub const list = @import("clang_options_data.zig").data;
-
-pub const CliArg = struct {
- name: []const u8,
- syntax: Syntax,
-
- zig_equivalent: @import("main.zig").ClangArgIterator.ZigEquivalent,
-
- /// Prefixed by "-"
- pd1: bool = false,
-
- /// Prefixed by "--"
- pd2: bool = false,
-
- /// Prefixed by "/"
- psl: bool = false,
-
- pub const Syntax = union(enum) {
- /// A flag with no values.
- flag,
-
- /// An option which prefixes its (single) value.
- joined,
-
- /// An option which is followed by its value.
- separate,
-
- /// An option which is either joined to its (non-empty) value, or followed by its value.
- joined_or_separate,
-
- /// An option which is both joined to its (first) value, and followed by its (second) value.
- joined_and_separate,
-
- /// An option followed by its values, which are separated by commas.
- comma_joined,
-
- /// An option which consumes an optional joined argument and any other remaining arguments.
- remaining_args_joined,
-
- /// An option which is which takes multiple (separate) arguments.
- multi_arg: u8,
- };
-
- pub fn matchEql(self: CliArg, arg: []const u8) u2 {
- if (self.pd1 and arg.len >= self.name.len + 1 and
- mem.startsWith(u8, arg, "-") and mem.eql(u8, arg[1..], self.name))
- {
- return 1;
- }
- if (self.pd2 and arg.len >= self.name.len + 2 and
- mem.startsWith(u8, arg, "--") and mem.eql(u8, arg[2..], self.name))
- {
- return 2;
- }
- if (self.psl and arg.len >= self.name.len + 1 and
- mem.startsWith(u8, arg, "/") and mem.eql(u8, arg[1..], self.name))
- {
- return 1;
- }
- return 0;
- }
-
- pub fn matchStartsWith(self: CliArg, arg: []const u8) usize {
- if (self.pd1 and arg.len >= self.name.len + 1 and
- mem.startsWith(u8, arg, "-") and mem.startsWith(u8, arg[1..], self.name))
- {
- return self.name.len + 1;
- }
- if (self.pd2 and arg.len >= self.name.len + 2 and
- mem.startsWith(u8, arg, "--") and mem.startsWith(u8, arg[2..], self.name))
- {
- return self.name.len + 2;
- }
- if (self.psl and arg.len >= self.name.len + 1 and
- mem.startsWith(u8, arg, "/") and mem.startsWith(u8, arg[1..], self.name))
- {
- return self.name.len + 1;
- }
- return 0;
- }
-};
-
-/// Shortcut function for initializing a `CliArg`
-pub fn flagpd1(name: []const u8) CliArg {
- return .{
- .name = name,
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- };
-}
-
-/// Shortcut function for initializing a `CliArg`
-pub fn flagpsl(name: []const u8) CliArg {
- return .{
- .name = name,
- .syntax = .flag,
- .zig_equivalent = .other,
- .psl = true,
- };
-}
-
-/// Shortcut function for initializing a `CliArg`
-pub fn joinpd1(name: []const u8) CliArg {
- return .{
- .name = name,
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- };
-}
-
-/// Shortcut function for initializing a `CliArg`
-pub fn jspd1(name: []const u8) CliArg {
- return .{
- .name = name,
- .syntax = .joined_or_separate,
- .zig_equivalent = .other,
- .pd1 = true,
- };
-}
-
-/// Shortcut function for initializing a `CliArg`
-pub fn sepd1(name: []const u8) CliArg {
- return .{
- .name = name,
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = true,
- };
-}
diff --git a/src-self-hosted/clang_options_data.zig b/src-self-hosted/clang_options_data.zig
deleted file mode 100644
index 889737bdac6020f99333755b321bd664a2818d26..0000000000000000000000000000000000000000
--- a/src-self-hosted/clang_options_data.zig
+++ /dev/null
@@ -1,5870 +0,0 @@
-// This file is generated by tools/update_clang_options.zig.
-// zig fmt: off
-usingnamespace @import("clang_options.zig");
-pub const data = blk: { @setEvalBranchQuota(6000); break :blk &[_]CliArg{
-flagpd1("C"),
-flagpd1("CC"),
-.{
- .name = "E",
- .syntax = .flag,
- .zig_equivalent = .pp_or_asm,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-flagpd1("EB"),
-flagpd1("EL"),
-flagpd1("Eonly"),
-flagpd1("H"),
-.{
- .name = "",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = false,
- .psl = false,
-},
-flagpd1("I-"),
-flagpd1("M"),
-.{
- .name = "MD",
- .syntax = .flag,
- .zig_equivalent = .dep_file,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "MG",
- .syntax = .flag,
- .zig_equivalent = .dep_file,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "MM",
- .syntax = .flag,
- .zig_equivalent = .dep_file,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "MMD",
- .syntax = .flag,
- .zig_equivalent = .dep_file,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "MP",
- .syntax = .flag,
- .zig_equivalent = .dep_file,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "MV",
- .syntax = .flag,
- .zig_equivalent = .dep_file,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-flagpd1("Mach"),
-flagpd1("O0"),
-flagpd1("O4"),
-.{
- .name = "O",
- .syntax = .flag,
- .zig_equivalent = .optimize,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-flagpd1("ObjC"),
-flagpd1("ObjC++"),
-flagpd1("P"),
-flagpd1("Q"),
-flagpd1("Qn"),
-flagpd1("Qunused-arguments"),
-flagpd1("Qy"),
-.{
- .name = "S",
- .syntax = .flag,
- .zig_equivalent = .pp_or_asm,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = false,
- .psl = false,
-},
-flagpd1("WCL4"),
-flagpd1("Wall"),
-flagpd1("Wdeprecated"),
-flagpd1("Wlarge-by-value-copy"),
-flagpd1("Wno-deprecated"),
-flagpd1("Wno-rewrite-macros"),
-flagpd1("Wno-write-strings"),
-flagpd1("Wwrite-strings"),
-flagpd1("X"),
-sepd1("Xanalyzer"),
-sepd1("Xassembler"),
-sepd1("Xclang"),
-sepd1("Xcuda-fatbinary"),
-sepd1("Xcuda-ptxas"),
-.{
- .name = "Xlinker",
- .syntax = .separate,
- .zig_equivalent = .for_linker,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-sepd1("Xopenmp-target"),
-sepd1("Xpreprocessor"),
-flagpd1("Z"),
-flagpd1("Z-Xlinker-no-demangle"),
-flagpd1("Z-reserved-lib-cckext"),
-flagpd1("Z-reserved-lib-stdc++"),
-sepd1("Zlinker-input"),
-.{
- .name = "CLASSPATH",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "###",
- .syntax = .flag,
- .zig_equivalent = .verbose_cmds,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "Brepro",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Brepro-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Bt",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Bt+",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "C",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "E",
- .syntax = .flag,
- .zig_equivalent = .pp_or_asm,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "EP",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "FA",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "FC",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "FS",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Fx",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "G1",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "G2",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "GA",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "GF",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "GF-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "GH",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "GL",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "GL-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "GR",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "GR-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "GS",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "GS-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "GT",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "GX",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "GX-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "GZ",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Gd",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Ge",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Gh",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Gm",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Gm-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Gr",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Gregcall",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Gv",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Gw",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Gw-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Gy",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Gy-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Gz",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "H",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "HELP",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "J",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "JMC",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "LD",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "LDd",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "LN",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "MD",
- .syntax = .flag,
- .zig_equivalent = .dep_file,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "MDd",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-flagpsl("MT"),
-.{
- .name = "MTd",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "P",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "QIfist",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "?",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Qfast_transcendentals",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Qimprecise_fwaits",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Qpar",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Qsafe_fp_loads",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Qspectre",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Qvec",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Qvec-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "TC",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "TP",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "V",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "W0",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "W1",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "W2",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "W3",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "W4",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "WL",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "WX",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "WX-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Wall",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Wp64",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "X",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Y-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Yd",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Z7",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "ZH:MD5",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "ZH:SHA1",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "ZH:SHA_256",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "ZI",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Za",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:__cplusplus",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:alignedNew",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:alignedNew-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:auto",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:char8_t",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:char8_t-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:dllexportInlines",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:dllexportInlines-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:forScope",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:inline",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:rvalueCast",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:sizedDealloc",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:sizedDealloc-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:strictStrings",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:ternary",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:threadSafeInit",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:threadSafeInit-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:trigraphs",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:trigraphs-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:twoPhase",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:twoPhase-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:wchar_t",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zd",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Ze",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zg",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zi",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zl",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zo",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zo-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zp",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zs",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "analyze-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "await",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "bigobj",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "c",
- .syntax = .flag,
- .zig_equivalent = .c,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "d1PP",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "d1reportAllClassLayout",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "d2FastFail",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "d2Zi+",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "diagnostics:caret",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "diagnostics:classic",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "diagnostics:column",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "fallback",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "fp:except",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "fp:except-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "fp:fast",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "fp:precise",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "fp:strict",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "help",
- .syntax = .flag,
- .zig_equivalent = .driver_punt,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "homeparams",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "hotpatch",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "kernel",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "kernel-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "nologo",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "openmp",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "openmp-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "openmp:experimental",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "permissive-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "sdl",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "sdl-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "showFilenames",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "showFilenames-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "showIncludes",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "u",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "utf-8",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "validate-charset",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "validate-charset-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "vmb",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "vmg",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "vmm",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "vms",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "vmv",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "volatile:iso",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "volatile:ms",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "w",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "wd4005",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "wd4018",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "wd4100",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "wd4910",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "wd4996",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "all-warnings",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "analyze",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "analyzer-no-default-checks",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "assemble",
- .syntax = .flag,
- .zig_equivalent = .pp_or_asm,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "assert",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "bootclasspath",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "classpath",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "comments",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "comments-in-macros",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "compile",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "constant-cfstrings",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "debug",
- .syntax = .flag,
- .zig_equivalent = .debug,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "define-macro",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "dependencies",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "dyld-prefix",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "encoding",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "entry",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "extdirs",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "extra-warnings",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "for-linker",
- .syntax = .separate,
- .zig_equivalent = .for_linker,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "force-link",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "help-hidden",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "include-barrier",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "include-directory",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "include-directory-after",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "include-prefix",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "include-with-prefix",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "include-with-prefix-after",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "include-with-prefix-before",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "language",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "library-directory",
- .syntax = .separate,
- .zig_equivalent = .lib_dir,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "mhwdiv",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "migrate",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "no-line-commands",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "no-standard-includes",
- .syntax = .flag,
- .zig_equivalent = .nostdlibinc,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "no-standard-libraries",
- .syntax = .flag,
- .zig_equivalent = .nostdlib,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "no-undefined",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "no-warnings",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "optimize",
- .syntax = .flag,
- .zig_equivalent = .optimize,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "output",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "output-class-directory",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "param",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "precompile",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "prefix",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "preprocess",
- .syntax = .flag,
- .zig_equivalent = .pp_or_asm,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "print-diagnostic-categories",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "print-file-name",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "print-missing-file-dependencies",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "print-prog-name",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "profile",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "profile-blocks",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "resource",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "rtlib",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "serialize-diagnostics",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "signed-char",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "std",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "stdlib",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "sysroot",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "target-help",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "trace-includes",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "undefine-macro",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "unsigned-char",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "user-dependencies",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "verbose",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "version",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "write-dependencies",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "write-user-dependencies",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-sepd1("add-plugin"),
-flagpd1("faggressive-function-elimination"),
-flagpd1("fno-aggressive-function-elimination"),
-flagpd1("falign-commons"),
-flagpd1("fno-align-commons"),
-flagpd1("falign-jumps"),
-flagpd1("fno-align-jumps"),
-flagpd1("falign-labels"),
-flagpd1("fno-align-labels"),
-flagpd1("falign-loops"),
-flagpd1("fno-align-loops"),
-flagpd1("faligned-alloc-unavailable"),
-flagpd1("all_load"),
-flagpd1("fall-intrinsics"),
-flagpd1("fno-all-intrinsics"),
-sepd1("allowable_client"),
-flagpd1("cfg-add-implicit-dtors"),
-flagpd1("unoptimized-cfg"),
-flagpd1("analyze"),
-sepd1("analyze-function"),
-sepd1("analyzer-checker"),
-flagpd1("analyzer-checker-help"),
-flagpd1("analyzer-checker-help-alpha"),
-flagpd1("analyzer-checker-help-developer"),
-flagpd1("analyzer-checker-option-help"),
-flagpd1("analyzer-checker-option-help-alpha"),
-flagpd1("analyzer-checker-option-help-developer"),
-sepd1("analyzer-config"),
-sepd1("analyzer-config-compatibility-mode"),
-flagpd1("analyzer-config-help"),
-sepd1("analyzer-constraints"),
-flagpd1("analyzer-disable-all-checks"),
-sepd1("analyzer-disable-checker"),
-flagpd1("analyzer-disable-retry-exhausted"),
-flagpd1("analyzer-display-progress"),
-sepd1("analyzer-dump-egraph"),
-sepd1("analyzer-inline-max-stack-depth"),
-sepd1("analyzer-inlining-mode"),
-flagpd1("analyzer-list-enabled-checkers"),
-sepd1("analyzer-max-loop"),
-flagpd1("analyzer-opt-analyze-headers"),
-flagpd1("analyzer-opt-analyze-nested-blocks"),
-sepd1("analyzer-output"),
-sepd1("analyzer-purge"),
-flagpd1("analyzer-stats"),
-sepd1("analyzer-store"),
-flagpd1("analyzer-viz-egraph-graphviz"),
-flagpd1("analyzer-werror"),
-flagpd1("fslp-vectorize-aggressive"),
-flagpd1("fno-slp-vectorize-aggressive"),
-flagpd1("fexpensive-optimizations"),
-flagpd1("fno-expensive-optimizations"),
-flagpd1("fdefer-pop"),
-flagpd1("fno-defer-pop"),
-flagpd1("fextended-identifiers"),
-flagpd1("fno-extended-identifiers"),
-flagpd1("fhonor-infinites"),
-flagpd1("fno-honor-infinites"),
-flagpd1("findirect-virtual-calls"),
-sepd1("fnew-alignment"),
-flagpd1("faligned-new"),
-flagpd1("fno-aligned-new"),
-flagpd1("fsched-interblock"),
-flagpd1("ftree-vectorize"),
-flagpd1("fno-tree-vectorize"),
-flagpd1("ftree-slp-vectorize"),
-flagpd1("fno-tree-slp-vectorize"),
-flagpd1("fterminated-vtables"),
-flagpd1("grecord-gcc-switches"),
-flagpd1("gno-record-gcc-switches"),
-flagpd1("fident"),
-flagpd1("nocudalib"),
-.{
- .name = "system-header-prefix",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "no-system-header-prefix",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("integrated-as"),
-flagpd1("no-integrated-as"),
-flagpd1("fkeep-inline-functions"),
-flagpd1("fno-keep-inline-functions"),
-flagpd1("fno-semantic-interposition"),
-.{
- .name = "Gs",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "O1",
- .syntax = .flag,
- .zig_equivalent = .optimize,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "O2",
- .syntax = .flag,
- .zig_equivalent = .optimize,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-flagpd1("fno-ident"),
-.{
- .name = "Ob0",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Ob1",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Ob2",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Od",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Og",
- .syntax = .flag,
- .zig_equivalent = .optimize,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Oi",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Oi-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Os",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Ot",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Ox",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-flagpd1("fcuda-rdc"),
-.{
- .name = "Oy",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Oy-",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-flagpd1("fno-cuda-rdc"),
-flagpd1("shared-libasan"),
-flagpd1("frecord-gcc-switches"),
-flagpd1("fno-record-gcc-switches"),
-.{
- .name = "ansi",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-sepd1("arch"),
-flagpd1("arch_errors_fatal"),
-sepd1("arch_only"),
-flagpd1("arcmt-check"),
-flagpd1("arcmt-migrate"),
-flagpd1("arcmt-migrate-emit-errors"),
-sepd1("arcmt-migrate-report-output"),
-flagpd1("arcmt-modify"),
-flagpd1("ast-dump"),
-flagpd1("ast-dump-all"),
-sepd1("ast-dump-filter"),
-flagpd1("ast-dump-lookups"),
-flagpd1("ast-list"),
-sepd1("ast-merge"),
-flagpd1("ast-print"),
-flagpd1("ast-view"),
-flagpd1("fautomatic"),
-flagpd1("fno-automatic"),
-sepd1("aux-triple"),
-flagpd1("fbackslash"),
-flagpd1("fno-backslash"),
-flagpd1("fbacktrace"),
-flagpd1("fno-backtrace"),
-flagpd1("bind_at_load"),
-flagpd1("fbounds-check"),
-flagpd1("fno-bounds-check"),
-flagpd1("fbranch-count-reg"),
-flagpd1("fno-branch-count-reg"),
-flagpd1("building-pch-with-obj"),
-flagpd1("bundle"),
-sepd1("bundle_loader"),
-.{
- .name = "c",
- .syntax = .flag,
- .zig_equivalent = .c,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-flagpd1("fcaller-saves"),
-flagpd1("fno-caller-saves"),
-flagpd1("cc1"),
-flagpd1("cc1as"),
-flagpd1("ccc-arcmt-check"),
-sepd1("ccc-arcmt-migrate"),
-flagpd1("ccc-arcmt-modify"),
-sepd1("ccc-gcc-name"),
-sepd1("ccc-install-dir"),
-sepd1("ccc-objcmt-migrate"),
-flagpd1("ccc-print-bindings"),
-flagpd1("ccc-print-phases"),
-flagpd1("cfguard"),
-flagpd1("cfguard-no-checks"),
-sepd1("chain-include"),
-flagpd1("fcheck-array-temporaries"),
-flagpd1("fno-check-array-temporaries"),
-flagpd1("cl-denorms-are-zero"),
-flagpd1("cl-fast-relaxed-math"),
-flagpd1("cl-finite-math-only"),
-flagpd1("cl-fp32-correctly-rounded-divide-sqrt"),
-flagpd1("cl-kernel-arg-info"),
-flagpd1("cl-mad-enable"),
-flagpd1("cl-no-signed-zeros"),
-flagpd1("cl-opt-disable"),
-flagpd1("cl-single-precision-constant"),
-flagpd1("cl-strict-aliasing"),
-flagpd1("cl-uniform-work-group-size"),
-flagpd1("cl-unsafe-math-optimizations"),
-sepd1("code-completion-at"),
-flagpd1("code-completion-brief-comments"),
-flagpd1("code-completion-macros"),
-flagpd1("code-completion-patterns"),
-flagpd1("code-completion-with-fixits"),
-.{
- .name = "combine",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("compiler-options-dump"),
-.{
- .name = "compress-debug-sections",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "config",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "coverage",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("coverage-cfg-checksum"),
-sepd1("coverage-data-file"),
-flagpd1("coverage-exit-block-before-body"),
-flagpd1("coverage-no-function-names-in-data"),
-sepd1("coverage-notes-file"),
-flagpd1("cpp"),
-flagpd1("cpp-precomp"),
-flagpd1("fcray-pointer"),
-flagpd1("fno-cray-pointer"),
-.{
- .name = "cuda-compile-host-device",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "cuda-device-only",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "cuda-host-only",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "cuda-noopt-device-debug",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "cuda-path-ignore-env",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("dA"),
-flagpd1("dD"),
-flagpd1("dI"),
-flagpd1("dM"),
-flagpd1("d"),
-flagpd1("fd-lines-as-code"),
-flagpd1("fno-d-lines-as-code"),
-flagpd1("fd-lines-as-comments"),
-flagpd1("fno-d-lines-as-comments"),
-flagpd1("dead_strip"),
-flagpd1("debug-forward-template-params"),
-flagpd1("debug-info-macro"),
-flagpd1("fdefault-double-8"),
-flagpd1("fno-default-double-8"),
-sepd1("default-function-attr"),
-flagpd1("fdefault-inline"),
-flagpd1("fno-default-inline"),
-flagpd1("fdefault-integer-8"),
-flagpd1("fno-default-integer-8"),
-flagpd1("fdefault-real-8"),
-flagpd1("fno-default-real-8"),
-sepd1("defsym"),
-sepd1("dependency-dot"),
-sepd1("dependency-file"),
-flagpd1("detailed-preprocessing-record"),
-flagpd1("fdevirtualize"),
-flagpd1("fno-devirtualize"),
-flagpd1("fdevirtualize-speculatively"),
-flagpd1("fno-devirtualize-speculatively"),
-sepd1("diagnostic-log-file"),
-sepd1("serialize-diagnostic-file"),
-flagpd1("disable-O0-optnone"),
-flagpd1("disable-free"),
-flagpd1("disable-lifetime-markers"),
-flagpd1("disable-llvm-optzns"),
-flagpd1("disable-llvm-passes"),
-flagpd1("disable-llvm-verifier"),
-flagpd1("disable-objc-default-synthesize-properties"),
-flagpd1("disable-pragma-debug-crash"),
-flagpd1("disable-red-zone"),
-flagpd1("discard-value-names"),
-flagpd1("fdollar-ok"),
-flagpd1("fno-dollar-ok"),
-flagpd1("dump-coverage-mapping"),
-flagpd1("dump-deserialized-decls"),
-flagpd1("fdump-fortran-optimized"),
-flagpd1("fno-dump-fortran-optimized"),
-flagpd1("fdump-fortran-original"),
-flagpd1("fno-dump-fortran-original"),
-flagpd1("fdump-parse-tree"),
-flagpd1("fno-dump-parse-tree"),
-flagpd1("dump-raw-tokens"),
-flagpd1("dump-tokens"),
-flagpd1("dumpmachine"),
-flagpd1("dumpspecs"),
-flagpd1("dumpversion"),
-flagpd1("dwarf-column-info"),
-sepd1("dwarf-debug-flags"),
-sepd1("dwarf-debug-producer"),
-flagpd1("dwarf-explicit-import"),
-flagpd1("dwarf-ext-refs"),
-sepd1("dylib_file"),
-flagpd1("dylinker"),
-flagpd1("dynamic"),
-flagpd1("dynamiclib"),
-flagpd1("feliminate-unused-debug-types"),
-flagpd1("fno-eliminate-unused-debug-types"),
-flagpd1("emit-ast"),
-flagpd1("emit-codegen-only"),
-flagpd1("emit-header-module"),
-flagpd1("emit-html"),
-flagpd1("emit-interface-stubs"),
-flagpd1("emit-llvm"),
-flagpd1("emit-llvm-bc"),
-flagpd1("emit-llvm-only"),
-flagpd1("emit-llvm-uselists"),
-flagpd1("emit-merged-ifs"),
-flagpd1("emit-module"),
-flagpd1("emit-module-interface"),
-flagpd1("emit-obj"),
-flagpd1("emit-pch"),
-flagpd1("enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang"),
-sepd1("error-on-deserialized-decl"),
-sepd1("exported_symbols_list"),
-flagpd1("fexternal-blas"),
-flagpd1("fno-external-blas"),
-flagpd1("ff2c"),
-flagpd1("fno-f2c"),
-.{
- .name = "fPIC",
- .syntax = .flag,
- .zig_equivalent = .pic,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-flagpd1("fPIE"),
-flagpd1("faccess-control"),
-flagpd1("faddrsig"),
-flagpd1("falign-functions"),
-flagpd1("faligned-allocation"),
-flagpd1("fallow-editor-placeholders"),
-flagpd1("fallow-half-arguments-and-returns"),
-flagpd1("fallow-pch-with-compiler-errors"),
-flagpd1("fallow-unsupported"),
-flagpd1("faltivec"),
-flagpd1("fansi-escape-codes"),
-flagpd1("fapple-kext"),
-flagpd1("fapple-link-rtlib"),
-flagpd1("fapple-pragma-pack"),
-flagpd1("fapplication-extension"),
-flagpd1("fapply-global-visibility-to-externs"),
-flagpd1("fasm"),
-flagpd1("fasm-blocks"),
-flagpd1("fassociative-math"),
-flagpd1("fassume-sane-operator-new"),
-flagpd1("fast"),
-flagpd1("fastcp"),
-flagpd1("fastf"),
-flagpd1("fasynchronous-unwind-tables"),
-flagpd1("ffat-lto-objects"),
-flagpd1("fno-fat-lto-objects"),
-flagpd1("fauto-profile"),
-flagpd1("fauto-profile-accurate"),
-flagpd1("fautolink"),
-flagpd1("fblocks"),
-flagpd1("fblocks-runtime-optional"),
-flagpd1("fborland-extensions"),
-sepd1("fbracket-depth"),
-flagpd1("fbuiltin"),
-flagpd1("fbuiltin-module-map"),
-flagpd1("fcall-saved-x10"),
-flagpd1("fcall-saved-x11"),
-flagpd1("fcall-saved-x12"),
-flagpd1("fcall-saved-x13"),
-flagpd1("fcall-saved-x14"),
-flagpd1("fcall-saved-x15"),
-flagpd1("fcall-saved-x18"),
-flagpd1("fcall-saved-x8"),
-flagpd1("fcall-saved-x9"),
-flagpd1("fcaret-diagnostics"),
-sepd1("fcaret-diagnostics-max-lines"),
-flagpd1("fcf-protection"),
-flagpd1("fchar8_t"),
-flagpd1("fcheck-new"),
-flagpd1("fno-check-new"),
-flagpd1("fcolor-diagnostics"),
-flagpd1("fcommon"),
-flagpd1("fcomplete-member-pointers"),
-flagpd1("fconcepts-ts"),
-flagpd1("fconst-strings"),
-flagpd1("fconstant-cfstrings"),
-sepd1("fconstant-string-class"),
-sepd1("fconstexpr-backtrace-limit"),
-sepd1("fconstexpr-depth"),
-sepd1("fconstexpr-steps"),
-flagpd1("fconvergent-functions"),
-flagpd1("fcoroutines-ts"),
-flagpd1("fcoverage-mapping"),
-flagpd1("fcreate-profile"),
-flagpd1("fcs-profile-generate"),
-flagpd1("fcuda-allow-variadic-functions"),
-flagpd1("fcuda-approx-transcendentals"),
-flagpd1("fcuda-flush-denormals-to-zero"),
-sepd1("fcuda-include-gpubinary"),
-flagpd1("fcuda-is-device"),
-flagpd1("fcuda-short-ptr"),
-flagpd1("fcxx-exceptions"),
-flagpd1("fcxx-modules"),
-flagpd1("fc++-static-destructors"),
-flagpd1("fdata-sections"),
-sepd1("fdebug-compilation-dir"),
-flagpd1("fdebug-info-for-profiling"),
-flagpd1("fdebug-macro"),
-flagpd1("fdebug-pass-arguments"),
-flagpd1("fdebug-pass-manager"),
-flagpd1("fdebug-pass-structure"),
-flagpd1("fdebug-ranges-base-address"),
-flagpd1("fdebug-types-section"),
-flagpd1("fdebugger-cast-result-to-id"),
-flagpd1("fdebugger-objc-literal"),
-flagpd1("fdebugger-support"),
-flagpd1("fdeclare-opencl-builtins"),
-flagpd1("fdeclspec"),
-flagpd1("fdelayed-template-parsing"),
-flagpd1("fdelete-null-pointer-checks"),
-flagpd1("fdeprecated-macro"),
-flagpd1("fdiagnostics-absolute-paths"),
-flagpd1("fdiagnostics-color"),
-flagpd1("fdiagnostics-fixit-info"),
-sepd1("fdiagnostics-format"),
-flagpd1("fdiagnostics-parseable-fixits"),
-flagpd1("fdiagnostics-print-source-range-info"),
-sepd1("fdiagnostics-show-category"),
-flagpd1("fdiagnostics-show-hotness"),
-flagpd1("fdiagnostics-show-note-include-stack"),
-flagpd1("fdiagnostics-show-option"),
-flagpd1("fdiagnostics-show-template-tree"),
-flagpd1("fdigraphs"),
-flagpd1("fdisable-module-hash"),
-flagpd1("fdiscard-value-names"),
-flagpd1("fdollars-in-identifiers"),
-flagpd1("fdouble-square-bracket-attributes"),
-flagpd1("fdump-record-layouts"),
-flagpd1("fdump-record-layouts-simple"),
-flagpd1("fdump-vtable-layouts"),
-flagpd1("fdwarf2-cfi-asm"),
-flagpd1("fdwarf-directory-asm"),
-flagpd1("fdwarf-exceptions"),
-flagpd1("felide-constructors"),
-flagpd1("feliminate-unused-debug-symbols"),
-flagpd1("fembed-bitcode"),
-flagpd1("fembed-bitcode-marker"),
-flagpd1("femit-all-decls"),
-flagpd1("femit-coverage-data"),
-flagpd1("femit-coverage-notes"),
-flagpd1("femit-debug-entry-values"),
-flagpd1("femulated-tls"),
-flagpd1("fencode-extended-block-signature"),
-sepd1("ferror-limit"),
-flagpd1("fescaping-block-tail-calls"),
-flagpd1("fexceptions"),
-flagpd1("fexperimental-isel"),
-flagpd1("fexperimental-new-constant-interpreter"),
-flagpd1("fexperimental-new-pass-manager"),
-flagpd1("fexternc-nounwind"),
-flagpd1("ffake-address-space-map"),
-flagpd1("ffast-math"),
-flagpd1("ffine-grained-bitfield-accesses"),
-flagpd1("ffinite-math-only"),
-flagpd1("ffixed-point"),
-flagpd1("ffixed-r19"),
-flagpd1("ffixed-r9"),
-flagpd1("ffixed-x1"),
-flagpd1("ffixed-x10"),
-flagpd1("ffixed-x11"),
-flagpd1("ffixed-x12"),
-flagpd1("ffixed-x13"),
-flagpd1("ffixed-x14"),
-flagpd1("ffixed-x15"),
-flagpd1("ffixed-x16"),
-flagpd1("ffixed-x17"),
-flagpd1("ffixed-x18"),
-flagpd1("ffixed-x19"),
-flagpd1("ffixed-x2"),
-flagpd1("ffixed-x20"),
-flagpd1("ffixed-x21"),
-flagpd1("ffixed-x22"),
-flagpd1("ffixed-x23"),
-flagpd1("ffixed-x24"),
-flagpd1("ffixed-x25"),
-flagpd1("ffixed-x26"),
-flagpd1("ffixed-x27"),
-flagpd1("ffixed-x28"),
-flagpd1("ffixed-x29"),
-flagpd1("ffixed-x3"),
-flagpd1("ffixed-x30"),
-flagpd1("ffixed-x31"),
-flagpd1("ffixed-x4"),
-flagpd1("ffixed-x5"),
-flagpd1("ffixed-x6"),
-flagpd1("ffixed-x7"),
-flagpd1("ffixed-x8"),
-flagpd1("ffixed-x9"),
-flagpd1("ffor-scope"),
-flagpd1("fforbid-guard-variables"),
-flagpd1("fforce-dwarf-frame"),
-flagpd1("fforce-emit-vtables"),
-flagpd1("fforce-enable-int128"),
-flagpd1("ffreestanding"),
-flagpd1("ffunction-sections"),
-flagpd1("fgnu89-inline"),
-flagpd1("fgnu-inline-asm"),
-flagpd1("fgnu-keywords"),
-flagpd1("fgnu-runtime"),
-flagpd1("fgpu-allow-device-init"),
-flagpd1("fgpu-rdc"),
-flagpd1("fheinous-gnu-extensions"),
-flagpd1("fhip-dump-offload-linker-script"),
-flagpd1("fhip-new-launch-api"),
-flagpd1("fhonor-infinities"),
-flagpd1("fhonor-nans"),
-flagpd1("fhosted"),
-sepd1("filelist"),
-sepd1("filetype"),
-flagpd1("fimplicit-module-maps"),
-flagpd1("fimplicit-modules"),
-flagpd1("finclude-default-header"),
-flagpd1("finline"),
-flagpd1("finline-functions"),
-flagpd1("finline-hint-functions"),
-flagpd1("finline-limit"),
-flagpd1("fno-inline-limit"),
-flagpd1("finstrument-function-entry-bare"),
-flagpd1("finstrument-functions"),
-flagpd1("finstrument-functions-after-inlining"),
-flagpd1("fintegrated-as"),
-flagpd1("fintegrated-cc1"),
-flagpd1("fix-only-warnings"),
-flagpd1("fix-what-you-can"),
-flagpd1("ffixed-form"),
-flagpd1("fno-fixed-form"),
-flagpd1("fixit"),
-flagpd1("fixit-recompile"),
-flagpd1("fixit-to-temporary"),
-flagpd1("fjump-tables"),
-flagpd1("fkeep-static-consts"),
-flagpd1("flat_namespace"),
-flagpd1("flax-vector-conversions"),
-flagpd1("flimit-debug-info"),
-flagpd1("ffloat-store"),
-flagpd1("fno-float-store"),
-flagpd1("flto"),
-flagpd1("flto-unit"),
-flagpd1("flto-visibility-public-std"),
-sepd1("fmacro-backtrace-limit"),
-flagpd1("fmath-errno"),
-flagpd1("fmerge-all-constants"),
-flagpd1("fmerge-functions"),
-sepd1("fmessage-length"),
-sepd1("fmodule-feature"),
-flagpd1("fmodule-file-deps"),
-sepd1("fmodule-implementation-of"),
-flagpd1("fmodule-map-file-home-is-cwd"),
-flagpd1("fmodule-maps"),
-sepd1("fmodule-name"),
-flagpd1("fmodules"),
-flagpd1("fmodules-codegen"),
-flagpd1("fmodules-debuginfo"),
-flagpd1("fmodules-decluse"),
-flagpd1("fmodules-disable-diagnostic-validation"),
-flagpd1("fmodules-hash-content"),
-flagpd1("fmodules-local-submodule-visibility"),
-flagpd1("fmodules-search-all"),
-flagpd1("fmodules-strict-context-hash"),
-flagpd1("fmodules-strict-decluse"),
-flagpd1("fmodules-ts"),
-sepd1("fmodules-user-build-path"),
-flagpd1("fmodules-validate-input-files-content"),
-flagpd1("fmodules-validate-once-per-build-session"),
-flagpd1("fmodules-validate-system-headers"),
-flagpd1("fms-compatibility"),
-flagpd1("fms-extensions"),
-flagpd1("fms-volatile"),
-flagpd1("fmudflap"),
-flagpd1("fmudflapth"),
-flagpd1("fnative-half-arguments-and-returns"),
-flagpd1("fnative-half-type"),
-flagpd1("fnested-functions"),
-flagpd1("fnext-runtime"),
-.{
- .name = "fno-PIC",
- .syntax = .flag,
- .zig_equivalent = .no_pic,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-flagpd1("fno-PIE"),
-flagpd1("fno-access-control"),
-flagpd1("fno-addrsig"),
-flagpd1("fno-align-functions"),
-flagpd1("fno-aligned-allocation"),
-flagpd1("fno-allow-editor-placeholders"),
-flagpd1("fno-altivec"),
-flagpd1("fno-apple-pragma-pack"),
-flagpd1("fno-application-extension"),
-flagpd1("fno-asm"),
-flagpd1("fno-asm-blocks"),
-flagpd1("fno-associative-math"),
-flagpd1("fno-assume-sane-operator-new"),
-flagpd1("fno-asynchronous-unwind-tables"),
-flagpd1("fno-auto-profile"),
-flagpd1("fno-auto-profile-accurate"),
-flagpd1("fno-autolink"),
-flagpd1("fno-bitfield-type-align"),
-flagpd1("fno-blocks"),
-flagpd1("fno-borland-extensions"),
-flagpd1("fno-builtin"),
-flagpd1("fno-caret-diagnostics"),
-flagpd1("fno-char8_t"),
-flagpd1("fno-color-diagnostics"),
-flagpd1("fno-common"),
-flagpd1("fno-complete-member-pointers"),
-flagpd1("fno-concept-satisfaction-caching"),
-flagpd1("fno-const-strings"),
-flagpd1("fno-constant-cfstrings"),
-flagpd1("fno-coroutines-ts"),
-flagpd1("fno-coverage-mapping"),
-flagpd1("fno-crash-diagnostics"),
-flagpd1("fno-cuda-approx-transcendentals"),
-flagpd1("fno-cuda-flush-denormals-to-zero"),
-flagpd1("fno-cuda-host-device-constexpr"),
-flagpd1("fno-cuda-short-ptr"),
-flagpd1("fno-cxx-exceptions"),
-flagpd1("fno-cxx-modules"),
-flagpd1("fno-c++-static-destructors"),
-flagpd1("fno-data-sections"),
-flagpd1("fno-debug-info-for-profiling"),
-flagpd1("fno-debug-macro"),
-flagpd1("fno-debug-pass-manager"),
-flagpd1("fno-debug-ranges-base-address"),
-flagpd1("fno-debug-types-section"),
-flagpd1("fno-declspec"),
-flagpd1("fno-delayed-template-parsing"),
-flagpd1("fno-delete-null-pointer-checks"),
-flagpd1("fno-deprecated-macro"),
-flagpd1("fno-diagnostics-color"),
-flagpd1("fno-diagnostics-fixit-info"),
-flagpd1("fno-diagnostics-show-hotness"),
-flagpd1("fno-diagnostics-show-note-include-stack"),
-flagpd1("fno-diagnostics-show-option"),
-flagpd1("fno-diagnostics-use-presumed-location"),
-flagpd1("fno-digraphs"),
-flagpd1("fno-discard-value-names"),
-flagpd1("fno-dllexport-inlines"),
-flagpd1("fno-dollars-in-identifiers"),
-flagpd1("fno-double-square-bracket-attributes"),
-flagpd1("fno-dwarf2-cfi-asm"),
-flagpd1("fno-dwarf-directory-asm"),
-flagpd1("fno-elide-constructors"),
-flagpd1("fno-elide-type"),
-flagpd1("fno-eliminate-unused-debug-symbols"),
-flagpd1("fno-emulated-tls"),
-flagpd1("fno-escaping-block-tail-calls"),
-flagpd1("fno-exceptions"),
-flagpd1("fno-experimental-isel"),
-flagpd1("fno-experimental-new-pass-manager"),
-flagpd1("fno-fast-math"),
-flagpd1("fno-fine-grained-bitfield-accesses"),
-flagpd1("fno-finite-math-only"),
-flagpd1("fno-fixed-point"),
-flagpd1("fno-for-scope"),
-flagpd1("fno-force-dwarf-frame"),
-flagpd1("fno-force-emit-vtables"),
-flagpd1("fno-force-enable-int128"),
-flagpd1("fno-function-sections"),
-flagpd1("fno-gnu89-inline"),
-flagpd1("fno-gnu-inline-asm"),
-flagpd1("fno-gnu-keywords"),
-flagpd1("fno-gpu-allow-device-init"),
-flagpd1("fno-gpu-rdc"),
-flagpd1("fno-hip-new-launch-api"),
-flagpd1("fno-honor-infinities"),
-flagpd1("fno-honor-nans"),
-flagpd1("fno-implicit-module-maps"),
-flagpd1("fno-implicit-modules"),
-flagpd1("fno-inline"),
-flagpd1("fno-inline-functions"),
-flagpd1("fno-integrated-as"),
-flagpd1("fno-integrated-cc1"),
-flagpd1("fno-jump-tables"),
-flagpd1("fno-lax-vector-conversions"),
-flagpd1("fno-limit-debug-info"),
-flagpd1("fno-lto"),
-flagpd1("fno-lto-unit"),
-flagpd1("fno-math-builtin"),
-flagpd1("fno-math-errno"),
-flagpd1("fno-max-type-align"),
-flagpd1("fno-merge-all-constants"),
-flagpd1("fno-module-file-deps"),
-flagpd1("fno-module-maps"),
-flagpd1("fno-modules"),
-flagpd1("fno-modules-decluse"),
-flagpd1("fno-modules-error-recovery"),
-flagpd1("fno-modules-global-index"),
-flagpd1("fno-modules-search-all"),
-flagpd1("fno-strict-modules-decluse"),
-flagpd1("fno_modules-validate-input-files-content"),
-flagpd1("fno-modules-validate-system-headers"),
-flagpd1("fno-ms-compatibility"),
-flagpd1("fno-ms-extensions"),
-flagpd1("fno-objc-arc"),
-flagpd1("fno-objc-arc-exceptions"),
-flagpd1("fno-objc-convert-messages-to-runtime-calls"),
-flagpd1("fno-objc-exceptions"),
-flagpd1("fno-objc-infer-related-result-type"),
-flagpd1("fno-objc-legacy-dispatch"),
-flagpd1("fno-objc-nonfragile-abi"),
-flagpd1("fno-objc-weak"),
-flagpd1("fno-omit-frame-pointer"),
-flagpd1("fno-openmp"),
-flagpd1("fno-openmp-cuda-force-full-runtime"),
-flagpd1("fno-openmp-cuda-mode"),
-flagpd1("fno-openmp-optimistic-collapse"),
-flagpd1("fno-openmp-simd"),
-flagpd1("fno-operator-names"),
-flagpd1("fno-optimize-sibling-calls"),
-flagpd1("fno-pack-struct"),
-flagpd1("fno-padding-on-unsigned-fixed-point"),
-flagpd1("fno-pascal-strings"),
-flagpd1("fno-pch-timestamp"),
-flagpd1("fno_pch-validate-input-files-content"),
-flagpd1("fno-pic"),
-flagpd1("fno-pie"),
-flagpd1("fno-plt"),
-flagpd1("fno-preserve-as-comments"),
-flagpd1("fno-profile-arcs"),
-flagpd1("fno-profile-generate"),
-flagpd1("fno-profile-instr-generate"),
-flagpd1("fno-profile-instr-use"),
-flagpd1("fno-profile-sample-accurate"),
-flagpd1("fno-profile-sample-use"),
-flagpd1("fno-profile-use"),
-flagpd1("fno-reciprocal-math"),
-flagpd1("fno-record-command-line"),
-flagpd1("fno-register-global-dtors-with-atexit"),
-flagpd1("fno-relaxed-template-template-args"),
-flagpd1("fno-reroll-loops"),
-flagpd1("fno-rewrite-imports"),
-flagpd1("fno-rewrite-includes"),
-flagpd1("fno-ropi"),
-flagpd1("fno-rounding-math"),
-flagpd1("fno-rtlib-add-rpath"),
-flagpd1("fno-rtti"),
-flagpd1("fno-rtti-data"),
-flagpd1("fno-rwpi"),
-flagpd1("fno-sanitize-address-poison-custom-array-cookie"),
-flagpd1("fno-sanitize-address-use-after-scope"),
-flagpd1("fno-sanitize-address-use-odr-indicator"),
-flagpd1("fno-sanitize-blacklist"),
-flagpd1("fno-sanitize-cfi-canonical-jump-tables"),
-flagpd1("fno-sanitize-cfi-cross-dso"),
-flagpd1("fno-sanitize-link-c++-runtime"),
-flagpd1("fno-sanitize-link-runtime"),
-flagpd1("fno-sanitize-memory-track-origins"),
-flagpd1("fno-sanitize-memory-use-after-dtor"),
-flagpd1("fno-sanitize-minimal-runtime"),
-flagpd1("fno-sanitize-recover"),
-flagpd1("fno-sanitize-stats"),
-flagpd1("fno-sanitize-thread-atomics"),
-flagpd1("fno-sanitize-thread-func-entry-exit"),
-flagpd1("fno-sanitize-thread-memory-access"),
-flagpd1("fno-sanitize-undefined-trap-on-error"),
-flagpd1("fno-save-optimization-record"),
-flagpd1("fno-short-enums"),
-flagpd1("fno-short-wchar"),
-flagpd1("fno-show-column"),
-flagpd1("fno-show-source-location"),
-flagpd1("fno-signaling-math"),
-flagpd1("fno-signed-char"),
-flagpd1("fno-signed-wchar"),
-flagpd1("fno-signed-zeros"),
-flagpd1("fno-sized-deallocation"),
-flagpd1("fno-slp-vectorize"),
-flagpd1("fno-spell-checking"),
-flagpd1("fno-split-dwarf-inlining"),
-flagpd1("fno-split-lto-unit"),
-flagpd1("fno-stack-protector"),
-flagpd1("fno-stack-size-section"),
-flagpd1("fno-standalone-debug"),
-flagpd1("fno-strict-aliasing"),
-flagpd1("fno-strict-enums"),
-flagpd1("fno-strict-float-cast-overflow"),
-flagpd1("fno-strict-overflow"),
-flagpd1("fno-strict-return"),
-flagpd1("fno-strict-vtable-pointers"),
-flagpd1("fno-struct-path-tbaa"),
-flagpd1("fno-temp-file"),
-flagpd1("fno-threadsafe-statics"),
-flagpd1("fno-trapping-math"),
-flagpd1("fno-trigraphs"),
-flagpd1("fno-unique-section-names"),
-flagpd1("fno-unit-at-a-time"),
-flagpd1("fno-unroll-loops"),
-flagpd1("fno-unsafe-math-optimizations"),
-flagpd1("fno-unsigned-char"),
-flagpd1("fno-unwind-tables"),
-flagpd1("fno-use-cxa-atexit"),
-flagpd1("fno-use-init-array"),
-flagpd1("fno-use-line-directives"),
-flagpd1("fno-validate-pch"),
-flagpd1("fno-var-tracking"),
-flagpd1("fno-vectorize"),
-flagpd1("fno-verbose-asm"),
-flagpd1("fno-virtual-function_elimination"),
-flagpd1("fno-wchar"),
-flagpd1("fno-whole-program-vtables"),
-flagpd1("fno-working-directory"),
-flagpd1("fno-wrapv"),
-flagpd1("fno-zero-initialized-in-bss"),
-flagpd1("fno-zvector"),
-flagpd1("fnoopenmp-relocatable-target"),
-flagpd1("fnoopenmp-use-tls"),
-flagpd1("fno-xray-always-emit-customevents"),
-flagpd1("fno-xray-always-emit-typedevents"),
-flagpd1("fno-xray-instrument"),
-flagpd1("fnoxray-link-deps"),
-flagpd1("fobjc-arc"),
-flagpd1("fobjc-arc-exceptions"),
-flagpd1("fobjc-atdefs"),
-flagpd1("fobjc-call-cxx-cdtors"),
-flagpd1("fobjc-convert-messages-to-runtime-calls"),
-flagpd1("fobjc-exceptions"),
-flagpd1("fobjc-gc"),
-flagpd1("fobjc-gc-only"),
-flagpd1("fobjc-infer-related-result-type"),
-flagpd1("fobjc-legacy-dispatch"),
-flagpd1("fobjc-link-runtime"),
-flagpd1("fobjc-new-property"),
-flagpd1("fobjc-nonfragile-abi"),
-flagpd1("fobjc-runtime-has-weak"),
-flagpd1("fobjc-sender-dependent-dispatch"),
-flagpd1("fobjc-subscripting-legacy-runtime"),
-flagpd1("fobjc-weak"),
-flagpd1("fomit-frame-pointer"),
-flagpd1("fopenmp"),
-flagpd1("fopenmp-cuda-force-full-runtime"),
-flagpd1("fopenmp-cuda-mode"),
-flagpd1("fopenmp-enable-irbuilder"),
-sepd1("fopenmp-host-ir-file-path"),
-flagpd1("fopenmp-is-device"),
-flagpd1("fopenmp-optimistic-collapse"),
-flagpd1("fopenmp-relocatable-target"),
-flagpd1("fopenmp-simd"),
-flagpd1("fopenmp-use-tls"),
-sepd1("foperator-arrow-depth"),
-flagpd1("foptimize-sibling-calls"),
-flagpd1("force_cpusubtype_ALL"),
-flagpd1("force_flat_namespace"),
-sepd1("force_load"),
-flagpd1("forder-file-instrumentation"),
-flagpd1("fpack-struct"),
-flagpd1("fpadding-on-unsigned-fixed-point"),
-flagpd1("fparse-all-comments"),
-flagpd1("fpascal-strings"),
-flagpd1("fpcc-struct-return"),
-flagpd1("fpch-preprocess"),
-flagpd1("fpch-validate-input-files-content"),
-flagpd1("fpic"),
-flagpd1("fpie"),
-flagpd1("fplt"),
-flagpd1("fpreserve-as-comments"),
-flagpd1("fpreserve-vec3-type"),
-flagpd1("fprofile-arcs"),
-flagpd1("fprofile-generate"),
-flagpd1("fprofile-instr-generate"),
-flagpd1("fprofile-instr-use"),
-sepd1("fprofile-remapping-file"),
-flagpd1("fprofile-sample-accurate"),
-flagpd1("fprofile-sample-use"),
-flagpd1("fprofile-use"),
-.{
- .name = "framework",
- .syntax = .separate,
- .zig_equivalent = .framework,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-flagpd1("freciprocal-math"),
-flagpd1("frecord-command-line"),
-flagpd1("ffree-form"),
-flagpd1("fno-free-form"),
-flagpd1("freg-struct-return"),
-flagpd1("fregister-global-dtors-with-atexit"),
-flagpd1("frelaxed-template-template-args"),
-flagpd1("freroll-loops"),
-flagpd1("fretain-comments-from-system-headers"),
-flagpd1("frewrite-imports"),
-flagpd1("frewrite-includes"),
-sepd1("frewrite-map-file"),
-flagpd1("ffriend-injection"),
-flagpd1("fno-friend-injection"),
-flagpd1("ffrontend-optimize"),
-flagpd1("fno-frontend-optimize"),
-flagpd1("fropi"),
-flagpd1("frounding-math"),
-flagpd1("frtlib-add-rpath"),
-flagpd1("frtti"),
-flagpd1("frwpi"),
-flagpd1("fsanitize-address-globals-dead-stripping"),
-flagpd1("fsanitize-address-poison-custom-array-cookie"),
-flagpd1("fsanitize-address-use-after-scope"),
-flagpd1("fsanitize-address-use-odr-indicator"),
-flagpd1("fsanitize-cfi-canonical-jump-tables"),
-flagpd1("fsanitize-cfi-cross-dso"),
-flagpd1("fsanitize-cfi-icall-generalize-pointers"),
-flagpd1("fsanitize-coverage-8bit-counters"),
-flagpd1("fsanitize-coverage-indirect-calls"),
-flagpd1("fsanitize-coverage-inline-8bit-counters"),
-flagpd1("fsanitize-coverage-no-prune"),
-flagpd1("fsanitize-coverage-pc-table"),
-flagpd1("fsanitize-coverage-stack-depth"),
-flagpd1("fsanitize-coverage-trace-bb"),
-flagpd1("fsanitize-coverage-trace-cmp"),
-flagpd1("fsanitize-coverage-trace-div"),
-flagpd1("fsanitize-coverage-trace-gep"),
-flagpd1("fsanitize-coverage-trace-pc"),
-flagpd1("fsanitize-coverage-trace-pc-guard"),
-flagpd1("fsanitize-link-c++-runtime"),
-flagpd1("fsanitize-link-runtime"),
-flagpd1("fsanitize-memory-track-origins"),
-flagpd1("fsanitize-memory-use-after-dtor"),
-flagpd1("fsanitize-minimal-runtime"),
-flagpd1("fsanitize-recover"),
-flagpd1("fsanitize-stats"),
-flagpd1("fsanitize-thread-atomics"),
-flagpd1("fsanitize-thread-func-entry-exit"),
-flagpd1("fsanitize-thread-memory-access"),
-flagpd1("fsanitize-undefined-trap-on-error"),
-flagpd1("fsave-optimization-record"),
-flagpd1("fseh-exceptions"),
-flagpd1("fshort-enums"),
-flagpd1("fshort-wchar"),
-flagpd1("fshow-column"),
-flagpd1("fshow-source-location"),
-flagpd1("fsignaling-math"),
-flagpd1("fsigned-bitfields"),
-flagpd1("fsigned-char"),
-flagpd1("fsigned-wchar"),
-flagpd1("fsigned-zeros"),
-flagpd1("fsized-deallocation"),
-flagpd1("fsjlj-exceptions"),
-flagpd1("fslp-vectorize"),
-flagpd1("fspell-checking"),
-sepd1("fspell-checking-limit"),
-flagpd1("fsplit-dwarf-inlining"),
-flagpd1("fsplit-lto-unit"),
-flagpd1("fsplit-stack"),
-flagpd1("fstack-protector"),
-flagpd1("fstack-protector-all"),
-flagpd1("fstack-protector-strong"),
-flagpd1("fstack-size-section"),
-flagpd1("fstandalone-debug"),
-flagpd1("fstrict-aliasing"),
-flagpd1("fstrict-enums"),
-flagpd1("fstrict-float-cast-overflow"),
-flagpd1("fstrict-overflow"),
-flagpd1("fstrict-return"),
-flagpd1("fstrict-vtable-pointers"),
-flagpd1("fstruct-path-tbaa"),
-flagpd1("fsycl-is-device"),
-flagpd1("fsyntax-only"),
-sepd1("ftabstop"),
-sepd1("ftemplate-backtrace-limit"),
-sepd1("ftemplate-depth"),
-flagpd1("ftest-coverage"),
-flagpd1("fthreadsafe-statics"),
-flagpd1("ftime-report"),
-flagpd1("ftime-trace"),
-flagpd1("ftrapping-math"),
-flagpd1("ftrapv"),
-sepd1("ftrapv-handler"),
-flagpd1("ftrigraphs"),
-sepd1("ftype-visibility"),
-sepd1("function-alignment"),
-flagpd1("ffunction-attribute-list"),
-flagpd1("fno-function-attribute-list"),
-flagpd1("funique-section-names"),
-flagpd1("funit-at-a-time"),
-flagpd1("funknown-anytype"),
-flagpd1("funroll-loops"),
-flagpd1("funsafe-math-optimizations"),
-flagpd1("funsigned-bitfields"),
-flagpd1("funsigned-char"),
-flagpd1("funwind-tables"),
-flagpd1("fuse-cxa-atexit"),
-flagpd1("fuse-init-array"),
-flagpd1("fuse-line-directives"),
-flagpd1("fuse-register-sized-bitfield-access"),
-flagpd1("fvalidate-ast-input-files-content"),
-flagpd1("fvectorize"),
-flagpd1("fverbose-asm"),
-flagpd1("fvirtual-function-elimination"),
-sepd1("fvisibility"),
-flagpd1("fvisibility-global-new-delete-hidden"),
-flagpd1("fvisibility-inlines-hidden"),
-flagpd1("fvisibility-ms-compat"),
-flagpd1("fwasm-exceptions"),
-flagpd1("fwhole-program-vtables"),
-flagpd1("fwrapv"),
-flagpd1("fwritable-strings"),
-flagpd1("fxray-always-emit-customevents"),
-flagpd1("fxray-always-emit-typedevents"),
-flagpd1("fxray-instrument"),
-flagpd1("fxray-link-deps"),
-flagpd1("fzero-initialized-in-bss"),
-flagpd1("fzvector"),
-flagpd1("g0"),
-flagpd1("g1"),
-flagpd1("g2"),
-flagpd1("g3"),
-.{
- .name = "g",
- .syntax = .flag,
- .zig_equivalent = .debug,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-sepd1("gcc-toolchain"),
-flagpd1("gcodeview"),
-flagpd1("gcodeview-ghash"),
-flagpd1("gcolumn-info"),
-flagpd1("fgcse-after-reload"),
-flagpd1("fno-gcse-after-reload"),
-flagpd1("fgcse"),
-flagpd1("fno-gcse"),
-flagpd1("fgcse-las"),
-flagpd1("fno-gcse-las"),
-flagpd1("fgcse-sm"),
-flagpd1("fno-gcse-sm"),
-flagpd1("gdwarf"),
-flagpd1("gdwarf-2"),
-flagpd1("gdwarf-3"),
-flagpd1("gdwarf-4"),
-flagpd1("gdwarf-5"),
-flagpd1("gdwarf-aranges"),
-flagpd1("gembed-source"),
-sepd1("gen-cdb-fragment-path"),
-flagpd1("gen-reproducer"),
-flagpd1("gfull"),
-flagpd1("ggdb"),
-flagpd1("ggdb0"),
-flagpd1("ggdb1"),
-flagpd1("ggdb2"),
-flagpd1("ggdb3"),
-flagpd1("ggnu-pubnames"),
-flagpd1("ginline-line-tables"),
-flagpd1("gline-directives-only"),
-flagpd1("gline-tables-only"),
-flagpd1("glldb"),
-flagpd1("gmlt"),
-flagpd1("gmodules"),
-flagpd1("gno-codeview-ghash"),
-flagpd1("gno-column-info"),
-flagpd1("gno-embed-source"),
-flagpd1("gno-gnu-pubnames"),
-flagpd1("gno-inline-line-tables"),
-flagpd1("gno-pubnames"),
-flagpd1("gno-record-command-line"),
-flagpd1("gno-strict-dwarf"),
-flagpd1("fgnu"),
-flagpd1("fno-gnu"),
-flagpd1("gpubnames"),
-flagpd1("grecord-command-line"),
-flagpd1("gsce"),
-flagpd1("gsplit-dwarf"),
-flagpd1("gstrict-dwarf"),
-flagpd1("gtoggle"),
-flagpd1("gused"),
-flagpd1("gz"),
-sepd1("header-include-file"),
-.{
- .name = "help",
- .syntax = .flag,
- .zig_equivalent = .driver_punt,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "hip-link",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-sepd1("image_base"),
-flagpd1("fimplement-inlines"),
-flagpd1("fno-implement-inlines"),
-flagpd1("fimplicit-none"),
-flagpd1("fno-implicit-none"),
-flagpd1("fimplicit-templates"),
-flagpd1("fno-implicit-templates"),
-sepd1("imultilib"),
-sepd1("include-pch"),
-flagpd1("index-header-map"),
-sepd1("init"),
-flagpd1("finit-local-zero"),
-flagpd1("fno-init-local-zero"),
-flagpd1("init-only"),
-flagpd1("finline-functions-called-once"),
-flagpd1("fno-inline-functions-called-once"),
-flagpd1("finline-small-functions"),
-flagpd1("fno-inline-small-functions"),
-sepd1("install_name"),
-flagpd1("finteger-4-integer-8"),
-flagpd1("fno-integer-4-integer-8"),
-flagpd1("fintrinsic-modules-path"),
-flagpd1("fno-intrinsic-modules-path"),
-flagpd1("fipa-cp"),
-flagpd1("fno-ipa-cp"),
-flagpd1("fivopts"),
-flagpd1("fno-ivopts"),
-flagpd1("keep_private_externs"),
-sepd1("lazy_framework"),
-sepd1("lazy_library"),
-sepd1("load"),
-flagpd1("m16"),
-flagpd1("m32"),
-flagpd1("m3dnow"),
-flagpd1("m3dnowa"),
-flagpd1("m64"),
-flagpd1("m80387"),
-flagpd1("mabi=ieeelongdouble"),
-flagpd1("mabicalls"),
-flagpd1("madx"),
-flagpd1("maes"),
-sepd1("main-file-name"),
-flagpd1("malign-double"),
-flagpd1("maltivec"),
-flagpd1("marm"),
-flagpd1("masm-verbose"),
-flagpd1("massembler-fatal-warnings"),
-flagpd1("massembler-no-warn"),
-flagpd1("matomics"),
-flagpd1("mavx"),
-flagpd1("mavx2"),
-flagpd1("mavx512bf16"),
-flagpd1("mavx512bitalg"),
-flagpd1("mavx512bw"),
-flagpd1("mavx512cd"),
-flagpd1("mavx512dq"),
-flagpd1("mavx512er"),
-flagpd1("mavx512f"),
-flagpd1("mavx512ifma"),
-flagpd1("mavx512pf"),
-flagpd1("mavx512vbmi"),
-flagpd1("mavx512vbmi2"),
-flagpd1("mavx512vl"),
-flagpd1("mavx512vnni"),
-flagpd1("mavx512vp2intersect"),
-flagpd1("mavx512vpopcntdq"),
-flagpd1("fmax-identifier-length"),
-flagpd1("fno-max-identifier-length"),
-flagpd1("mbackchain"),
-flagpd1("mbig-endian"),
-flagpd1("mbmi"),
-flagpd1("mbmi2"),
-flagpd1("mbranch-likely"),
-flagpd1("mbranch-target-enforce"),
-flagpd1("mbranches-within-32B-boundaries"),
-flagpd1("mbulk-memory"),
-flagpd1("mcheck-zero-division"),
-flagpd1("mcldemote"),
-flagpd1("mclflushopt"),
-flagpd1("mclwb"),
-flagpd1("mclzero"),
-flagpd1("mcmodel=medany"),
-flagpd1("mcmodel=medlow"),
-flagpd1("mcmpb"),
-flagpd1("mcmse"),
-sepd1("mcode-model"),
-flagpd1("mcode-object-v3"),
-flagpd1("mconstant-cfstrings"),
-flagpd1("mconstructor-aliases"),
-flagpd1("mcpu=?"),
-flagpd1("mcrbits"),
-flagpd1("mcrc"),
-flagpd1("mcumode"),
-flagpd1("mcx16"),
-sepd1("mdebug-pass"),
-flagpd1("mdirect-move"),
-flagpd1("mdisable-tail-calls"),
-flagpd1("mdouble-float"),
-flagpd1("mdsp"),
-flagpd1("mdspr2"),
-sepd1("meabi"),
-flagpd1("membedded-data"),
-flagpd1("menable-no-infs"),
-flagpd1("menable-no-nans"),
-flagpd1("menable-unsafe-fp-math"),
-flagpd1("menqcmd"),
-flagpd1("fmerge-constants"),
-flagpd1("fno-merge-constants"),
-flagpd1("mexception-handling"),
-flagpd1("mexecute-only"),
-flagpd1("mextern-sdata"),
-flagpd1("mf16c"),
-flagpd1("mfancy-math-387"),
-flagpd1("mfentry"),
-flagpd1("mfix-and-continue"),
-flagpd1("mfix-cortex-a53-835769"),
-flagpd1("mfloat128"),
-sepd1("mfloat-abi"),
-flagpd1("mfma"),
-flagpd1("mfma4"),
-flagpd1("mfp32"),
-flagpd1("mfp64"),
-sepd1("mfpmath"),
-flagpd1("mfprnd"),
-flagpd1("mfpxx"),
-flagpd1("mfsgsbase"),
-flagpd1("mfxsr"),
-flagpd1("mgeneral-regs-only"),
-flagpd1("mgfni"),
-flagpd1("mginv"),
-flagpd1("mglibc"),
-flagpd1("mglobal-merge"),
-flagpd1("mgpopt"),
-flagpd1("mhard-float"),
-flagpd1("mhvx"),
-flagpd1("mhtm"),
-flagpd1("miamcu"),
-flagpd1("mieee-fp"),
-flagpd1("mieee-rnd-near"),
-flagpd1("migrate"),
-flagpd1("no-finalize-removal"),
-flagpd1("no-ns-alloc-error"),
-flagpd1("mimplicit-float"),
-flagpd1("mincremental-linker-compatible"),
-flagpd1("minline-all-stringops"),
-flagpd1("minvariant-function-descriptors"),
-flagpd1("minvpcid"),
-flagpd1("mips1"),
-flagpd1("mips16"),
-flagpd1("mips2"),
-flagpd1("mips3"),
-flagpd1("mips32"),
-flagpd1("mips32r2"),
-flagpd1("mips32r3"),
-flagpd1("mips32r5"),
-flagpd1("mips32r6"),
-flagpd1("mips4"),
-flagpd1("mips5"),
-flagpd1("mips64"),
-flagpd1("mips64r2"),
-flagpd1("mips64r3"),
-flagpd1("mips64r5"),
-flagpd1("mips64r6"),
-flagpd1("misel"),
-flagpd1("mkernel"),
-flagpd1("mldc1-sdc1"),
-sepd1("mlimit-float-precision"),
-sepd1("mlink-bitcode-file"),
-sepd1("mlink-builtin-bitcode"),
-sepd1("mlink-cuda-bitcode"),
-flagpd1("mlittle-endian"),
-sepd1("mllvm"),
-flagpd1("mlocal-sdata"),
-flagpd1("mlong-calls"),
-flagpd1("mlong-double-128"),
-flagpd1("mlong-double-64"),
-flagpd1("mlong-double-80"),
-flagpd1("mlongcall"),
-flagpd1("mlwp"),
-flagpd1("mlzcnt"),
-flagpd1("mmadd4"),
-flagpd1("mmemops"),
-flagpd1("mmfcrf"),
-flagpd1("mmfocrf"),
-flagpd1("mmicromips"),
-flagpd1("mmmx"),
-flagpd1("mmovbe"),
-flagpd1("mmovdir64b"),
-flagpd1("mmovdiri"),
-flagpd1("mmpx"),
-flagpd1("mms-bitfields"),
-flagpd1("mmsa"),
-flagpd1("mmt"),
-flagpd1("mmultivalue"),
-flagpd1("mmutable-globals"),
-flagpd1("mmwaitx"),
-flagpd1("mno-3dnow"),
-flagpd1("mno-3dnowa"),
-flagpd1("mno-80387"),
-flagpd1("mno-abicalls"),
-flagpd1("mno-adx"),
-flagpd1("mno-aes"),
-flagpd1("mno-altivec"),
-flagpd1("mno-atomics"),
-flagpd1("mno-avx"),
-flagpd1("mno-avx2"),
-flagpd1("mno-avx512bf16"),
-flagpd1("mno-avx512bitalg"),
-flagpd1("mno-avx512bw"),
-flagpd1("mno-avx512cd"),
-flagpd1("mno-avx512dq"),
-flagpd1("mno-avx512er"),
-flagpd1("mno-avx512f"),
-flagpd1("mno-avx512ifma"),
-flagpd1("mno-avx512pf"),
-flagpd1("mno-avx512vbmi"),
-flagpd1("mno-avx512vbmi2"),
-flagpd1("mno-avx512vl"),
-flagpd1("mno-avx512vnni"),
-flagpd1("mno-avx512vp2intersect"),
-flagpd1("mno-avx512vpopcntdq"),
-flagpd1("mno-backchain"),
-flagpd1("mno-bmi"),
-flagpd1("mno-bmi2"),
-flagpd1("mno-branch-likely"),
-flagpd1("mno-bulk-memory"),
-flagpd1("mno-check-zero-division"),
-flagpd1("mno-cldemote"),
-flagpd1("mno-clflushopt"),
-flagpd1("mno-clwb"),
-flagpd1("mno-clzero"),
-flagpd1("mno-cmpb"),
-flagpd1("mno-code-object-v3"),
-flagpd1("mno-constant-cfstrings"),
-flagpd1("mno-crbits"),
-flagpd1("mno-crc"),
-flagpd1("mno-cumode"),
-flagpd1("mno-cx16"),
-flagpd1("mno-dsp"),
-flagpd1("mno-dspr2"),
-flagpd1("mno-embedded-data"),
-flagpd1("mno-enqcmd"),
-flagpd1("mno-exception-handling"),
-flagpd1("mnoexecstack"),
-flagpd1("mno-execute-only"),
-flagpd1("mno-extern-sdata"),
-flagpd1("mno-f16c"),
-flagpd1("mno-fix-cortex-a53-835769"),
-flagpd1("mno-float128"),
-flagpd1("mno-fma"),
-flagpd1("mno-fma4"),
-flagpd1("mno-fprnd"),
-flagpd1("mno-fsgsbase"),
-flagpd1("mno-fxsr"),
-flagpd1("mno-gfni"),
-flagpd1("mno-ginv"),
-flagpd1("mno-global-merge"),
-flagpd1("mno-gpopt"),
-flagpd1("mno-hvx"),
-flagpd1("mno-htm"),
-flagpd1("mno-iamcu"),
-flagpd1("mno-implicit-float"),
-flagpd1("mno-incremental-linker-compatible"),
-flagpd1("mno-inline-all-stringops"),
-flagpd1("mno-invariant-function-descriptors"),
-flagpd1("mno-invpcid"),
-flagpd1("mno-isel"),
-flagpd1("mno-ldc1-sdc1"),
-flagpd1("mno-local-sdata"),
-flagpd1("mno-long-calls"),
-flagpd1("mno-longcall"),
-flagpd1("mno-lwp"),
-flagpd1("mno-lzcnt"),
-flagpd1("mno-madd4"),
-flagpd1("mno-memops"),
-flagpd1("mno-mfcrf"),
-flagpd1("mno-mfocrf"),
-flagpd1("mno-micromips"),
-flagpd1("mno-mips16"),
-flagpd1("mno-mmx"),
-flagpd1("mno-movbe"),
-flagpd1("mno-movdir64b"),
-flagpd1("mno-movdiri"),
-flagpd1("mno-movt"),
-flagpd1("mno-mpx"),
-flagpd1("mno-ms-bitfields"),
-flagpd1("mno-msa"),
-flagpd1("mno-mt"),
-flagpd1("mno-multivalue"),
-flagpd1("mno-mutable-globals"),
-flagpd1("mno-mwaitx"),
-flagpd1("mno-neg-immediates"),
-flagpd1("mno-nontrapping-fptoint"),
-flagpd1("mno-nvj"),
-flagpd1("mno-nvs"),
-flagpd1("mno-odd-spreg"),
-flagpd1("mno-omit-leaf-frame-pointer"),
-flagpd1("mno-outline"),
-flagpd1("mno-packed-stack"),
-flagpd1("mno-packets"),
-flagpd1("mno-pascal-strings"),
-flagpd1("mno-pclmul"),
-flagpd1("mno-pconfig"),
-flagpd1("mno-pie-copy-relocations"),
-flagpd1("mno-pku"),
-flagpd1("mno-popcnt"),
-flagpd1("mno-popcntd"),
-flagpd1("mno-power8-vector"),
-flagpd1("mno-power9-vector"),
-flagpd1("mno-prefetchwt1"),
-flagpd1("mno-prfchw"),
-flagpd1("mno-ptwrite"),
-flagpd1("mno-pure-code"),
-flagpd1("mno-qpx"),
-flagpd1("mno-rdpid"),
-flagpd1("mno-rdrnd"),
-flagpd1("mno-rdseed"),
-flagpd1("mno-red-zone"),
-flagpd1("mno-reference-types"),
-flagpd1("mno-relax"),
-flagpd1("mno-relax-all"),
-flagpd1("mno-relax-pic-calls"),
-flagpd1("mno-restrict-it"),
-flagpd1("mno-retpoline"),
-flagpd1("mno-retpoline-external-thunk"),
-flagpd1("mno-rtd"),
-flagpd1("mno-rtm"),
-flagpd1("mno-sahf"),
-flagpd1("mno-save-restore"),
-flagpd1("mno-sgx"),
-flagpd1("mno-sha"),
-flagpd1("mno-shstk"),
-flagpd1("mno-sign-ext"),
-flagpd1("mno-simd128"),
-flagpd1("mno-soft-float"),
-flagpd1("mno-spe"),
-flagpd1("mno-speculative-load-hardening"),
-flagpd1("mno-sram-ecc"),
-flagpd1("mno-sse"),
-flagpd1("mno-sse2"),
-flagpd1("mno-sse3"),
-flagpd1("mno-sse4"),
-flagpd1("mno-sse4.1"),
-flagpd1("mno-sse4.2"),
-flagpd1("mno-sse4a"),
-flagpd1("mno-ssse3"),
-flagpd1("mno-stack-arg-probe"),
-flagpd1("mno-stackrealign"),
-flagpd1("mno-tail-call"),
-flagpd1("mno-tbm"),
-flagpd1("mno-thumb"),
-flagpd1("mno-tls-direct-seg-refs"),
-flagpd1("mno-unaligned-access"),
-flagpd1("mno-unimplemented-simd128"),
-flagpd1("mno-vaes"),
-flagpd1("mno-virt"),
-flagpd1("mno-vpclmulqdq"),
-flagpd1("mno-vsx"),
-flagpd1("mno-vx"),
-flagpd1("mno-vzeroupper"),
-flagpd1("mno-waitpkg"),
-flagpd1("mno-warn-nonportable-cfstrings"),
-flagpd1("mno-wavefrontsize64"),
-flagpd1("mno-wbnoinvd"),
-flagpd1("mno-x87"),
-flagpd1("mno-xgot"),
-flagpd1("mno-xnack"),
-flagpd1("mno-xop"),
-flagpd1("mno-xsave"),
-flagpd1("mno-xsavec"),
-flagpd1("mno-xsaveopt"),
-flagpd1("mno-xsaves"),
-flagpd1("mno-zero-initialized-in-bss"),
-flagpd1("mno-zvector"),
-flagpd1("mnocrc"),
-flagpd1("mno-direct-move"),
-flagpd1("mnontrapping-fptoint"),
-flagpd1("mnop-mcount"),
-flagpd1("mno-crypto"),
-flagpd1("mnvj"),
-flagpd1("mnvs"),
-flagpd1("modd-spreg"),
-sepd1("module-dependency-dir"),
-flagpd1("module-file-deps"),
-flagpd1("module-file-info"),
-flagpd1("fmodule-private"),
-flagpd1("fno-module-private"),
-flagpd1("fmodulo-sched-allow-regmoves"),
-flagpd1("fno-modulo-sched-allow-regmoves"),
-flagpd1("fmodulo-sched"),
-flagpd1("fno-modulo-sched"),
-flagpd1("momit-leaf-frame-pointer"),
-flagpd1("moutline"),
-flagpd1("mpacked-stack"),
-flagpd1("mpackets"),
-flagpd1("mpascal-strings"),
-flagpd1("mpclmul"),
-flagpd1("mpconfig"),
-flagpd1("mpie-copy-relocations"),
-flagpd1("mpku"),
-flagpd1("mpopcnt"),
-flagpd1("mpopcntd"),
-flagpd1("mcrypto"),
-flagpd1("mpower8-vector"),
-flagpd1("mpower9-vector"),
-flagpd1("mprefetchwt1"),
-flagpd1("mprfchw"),
-flagpd1("mptwrite"),
-flagpd1("mpure-code"),
-flagpd1("mqdsp6-compat"),
-flagpd1("mqpx"),
-flagpd1("mrdpid"),
-flagpd1("mrdrnd"),
-flagpd1("mrdseed"),
-flagpd1("mreassociate"),
-flagpd1("mrecip"),
-flagpd1("mrecord-mcount"),
-flagpd1("mred-zone"),
-flagpd1("mreference-types"),
-sepd1("mregparm"),
-flagpd1("mrelax"),
-flagpd1("mrelax-all"),
-flagpd1("mrelax-pic-calls"),
-.{
- .name = "mrelax-relocations",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-sepd1("mrelocation-model"),
-flagpd1("mrestrict-it"),
-flagpd1("mretpoline"),
-flagpd1("mretpoline-external-thunk"),
-flagpd1("mrtd"),
-flagpd1("mrtm"),
-flagpd1("msahf"),
-flagpd1("msave-restore"),
-flagpd1("msave-temp-labels"),
-flagpd1("msecure-plt"),
-flagpd1("msgx"),
-flagpd1("msha"),
-flagpd1("mshstk"),
-flagpd1("msign-ext"),
-flagpd1("msimd128"),
-flagpd1("msingle-float"),
-flagpd1("msoft-float"),
-flagpd1("mspe"),
-flagpd1("mspeculative-load-hardening"),
-flagpd1("msram-ecc"),
-flagpd1("msse"),
-flagpd1("msse2"),
-flagpd1("msse3"),
-flagpd1("msse4"),
-flagpd1("msse4.1"),
-flagpd1("msse4.2"),
-flagpd1("msse4a"),
-flagpd1("mssse3"),
-flagpd1("mstack-arg-probe"),
-flagpd1("mstackrealign"),
-flagpd1("mstrict-align"),
-sepd1("mt-migrate-directory"),
-flagpd1("mtail-call"),
-flagpd1("mtbm"),
-sepd1("mthread-model"),
-flagpd1("mthumb"),
-flagpd1("mtls-direct-seg-refs"),
-sepd1("mtp"),
-flagpd1("mtune=?"),
-flagpd1("muclibc"),
-flagpd1("multi_module"),
-sepd1("multiply_defined"),
-sepd1("multiply_defined_unused"),
-flagpd1("munaligned-access"),
-flagpd1("munimplemented-simd128"),
-flagpd1("munwind-tables"),
-flagpd1("mv5"),
-flagpd1("mv55"),
-flagpd1("mv60"),
-flagpd1("mv62"),
-flagpd1("mv65"),
-flagpd1("mv66"),
-flagpd1("mvaes"),
-flagpd1("mvirt"),
-flagpd1("mvpclmulqdq"),
-flagpd1("mvsx"),
-flagpd1("mvx"),
-flagpd1("mvzeroupper"),
-flagpd1("mwaitpkg"),
-flagpd1("mwarn-nonportable-cfstrings"),
-flagpd1("mwavefrontsize64"),
-flagpd1("mwbnoinvd"),
-flagpd1("mx32"),
-flagpd1("mx87"),
-flagpd1("mxgot"),
-flagpd1("mxnack"),
-flagpd1("mxop"),
-flagpd1("mxsave"),
-flagpd1("mxsavec"),
-flagpd1("mxsaveopt"),
-flagpd1("mxsaves"),
-flagpd1("mzvector"),
-flagpd1("n"),
-flagpd1("new-struct-path-tbaa"),
-flagpd1("no_dead_strip_inits_and_terms"),
-flagpd1("no-canonical-prefixes"),
-flagpd1("no-code-completion-globals"),
-flagpd1("no-code-completion-ns-level-decls"),
-flagpd1("no-cpp-precomp"),
-.{
- .name = "no-cuda-noopt-device-debug",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "no-cuda-version-check",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("no-emit-llvm-uselists"),
-flagpd1("no-implicit-float"),
-.{
- .name = "no-integrated-cpp",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "no-pedantic",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("no-pie"),
-flagpd1("no-pthread"),
-flagpd1("no-struct-path-tbaa"),
-flagpd1("nobuiltininc"),
-flagpd1("nocpp"),
-flagpd1("nocudainc"),
-flagpd1("nodefaultlibs"),
-flagpd1("nofixprebinding"),
-flagpd1("nogpulib"),
-.{
- .name = "nolibc",
- .syntax = .flag,
- .zig_equivalent = .nostdlib,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-flagpd1("nomultidefs"),
-flagpd1("fnon-call-exceptions"),
-flagpd1("fno-non-call-exceptions"),
-flagpd1("nopie"),
-flagpd1("noprebind"),
-flagpd1("noprofilelib"),
-flagpd1("noseglinkedit"),
-flagpd1("nostartfiles"),
-.{
- .name = "nostdinc",
- .syntax = .flag,
- .zig_equivalent = .nostdlibinc,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "nostdinc++",
- .syntax = .flag,
- .zig_equivalent = .nostdlib_cpp,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "nostdlib",
- .syntax = .flag,
- .zig_equivalent = .nostdlib,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "nostdlibinc",
- .syntax = .flag,
- .zig_equivalent = .nostdlibinc,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "nostdlib++",
- .syntax = .flag,
- .zig_equivalent = .nostdlib_cpp,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-flagpd1("nostdsysteminc"),
-flagpd1("objcmt-atomic-property"),
-flagpd1("objcmt-migrate-all"),
-flagpd1("objcmt-migrate-annotation"),
-flagpd1("objcmt-migrate-designated-init"),
-flagpd1("objcmt-migrate-instancetype"),
-flagpd1("objcmt-migrate-literals"),
-flagpd1("objcmt-migrate-ns-macros"),
-flagpd1("objcmt-migrate-property"),
-flagpd1("objcmt-migrate-property-dot-syntax"),
-flagpd1("objcmt-migrate-protocol-conformance"),
-flagpd1("objcmt-migrate-readonly-property"),
-flagpd1("objcmt-migrate-readwrite-property"),
-flagpd1("objcmt-migrate-subscripting"),
-flagpd1("objcmt-ns-nonatomic-iosonly"),
-flagpd1("objcmt-returns-innerpointer-property"),
-flagpd1("object"),
-sepd1("opt-record-file"),
-sepd1("opt-record-format"),
-sepd1("opt-record-passes"),
-sepd1("output-asm-variant"),
-flagpd1("p"),
-flagpd1("fpack-derived"),
-flagpd1("fno-pack-derived"),
-.{
- .name = "pass-exit-codes",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("pch-through-hdrstop-create"),
-flagpd1("pch-through-hdrstop-use"),
-.{
- .name = "pedantic",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "pedantic-errors",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("fpeel-loops"),
-flagpd1("fno-peel-loops"),
-flagpd1("fpermissive"),
-flagpd1("fno-permissive"),
-flagpd1("pg"),
-flagpd1("pic-is-pie"),
-sepd1("pic-level"),
-flagpd1("pie"),
-.{
- .name = "pipe",
- .syntax = .flag,
- .zig_equivalent = .ignore,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-sepd1("plugin"),
-flagpd1("prebind"),
-flagpd1("prebind_all_twolevel_modules"),
-flagpd1("fprefetch-loop-arrays"),
-flagpd1("fno-prefetch-loop-arrays"),
-flagpd1("preload"),
-flagpd1("print-dependency-directives-minimized-source"),
-.{
- .name = "print-effective-triple",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("print-ivar-layout"),
-.{
- .name = "print-libgcc-file-name",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "print-multi-directory",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "print-multi-lib",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "print-multi-os-directory",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("print-preamble"),
-.{
- .name = "print-resource-dir",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "print-search-dirs",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("print-stats"),
-.{
- .name = "print-supported-cpus",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "print-target-triple",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("fprintf"),
-flagpd1("fno-printf"),
-flagpd1("private_bundle"),
-flagpd1("fprofile-correction"),
-flagpd1("fno-profile-correction"),
-flagpd1("fprofile"),
-flagpd1("fno-profile"),
-flagpd1("fprofile-generate-sampling"),
-flagpd1("fno-profile-generate-sampling"),
-flagpd1("fprofile-reusedist"),
-flagpd1("fno-profile-reusedist"),
-flagpd1("fprofile-values"),
-flagpd1("fno-profile-values"),
-flagpd1("fprotect-parens"),
-flagpd1("fno-protect-parens"),
-flagpd1("pthread"),
-flagpd1("pthreads"),
-flagpd1("r"),
-flagpd1("frange-check"),
-flagpd1("fno-range-check"),
-.{
- .name = "rdynamic",
- .syntax = .flag,
- .zig_equivalent = .rdynamic,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-sepd1("read_only_relocs"),
-flagpd1("freal-4-real-10"),
-flagpd1("fno-real-4-real-10"),
-flagpd1("freal-4-real-16"),
-flagpd1("fno-real-4-real-16"),
-flagpd1("freal-4-real-8"),
-flagpd1("fno-real-4-real-8"),
-flagpd1("freal-8-real-10"),
-flagpd1("fno-real-8-real-10"),
-flagpd1("freal-8-real-16"),
-flagpd1("fno-real-8-real-16"),
-flagpd1("freal-8-real-4"),
-flagpd1("fno-real-8-real-4"),
-flagpd1("frealloc-lhs"),
-flagpd1("fno-realloc-lhs"),
-sepd1("record-command-line"),
-flagpd1("frecursive"),
-flagpd1("fno-recursive"),
-flagpd1("fregs-graph"),
-flagpd1("fno-regs-graph"),
-flagpd1("relaxed-aliasing"),
-.{
- .name = "relocatable-pch",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("remap"),
-sepd1("remap-file"),
-flagpd1("frename-registers"),
-flagpd1("fno-rename-registers"),
-flagpd1("freorder-blocks"),
-flagpd1("fno-reorder-blocks"),
-flagpd1("frepack-arrays"),
-flagpd1("fno-repack-arrays"),
-sepd1("resource-dir"),
-flagpd1("rewrite-legacy-objc"),
-flagpd1("rewrite-macros"),
-flagpd1("rewrite-objc"),
-flagpd1("rewrite-test"),
-flagpd1("fripa"),
-flagpd1("fno-ripa"),
-sepd1("rpath"),
-flagpd1("s"),
-.{
- .name = "save-stats",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "save-temps",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("fschedule-insns2"),
-flagpd1("fno-schedule-insns2"),
-flagpd1("fschedule-insns"),
-flagpd1("fno-schedule-insns"),
-flagpd1("fsecond-underscore"),
-flagpd1("fno-second-underscore"),
-.{
- .name = "sectalign",
- .syntax = .{.multi_arg=3},
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "sectcreate",
- .syntax = .{.multi_arg=3},
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "sectobjectsymbols",
- .syntax = .{.multi_arg=2},
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "sectorder",
- .syntax = .{.multi_arg=3},
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-flagpd1("fsee"),
-flagpd1("fno-see"),
-sepd1("seg_addr_table"),
-sepd1("seg_addr_table_filename"),
-.{
- .name = "segaddr",
- .syntax = .{.multi_arg=2},
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "segcreate",
- .syntax = .{.multi_arg=3},
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-flagpd1("seglinkedit"),
-.{
- .name = "segprot",
- .syntax = .{.multi_arg=3},
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-sepd1("segs_read_only_addr"),
-sepd1("segs_read_write_addr"),
-flagpd1("setup-static-analyzer"),
-.{
- .name = "shared",
- .syntax = .flag,
- .zig_equivalent = .shared,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("shared-libgcc"),
-flagpd1("shared-libsan"),
-flagpd1("show-encoding"),
-.{
- .name = "show-includes",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("show-inst"),
-flagpd1("fsign-zero"),
-flagpd1("fno-sign-zero"),
-flagpd1("fsignaling-nans"),
-flagpd1("fno-signaling-nans"),
-flagpd1("single_module"),
-flagpd1("fsingle-precision-constant"),
-flagpd1("fno-single-precision-constant"),
-flagpd1("fspec-constr-count"),
-flagpd1("fno-spec-constr-count"),
-.{
- .name = "specs",
- .syntax = .separate,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-sepd1("split-dwarf-file"),
-sepd1("split-dwarf-output"),
-flagpd1("split-stacks"),
-flagpd1("fstack-arrays"),
-flagpd1("fno-stack-arrays"),
-flagpd1("fstack-check"),
-flagpd1("fno-stack-check"),
-sepd1("stack-protector"),
-sepd1("stack-protector-buffer-size"),
-.{
- .name = "static",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("static-define"),
-flagpd1("static-libgcc"),
-flagpd1("static-libgfortran"),
-flagpd1("static-libsan"),
-flagpd1("static-libstdc++"),
-flagpd1("static-openmp"),
-flagpd1("static-pie"),
-flagpd1("fstrength-reduce"),
-flagpd1("fno-strength-reduce"),
-flagpd1("sys-header-deps"),
-flagpd1("t"),
-sepd1("target-abi"),
-sepd1("target-cpu"),
-sepd1("target-feature"),
-.{
- .name = "target",
- .syntax = .separate,
- .zig_equivalent = .target,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-sepd1("target-linker-version"),
-flagpd1("templight-dump"),
-flagpd1("test-coverage"),
-flagpd1("time"),
-flagpd1("ftls-model"),
-flagpd1("fno-tls-model"),
-flagpd1("ftracer"),
-flagpd1("fno-tracer"),
-.{
- .name = "traditional",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "traditional-cpp",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("ftree-dce"),
-flagpd1("fno-tree-dce"),
-flagpd1("ftree_loop_im"),
-flagpd1("fno-tree_loop_im"),
-flagpd1("ftree_loop_ivcanon"),
-flagpd1("fno-tree_loop_ivcanon"),
-flagpd1("ftree_loop_linear"),
-flagpd1("fno-tree_loop_linear"),
-flagpd1("ftree-salias"),
-flagpd1("fno-tree-salias"),
-flagpd1("ftree-ter"),
-flagpd1("fno-tree-ter"),
-flagpd1("ftree-vectorizer-verbose"),
-flagpd1("fno-tree-vectorizer-verbose"),
-flagpd1("ftree-vrp"),
-flagpd1("fno-tree-vrp"),
-.{
- .name = "trigraphs",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("trim-egraph"),
-sepd1("triple"),
-flagpd1("twolevel_namespace"),
-flagpd1("twolevel_namespace_hints"),
-sepd1("umbrella"),
-flagpd1("undef"),
-flagpd1("funderscoring"),
-flagpd1("fno-underscoring"),
-sepd1("unexported_symbols_list"),
-flagpd1("funroll-all-loops"),
-flagpd1("fno-unroll-all-loops"),
-flagpd1("funsafe-loop-optimizations"),
-flagpd1("fno-unsafe-loop-optimizations"),
-flagpd1("funswitch-loops"),
-flagpd1("fno-unswitch-loops"),
-flagpd1("fuse-linker-plugin"),
-flagpd1("fno-use-linker-plugin"),
-flagpd1("v"),
-flagpd1("fvariable-expansion-in-unroller"),
-flagpd1("fno-variable-expansion-in-unroller"),
-flagpd1("fvect-cost-model"),
-flagpd1("fno-vect-cost-model"),
-flagpd1("vectorize-loops"),
-flagpd1("vectorize-slp"),
-flagpd1("verify"),
-.{
- .name = "verify-debug-info",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("verify-ignore-unexpected"),
-flagpd1("verify-pch"),
-flagpd1("version"),
-.{
- .name = "via-file-asm",
- .syntax = .flag,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-flagpd1("w"),
-sepd1("weak_framework"),
-sepd1("weak_library"),
-sepd1("weak_reference_mismatches"),
-flagpd1("fweb"),
-flagpd1("fno-web"),
-flagpd1("whatsloaded"),
-flagpd1("fwhole-file"),
-flagpd1("fno-whole-file"),
-flagpd1("fwhole-program"),
-flagpd1("fno-whole-program"),
-flagpd1("whyload"),
-.{
- .name = "z",
- .syntax = .separate,
- .zig_equivalent = .linker_input_z,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-joinpd1("fsanitize-undefined-strip-path-components="),
-joinpd1("fopenmp-cuda-teams-reduction-recs-num="),
-joinpd1("analyzer-config-compatibility-mode="),
-joinpd1("fpatchable-function-entry-offset="),
-joinpd1("analyzer-inline-max-stack-depth="),
-joinpd1("fsanitize-address-field-padding="),
-joinpd1("fdiagnostics-hotness-threshold="),
-joinpd1("fsanitize-memory-track-origins="),
-joinpd1("mwatchos-simulator-version-min="),
-joinpd1("mappletvsimulator-version-min="),
-joinpd1("fobjc-nonfragile-abi-version="),
-joinpd1("fprofile-instrument-use-path="),
-jspd1("fxray-instrumentation-bundle="),
-joinpd1("miphonesimulator-version-min="),
-joinpd1("faddress-space-map-mangling="),
-joinpd1("foptimization-record-passes="),
-joinpd1("ftest-module-file-extension="),
-jspd1("fxray-instruction-threshold="),
-joinpd1("mno-default-build-attributes"),
-joinpd1("mtvos-simulator-version-min="),
-joinpd1("mwatchsimulator-version-min="),
-.{
- .name = "include-with-prefix-before=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("objcmt-white-list-dir-path="),
-joinpd1("error-on-deserialized-decl="),
-joinpd1("fconstexpr-backtrace-limit="),
-joinpd1("fdiagnostics-show-category="),
-joinpd1("fdiagnostics-show-location="),
-joinpd1("fopenmp-cuda-blocks-per-sm="),
-joinpd1("fsanitize-system-blacklist="),
-jspd1("fxray-instruction-threshold"),
-joinpd1("headerpad_max_install_names"),
-joinpd1("mios-simulator-version-min="),
-.{
- .name = "include-with-prefix-after=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("fms-compatibility-version="),
-joinpd1("fopenmp-cuda-number-of-sm="),
-joinpd1("foptimization-record-file="),
-joinpd1("fpatchable-function-entry="),
-joinpd1("fsave-optimization-record="),
-joinpd1("ftemplate-backtrace-limit="),
-.{
- .name = "gpu-max-threads-per-block=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("malign-branch-prefix-size="),
-joinpd1("objcmt-whitelist-dir-path="),
-joinpd1("Wno-nonportable-cfstrings"),
-joinpd1("analyzer-disable-checker="),
-joinpd1("fbuild-session-timestamp="),
-joinpd1("fprofile-instrument-path="),
-joinpd1("mdefault-build-attributes"),
-joinpd1("msign-return-address-key="),
-.{
- .name = "verify-ignore-unexpected=",
- .syntax = .comma_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "include-directory-after=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "compress-debug-sections=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "fcomment-block-commands=",
- .syntax = .comma_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-joinpd1("flax-vector-conversions="),
-joinpd1("fmodules-embed-all-files"),
-joinpd1("fmodules-prune-interval="),
-joinpd1("foverride-record-layout="),
-joinpd1("fprofile-instr-generate="),
-joinpd1("fprofile-remapping-file="),
-joinpd1("fsanitize-coverage-type="),
-joinpd1("fsanitize-hwaddress-abi="),
-joinpd1("ftime-trace-granularity="),
-jspd1("fxray-always-instrument="),
-jspd1("internal-externc-isystem"),
-.{
- .name = "libomptarget-nvptx-path=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "no-system-header-prefix=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "output-class-directory=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("analyzer-inlining-mode="),
-joinpd1("fconstant-string-class="),
-joinpd1("fcrash-diagnostics-dir="),
-joinpd1("fdebug-compilation-dir="),
-joinpd1("fdebug-default-version="),
-joinpd1("ffp-exception-behavior="),
-joinpd1("fmacro-backtrace-limit="),
-joinpd1("fmax-array-constructor="),
-joinpd1("fprofile-exclude-files="),
-joinpd1("ftrivial-auto-var-init="),
-jspd1("fxray-never-instrument="),
-jspd1("interface-stub-version="),
-joinpd1("malign-branch-boundary="),
-joinpd1("mappletvos-version-min="),
-joinpd1("Wnonportable-cfstrings"),
-joinpd1("fdefault-calling-conv="),
-joinpd1("fmax-subrecord-length="),
-joinpd1("fmodules-ignore-macro="),
-.{
- .name = "fno-sanitize-coverage=",
- .syntax = .comma_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-joinpd1("fobjc-dispatch-method="),
-joinpd1("foperator-arrow-depth="),
-joinpd1("fprebuilt-module-path="),
-joinpd1("fprofile-filter-files="),
-joinpd1("fspell-checking-limit="),
-joinpd1("miphoneos-version-min="),
-joinpd1("msmall-data-threshold="),
-joinpd1("Wlarge-by-value-copy="),
-joinpd1("analyzer-constraints="),
-joinpd1("analyzer-dump-egraph="),
-jspd1("compatibility_version"),
-jspd1("dylinker_install_name"),
-joinpd1("fcs-profile-generate="),
-joinpd1("fmodules-prune-after="),
-.{
- .name = "fno-sanitize-recover=",
- .syntax = .comma_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-jspd1("iframeworkwithsysroot"),
-joinpd1("mamdgpu-debugger-abi="),
-joinpd1("mprefer-vector-width="),
-joinpd1("msign-return-address="),
-joinpd1("mwatchos-version-min="),
-.{
- .name = "system-header-prefix=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "include-with-prefix=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("coverage-notes-file="),
-joinpd1("fbuild-session-file="),
-joinpd1("fdiagnostics-format="),
-joinpd1("fmax-stack-var-size="),
-joinpd1("fmodules-cache-path="),
-joinpd1("fmodules-embed-file="),
-joinpd1("fprofile-instrument="),
-joinpd1("fprofile-sample-use="),
-joinpd1("fsanitize-blacklist="),
-.{
- .name = "hip-device-lib-path=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("mmacosx-version-min="),
-.{
- .name = "no-cuda-include-ptx=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("Wframe-larger-than="),
-joinpd1("code-completion-at="),
-joinpd1("coverage-data-file="),
-joinpd1("fblas-matmul-limit="),
-joinpd1("fdiagnostics-color="),
-joinpd1("ffixed-line-length-"),
-joinpd1("flimited-precision="),
-joinpd1("fprofile-instr-use="),
-.{
- .name = "fsanitize-coverage=",
- .syntax = .comma_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-joinpd1("fthin-link-bitcode="),
-joinpd1("mbranch-protection="),
-joinpd1("mmacos-version-min="),
-joinpd1("pch-through-header="),
-joinpd1("target-sdk-version="),
-.{
- .name = "execution-charset:",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "include-directory=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "library-directory=",
- .syntax = .joined,
- .zig_equivalent = .lib_dir,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "config-system-dir=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("fclang-abi-compat="),
-joinpd1("fcompile-resource="),
-joinpd1("fdebug-prefix-map="),
-joinpd1("fdenormal-fp-math="),
-joinpd1("fexcess-precision="),
-joinpd1("ffree-line-length-"),
-joinpd1("fmacro-prefix-map="),
-.{
- .name = "fno-sanitize-trap=",
- .syntax = .comma_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-joinpd1("fobjc-abi-version="),
-joinpd1("foutput-class-dir="),
-joinpd1("fprofile-generate="),
-joinpd1("frewrite-map-file="),
-.{
- .name = "fsanitize-recover=",
- .syntax = .comma_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-joinpd1("fsymbol-partition="),
-joinpd1("mcompact-branches="),
-joinpd1("mstack-probe-size="),
-joinpd1("mtvos-version-min="),
-joinpd1("working-directory="),
-joinpd1("analyze-function="),
-joinpd1("analyzer-checker="),
-joinpd1("coverage-version="),
-.{
- .name = "cuda-include-ptx=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("falign-functions="),
-joinpd1("fconstexpr-depth="),
-joinpd1("fconstexpr-steps="),
-joinpd1("ffile-prefix-map="),
-joinpd1("fmodule-map-file="),
-joinpd1("fobjc-arc-cxxlib="),
-jspd1("iwithprefixbefore"),
-joinpd1("malign-functions="),
-joinpd1("mios-version-min="),
-joinpd1("mstack-alignment="),
-.{
- .name = "no-cuda-gpu-arch=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-jspd1("working-directory"),
-joinpd1("analyzer-output="),
-.{
- .name = "config-user-dir=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("debug-info-kind="),
-joinpd1("debugger-tuning="),
-joinpd1("fcf-runtime-abi="),
-joinpd1("finit-character="),
-joinpd1("fmax-type-align="),
-joinpd1("fmessage-length="),
-.{
- .name = "fopenmp-targets=",
- .syntax = .comma_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-joinpd1("fopenmp-version="),
-joinpd1("fshow-overloads="),
-joinpd1("ftemplate-depth-"),
-joinpd1("ftemplate-depth="),
-jspd1("fxray-attr-list="),
-jspd1("internal-isystem"),
-joinpd1("mlinker-version="),
-.{
- .name = "print-file-name=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "print-prog-name=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-jspd1("stdlib++-isystem"),
-joinpd1("Rpass-analysis="),
-.{
- .name = "Xopenmp-target=",
- .syntax = .joined_and_separate,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "source-charset:",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "analyzer-output",
- .syntax = .joined_or_separate,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "include-prefix=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "undefine-macro=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("analyzer-purge="),
-joinpd1("analyzer-store="),
-jspd1("current_version"),
-joinpd1("fbootclasspath="),
-joinpd1("fbracket-depth="),
-joinpd1("fcf-protection="),
-joinpd1("fdepfile-entry="),
-joinpd1("fembed-bitcode="),
-joinpd1("finput-charset="),
-joinpd1("fmodule-format="),
-joinpd1("fms-memptr-rep="),
-joinpd1("fnew-alignment="),
-joinpd1("frecord-marker="),
-.{
- .name = "fsanitize-trap=",
- .syntax = .comma_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-joinpd1("fthinlto-index="),
-joinpd1("ftrap-function="),
-joinpd1("ftrapv-handler="),
-.{
- .name = "hip-device-lib=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("mdynamic-no-pic"),
-joinpd1("mframe-pointer="),
-joinpd1("mindirect-jump="),
-joinpd1("preamble-bytes="),
-.{
- .name = "bootclasspath=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "cuda-gpu-arch=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "dependent-lib=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("dwarf-version="),
-joinpd1("falign-labels="),
-joinpd1("fauto-profile="),
-joinpd1("fexec-charset="),
-joinpd1("fgnuc-version="),
-joinpd1("finit-integer="),
-joinpd1("finit-logical="),
-joinpd1("finline-limit="),
-joinpd1("fobjc-runtime="),
-.{
- .name = "gcc-toolchain=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "linker-option=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "malign-branch=",
- .syntax = .comma_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-jspd1("objcxx-isystem"),
-joinpd1("vtordisp-mode="),
-joinpd1("Rpass-missed="),
-joinpd1("Wlarger-than-"),
-joinpd1("Wlarger-than="),
-.{
- .name = "define-macro=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("ast-dump-all="),
-.{
- .name = "autocomplete=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("falign-jumps="),
-joinpd1("falign-loops="),
-joinpd1("faligned-new="),
-joinpd1("ferror-limit="),
-joinpd1("ffp-contract="),
-joinpd1("fmodule-file="),
-joinpd1("fmodule-name="),
-joinpd1("fmsc-version="),
-.{
- .name = "fno-sanitize=",
- .syntax = .comma_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-joinpd1("fpack-struct="),
-joinpd1("fpass-plugin="),
-joinpd1("fprofile-dir="),
-joinpd1("fprofile-use="),
-joinpd1("frandom-seed="),
-joinpd1("gsplit-dwarf="),
-jspd1("isystem-after"),
-joinpd1("malign-jumps="),
-joinpd1("malign-loops="),
-joinpd1("mimplicit-it="),
-jspd1("pagezero_size"),
-joinpd1("resource-dir="),
-.{
- .name = "dyld-prefix=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "driver-mode=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("fmax-errors="),
-joinpd1("fno-builtin-"),
-joinpd1("fvisibility="),
-joinpd1("fwchar-type="),
-jspd1("fxray-modes="),
-jspd1("iwithsysroot"),
-joinpd1("mhvx-length="),
-jspd1("objc-isystem"),
-.{
- .name = "rsp-quoting=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("std-default="),
-jspd1("sub_umbrella"),
-.{
- .name = "Qpar-report",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Qvec-report",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "errorReport",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "for-linker=",
- .syntax = .joined,
- .zig_equivalent = .for_linker,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "force-link=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-jspd1("client_name"),
-jspd1("cxx-isystem"),
-joinpd1("fclasspath="),
-joinpd1("finit-real="),
-joinpd1("fforce-addr"),
-joinpd1("ftls-model="),
-jspd1("ivfsoverlay"),
-jspd1("iwithprefix"),
-joinpd1("mfloat-abi="),
-.{
- .name = "plugin-arg-",
- .syntax = .joined_and_separate,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "ptxas-path=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "save-stats=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "save-temps=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("stats-file="),
-jspd1("sub_library"),
-.{
- .name = "CLASSPATH=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "constexpr:",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "classpath=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "cuda-path=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("fencoding="),
-joinpd1("ffp-model="),
-joinpd1("ffpe-trap="),
-joinpd1("flto-jobs="),
-.{
- .name = "fsanitize=",
- .syntax = .comma_joined,
- .zig_equivalent = .sanitize,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-jspd1("iframework"),
-joinpd1("mtls-size="),
-joinpd1("segs_read_"),
-.{
- .name = "unwindlib=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "cgthreads",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "encoding=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "language=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "optimize=",
- .syntax = .joined,
- .zig_equivalent = .optimize,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "resource=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("ast-dump="),
-jspd1("c-isystem"),
-joinpd1("fcoarray="),
-joinpd1("fconvert="),
-joinpd1("fextdirs="),
-joinpd1("ftabstop="),
-jspd1("idirafter"),
-joinpd1("mregparm="),
-jspd1("undefined"),
-.{
- .name = "extdirs=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "imacros=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "include=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "sysroot=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("fopenmp="),
-joinpd1("fplugin="),
-joinpd1("fuse-ld="),
-joinpd1("fveclib="),
-jspd1("isysroot"),
-joinpd1("mcmodel="),
-joinpd1("mconsole"),
-joinpd1("mfpmath="),
-joinpd1("mhwmult="),
-joinpd1("mthreads"),
-joinpd1("municode"),
-joinpd1("mwindows"),
-jspd1("seg1addr"),
-.{
- .name = "assert=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "mhwdiv=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "output=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "prefix=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "cl-ext=",
- .syntax = .comma_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-joinpd1("cl-std="),
-joinpd1("fcheck="),
-.{
- .name = "imacros",
- .syntax = .joined_or_separate,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "include",
- .syntax = .joined_or_separate,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-jspd1("iprefix"),
-jspd1("isystem"),
-joinpd1("mhwdiv="),
-joinpd1("moslib="),
-.{
- .name = "mrecip=",
- .syntax = .comma_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "stdlib=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "target=",
- .syntax = .joined,
- .zig_equivalent = .target,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("triple="),
-.{
- .name = "verify=",
- .syntax = .comma_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-joinpd1("Rpass="),
-.{
- .name = "Xarch_",
- .syntax = .joined_and_separate,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "clang:",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "guard:",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "debug=",
- .syntax = .joined,
- .zig_equivalent = .debug,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "param=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "warn-=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("fixit="),
-joinpd1("gstabs"),
-joinpd1("gxcoff"),
-jspd1("iquote"),
-.{
- .name = "march=",
- .syntax = .joined,
- .zig_equivalent = .mcpu,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "mtune=",
- .syntax = .joined,
- .zig_equivalent = .mcpu,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "rtlib=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "specs=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("weak-l"),
-.{
- .name = "Ofast",
- .syntax = .joined,
- .zig_equivalent = .optimize,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-jspd1("Tdata"),
-jspd1("Ttext"),
-.{
- .name = "arch:",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "favor",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "imsvc",
- .syntax = .joined_or_separate,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "warn-",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = false,
- .pd2 = true,
- .psl = false,
-},
-joinpd1("flto="),
-joinpd1("gcoff"),
-joinpd1("mabi="),
-joinpd1("mabs="),
-joinpd1("masm="),
-.{
- .name = "mcpu=",
- .syntax = .joined,
- .zig_equivalent = .mcpu,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-joinpd1("mfpu="),
-joinpd1("mhvx="),
-joinpd1("mmcu="),
-joinpd1("mnan="),
-jspd1("Tbss"),
-.{
- .name = "link",
- .syntax = .remaining_args_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "std:",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-joinpd1("ccc-"),
-joinpd1("gvms"),
-joinpd1("mdll"),
-joinpd1("mtp="),
-.{
- .name = "std=",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = true,
- .psl = false,
-},
-.{
- .name = "Wa,",
- .syntax = .comma_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "Wl,",
- .syntax = .comma_joined,
- .zig_equivalent = .wl,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "Wp,",
- .syntax = .comma_joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "RTC",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zc:",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "clr",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "doc",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-joinpd1("gz="),
-joinpd1("A-"),
-joinpd1("G="),
-.{
- .name = "MF",
- .syntax = .joined_or_separate,
- .zig_equivalent = .dep_file,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "MJ",
- .syntax = .joined_or_separate,
- .zig_equivalent = .dep_file,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "MQ",
- .syntax = .joined_or_separate,
- .zig_equivalent = .dep_file,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "MT",
- .syntax = .joined_or_separate,
- .zig_equivalent = .dep_file,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "AI",
- .syntax = .joined_or_separate,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "EH",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "FA",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "FI",
- .syntax = .joined_or_separate,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "FR",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "FU",
- .syntax = .joined_or_separate,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Fa",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Fd",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Fe",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Fi",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Fm",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Fo",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Fp",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Fr",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Gs",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "MP",
- .syntax = .joined,
- .zig_equivalent = .dep_file,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Tc",
- .syntax = .joined_or_separate,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Tp",
- .syntax = .joined_or_separate,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Yc",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Yl",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Yu",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "ZW",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zm",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "Zp",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "d2",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "vd",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-jspd1("A"),
-jspd1("B"),
-jspd1("D"),
-.{
- .name = "F",
- .syntax = .joined_or_separate,
- .zig_equivalent = .framework_dir,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-jspd1("G"),
-jspd1("I"),
-jspd1("J"),
-.{
- .name = "L",
- .syntax = .joined_or_separate,
- .zig_equivalent = .lib_dir,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "O",
- .syntax = .joined,
- .zig_equivalent = .optimize,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-joinpd1("R"),
-.{
- .name = "T",
- .syntax = .joined_or_separate,
- .zig_equivalent = .linker_script,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-jspd1("U"),
-jspd1("V"),
-joinpd1("W"),
-joinpd1("X"),
-joinpd1("Z"),
-.{
- .name = "D",
- .syntax = .joined_or_separate,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "F",
- .syntax = .joined_or_separate,
- .zig_equivalent = .framework_dir,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "I",
- .syntax = .joined_or_separate,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "O",
- .syntax = .joined,
- .zig_equivalent = .optimize,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "U",
- .syntax = .joined_or_separate,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "o",
- .syntax = .joined_or_separate,
- .zig_equivalent = .o,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-.{
- .name = "w",
- .syntax = .joined,
- .zig_equivalent = .other,
- .pd1 = true,
- .pd2 = false,
- .psl = true,
-},
-joinpd1("a"),
-jspd1("b"),
-joinpd1("d"),
-jspd1("e"),
-.{
- .name = "l",
- .syntax = .joined_or_separate,
- .zig_equivalent = .l,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-.{
- .name = "o",
- .syntax = .joined_or_separate,
- .zig_equivalent = .o,
- .pd1 = true,
- .pd2 = false,
- .psl = false,
-},
-jspd1("u"),
-jspd1("x"),
-joinpd1("y"),
-};};
diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig
deleted file mode 100644
index a1d3cc2fc4b64ed84055d202bba260500988461e..0000000000000000000000000000000000000000
--- a/src-self-hosted/codegen.zig
+++ /dev/null
@@ -1,2796 +0,0 @@
-const std = @import("std");
-const mem = std.mem;
-const math = std.math;
-const assert = std.debug.assert;
-const ir = @import("ir.zig");
-const Type = @import("type.zig").Type;
-const Value = @import("value.zig").Value;
-const TypedValue = @import("TypedValue.zig");
-const link = @import("link.zig");
-const Module = @import("Module.zig");
-const Compilation = @import("Compilation.zig");
-const ErrorMsg = Compilation.ErrorMsg;
-const Target = std.Target;
-const Allocator = mem.Allocator;
-const trace = @import("tracy.zig").trace;
-const DW = std.dwarf;
-const leb128 = std.debug.leb;
-const log = std.log.scoped(.codegen);
-
-// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented.
-// zig fmt: off
-
-/// The codegen-related data that is stored in `ir.Inst.Block` instructions.
-pub const BlockData = struct {
- relocs: std.ArrayListUnmanaged(Reloc) = undefined,
- /// The first break instruction encounters `null` here and chooses a
- /// machine code value for the block result, populating this field.
- /// Following break instructions encounter that value and use it for
- /// the location to store their block results.
- mcv: AnyMCValue = undefined,
-};
-
-/// Architecture-independent MCValue. Here, we have a type that is the same size as
-/// the architecture-specific MCValue. Next to the declaration of MCValue is a
-/// comptime assert that makes sure we guessed correctly about the size. This only
-/// exists so that we can bitcast an arch-independent field to and from the real MCValue.
-pub const AnyMCValue = extern struct {
- a: u64,
- b: u64,
-};
-
-pub const Reloc = union(enum) {
- /// The value is an offset into the `Function` `code` from the beginning.
- /// To perform the reloc, write 32-bit signed little-endian integer
- /// which is a relative jump, based on the address following the reloc.
- rel32: usize,
-};
-
-pub const Result = union(enum) {
- /// The `code` parameter passed to `generateSymbol` has the value appended.
- appended: void,
- /// The value is available externally, `code` is unused.
- externally_managed: []const u8,
- fail: *ErrorMsg,
-};
-
-pub const GenerateSymbolError = error{
- OutOfMemory,
- /// A Decl that this symbol depends on had a semantic analysis failure.
- AnalysisFail,
-};
-
-pub const DebugInfoOutput = union(enum) {
- dwarf: struct {
- dbg_line: *std.ArrayList(u8),
- dbg_info: *std.ArrayList(u8),
- dbg_info_type_relocs: *link.File.DbgInfoTypeRelocsTable,
- },
- none,
-};
-
-pub fn generateSymbol(
- bin_file: *link.File,
- src: usize,
- typed_value: TypedValue,
- code: *std.ArrayList(u8),
- debug_output: DebugInfoOutput,
-) GenerateSymbolError!Result {
- const tracy = trace(@src());
- defer tracy.end();
-
- switch (typed_value.ty.zigTypeTag()) {
- .Fn => {
- switch (bin_file.options.target.cpu.arch) {
- .wasm32 => unreachable, // has its own code path
- .wasm64 => unreachable, // has its own code path
- .arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code, debug_output),
- .armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.arc => return Function(.arc).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.avr => return Function(.avr).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.bpfel => return Function(.bpfel).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.bpfeb => return Function(.bpfeb).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.hexagon => return Function(.hexagon).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.mips => return Function(.mips).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.mipsel => return Function(.mipsel).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.mips64 => return Function(.mips64).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.mips64el => return Function(.mips64el).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.msp430 => return Function(.msp430).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.powerpc => return Function(.powerpc).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.powerpc64 => return Function(.powerpc64).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.powerpc64le => return Function(.powerpc64le).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code, debug_output),
- .riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code, debug_output),
- .spu_2 => return Function(.spu_2).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.thumbeb => return Function(.thumbeb).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.i386 => return Function(.i386).generateSymbol(bin_file, src, typed_value, code, debug_output),
- .x86_64 => return Function(.x86_64).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.xcore => return Function(.xcore).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.nvptx => return Function(.nvptx).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.nvptx64 => return Function(.nvptx64).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.le32 => return Function(.le32).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.le64 => return Function(.le64).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.amdil => return Function(.amdil).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.amdil64 => return Function(.amdil64).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.hsail => return Function(.hsail).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.hsail64 => return Function(.hsail64).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.spir => return Function(.spir).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.spir64 => return Function(.spir64).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.kalimba => return Function(.kalimba).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.shave => return Function(.shave).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.lanai => return Function(.lanai).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.renderscript32 => return Function(.renderscript32).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.renderscript64 => return Function(.renderscript64).generateSymbol(bin_file, src, typed_value, code, debug_output),
- //.ve => return Function(.ve).generateSymbol(bin_file, src, typed_value, code, debug_output),
- else => @panic("Backend architectures that don't have good support yet are commented out, to improve compilation performance. If you are interested in one of these other backends feel free to uncomment them. Eventually these will be completed, but stage1 is slow and a memory hog."),
- }
- },
- .Array => {
- // TODO populate .debug_info for the array
- if (typed_value.val.cast(Value.Payload.Bytes)) |payload| {
- if (typed_value.ty.sentinel()) |sentinel| {
- try code.ensureCapacity(code.items.len + payload.data.len + 1);
- code.appendSliceAssumeCapacity(payload.data);
- const prev_len = code.items.len;
- switch (try generateSymbol(bin_file, src, .{
- .ty = typed_value.ty.elemType(),
- .val = sentinel,
- }, code, debug_output)) {
- .appended => return Result{ .appended = {} },
- .externally_managed => |slice| {
- code.appendSliceAssumeCapacity(slice);
- return Result{ .appended = {} };
- },
- .fail => |em| return Result{ .fail = em },
- }
- } else {
- return Result{ .externally_managed = payload.data };
- }
- }
- return Result{
- .fail = try ErrorMsg.create(
- bin_file.allocator,
- src,
- "TODO implement generateSymbol for more kinds of arrays",
- .{},
- ),
- };
- },
- .Pointer => {
- // TODO populate .debug_info for the pointer
-
- if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {
- const decl = payload.decl;
- if (decl.analysis != .complete) return error.AnalysisFail;
- // TODO handle the dependency of this symbol on the decl's vaddr.
- // If the decl changes vaddr, then this symbol needs to get regenerated.
- const vaddr = bin_file.getDeclVAddr(decl);
- const endian = bin_file.options.target.cpu.arch.endian();
- switch (bin_file.options.target.cpu.arch.ptrBitWidth()) {
- 16 => {
- try code.resize(2);
- mem.writeInt(u16, code.items[0..2], @intCast(u16, vaddr), endian);
- },
- 32 => {
- try code.resize(4);
- mem.writeInt(u32, code.items[0..4], @intCast(u32, vaddr), endian);
- },
- 64 => {
- try code.resize(8);
- mem.writeInt(u64, code.items[0..8], vaddr, endian);
- },
- else => unreachable,
- }
- return Result{ .appended = {} };
- }
- return Result{
- .fail = try ErrorMsg.create(
- bin_file.allocator,
- src,
- "TODO implement generateSymbol for pointer {}",
- .{typed_value.val},
- ),
- };
- },
- .Int => {
- // TODO populate .debug_info for the integer
-
- const info = typed_value.ty.intInfo(bin_file.options.target);
- if (info.bits == 8 and !info.signed) {
- const x = typed_value.val.toUnsignedInt();
- try code.append(@intCast(u8, x));
- return Result{ .appended = {} };
- }
- return Result{
- .fail = try ErrorMsg.create(
- bin_file.allocator,
- src,
- "TODO implement generateSymbol for int type '{}'",
- .{typed_value.ty},
- ),
- };
- },
- else => |t| {
- return Result{
- .fail = try ErrorMsg.create(
- bin_file.allocator,
- src,
- "TODO implement generateSymbol for type '{}'",
- .{@tagName(t)},
- ),
- };
- },
- }
-}
-
-const InnerError = error{
- OutOfMemory,
- CodegenFail,
-};
-
-fn Function(comptime arch: std.Target.Cpu.Arch) type {
- return struct {
- gpa: *Allocator,
- bin_file: *link.File,
- target: *const std.Target,
- mod_fn: *const Module.Fn,
- code: *std.ArrayList(u8),
- debug_output: DebugInfoOutput,
- err_msg: ?*ErrorMsg,
- args: []MCValue,
- ret_mcv: MCValue,
- fn_type: Type,
- arg_index: usize,
- src: usize,
- stack_align: u32,
-
- /// Byte offset within the source file.
- prev_di_src: usize,
- /// Relative to the beginning of `code`.
- prev_di_pc: usize,
- /// Used to find newlines and count line deltas.
- source: []const u8,
- /// Byte offset within the source file of the ending curly.
- rbrace_src: usize,
-
- /// The value is an offset into the `Function` `code` from the beginning.
- /// To perform the reloc, write 32-bit signed little-endian integer
- /// which is a relative jump, based on the address following the reloc.
- exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{},
-
- /// Whenever there is a runtime branch, we push a Branch onto this stack,
- /// and pop it off when the runtime branch joins. This provides an "overlay"
- /// of the table of mappings from instructions to `MCValue` from within the branch.
- /// This way we can modify the `MCValue` for an instruction in different ways
- /// within different branches. Special consideration is needed when a branch
- /// joins with its parent, to make sure all instructions have the same MCValue
- /// across each runtime branch upon joining.
- branch_stack: *std.ArrayList(Branch),
-
- /// The key must be canonical register.
- registers: std.AutoHashMapUnmanaged(Register, *ir.Inst) = .{},
- free_registers: FreeRegInt = math.maxInt(FreeRegInt),
- /// Maps offset to what is stored there.
- stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
-
- /// Offset from the stack base, representing the end of the stack frame.
- max_end_stack: u32 = 0,
- /// Represents the current end stack offset. If there is no existing slot
- /// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.
- next_stack_offset: u32 = 0,
-
- const MCValue = union(enum) {
- /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.
- /// TODO Look into deleting this tag and using `dead` instead, since every use
- /// of MCValue.none should be instead looking at the type and noticing it is 0 bits.
- none,
- /// Control flow will not allow this value to be observed.
- unreach,
- /// No more references to this value remain.
- dead,
- /// The value is undefined.
- undef,
- /// A pointer-sized integer that fits in a register.
- /// If the type is a pointer, this is the pointer address in virtual address space.
- immediate: u64,
- /// The constant was emitted into the code, at this offset.
- /// If the type is a pointer, it means the pointer address is embedded in the code.
- embedded_in_code: usize,
- /// The value is a pointer to a constant which was emitted into the code, at this offset.
- ptr_embedded_in_code: usize,
- /// The value is in a target-specific register.
- register: Register,
- /// The value is in memory at a hard-coded address.
- /// If the type is a pointer, it means the pointer address is at this memory location.
- memory: u64,
- /// The value is one of the stack variables.
- /// If the type is a pointer, it means the pointer address is in the stack at this offset.
- stack_offset: u32,
- /// The value is a pointer to one of the stack variables (payload is stack offset).
- ptr_stack_offset: u32,
- /// The value is in the compare flags assuming an unsigned operation,
- /// with this operator applied on top of it.
- compare_flags_unsigned: math.CompareOperator,
- /// The value is in the compare flags assuming a signed operation,
- /// with this operator applied on top of it.
- compare_flags_signed: math.CompareOperator,
-
- fn isMemory(mcv: MCValue) bool {
- return switch (mcv) {
- .embedded_in_code, .memory, .stack_offset => true,
- else => false,
- };
- }
-
- fn isImmediate(mcv: MCValue) bool {
- return switch (mcv) {
- .immediate => true,
- else => false,
- };
- }
-
- fn isMutable(mcv: MCValue) bool {
- return switch (mcv) {
- .none => unreachable,
- .unreach => unreachable,
- .dead => unreachable,
-
- .immediate,
- .embedded_in_code,
- .memory,
- .compare_flags_unsigned,
- .compare_flags_signed,
- .ptr_stack_offset,
- .ptr_embedded_in_code,
- .undef,
- => false,
-
- .register,
- .stack_offset,
- => true,
- };
- }
- };
-
- const Branch = struct {
- inst_table: std.AutoArrayHashMapUnmanaged(*ir.Inst, MCValue) = .{},
-
- fn deinit(self: *Branch, gpa: *Allocator) void {
- self.inst_table.deinit(gpa);
- self.* = undefined;
- }
- };
-
- fn markRegUsed(self: *Self, reg: Register) void {
- if (FreeRegInt == u0) return;
- const index = reg.allocIndex() orelse return;
- const ShiftInt = math.Log2Int(FreeRegInt);
- const shift = @intCast(ShiftInt, index);
- self.free_registers &= ~(@as(FreeRegInt, 1) << shift);
- }
-
- fn markRegFree(self: *Self, reg: Register) void {
- if (FreeRegInt == u0) return;
- const index = reg.allocIndex() orelse return;
- const ShiftInt = math.Log2Int(FreeRegInt);
- const shift = @intCast(ShiftInt, index);
- self.free_registers |= @as(FreeRegInt, 1) << shift;
- }
-
- /// Before calling, must ensureCapacity + 1 on self.registers.
- /// Returns `null` if all registers are allocated.
- fn allocReg(self: *Self, inst: *ir.Inst) ?Register {
- const free_index = @ctz(FreeRegInt, self.free_registers);
- if (free_index >= callee_preserved_regs.len) {
- return null;
- }
- self.free_registers &= ~(@as(FreeRegInt, 1) << free_index);
- const reg = callee_preserved_regs[free_index];
- self.registers.putAssumeCapacityNoClobber(reg, inst);
- log.debug("alloc {} => {*}", .{reg, inst});
- return reg;
- }
-
- /// Does not track the register.
- fn findUnusedReg(self: *Self) ?Register {
- const free_index = @ctz(FreeRegInt, self.free_registers);
- if (free_index >= callee_preserved_regs.len) {
- return null;
- }
- return callee_preserved_regs[free_index];
- }
-
- const StackAllocation = struct {
- inst: *ir.Inst,
- /// TODO do we need size? should be determined by inst.ty.abiSize()
- size: u32,
- };
-
- const Self = @This();
-
- fn generateSymbol(
- bin_file: *link.File,
- src: usize,
- typed_value: TypedValue,
- code: *std.ArrayList(u8),
- debug_output: DebugInfoOutput,
- ) GenerateSymbolError!Result {
- const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
-
- const fn_type = module_fn.owner_decl.typed_value.most_recent.typed_value.ty;
-
- var branch_stack = std.ArrayList(Branch).init(bin_file.allocator);
- defer {
- assert(branch_stack.items.len == 1);
- branch_stack.items[0].deinit(bin_file.allocator);
- branch_stack.deinit();
- }
- try branch_stack.append(.{});
-
- const src_data: struct {lbrace_src: usize, rbrace_src: usize, source: []const u8} = blk: {
- if (module_fn.owner_decl.scope.cast(Module.Scope.Container)) |container_scope| {
- const tree = container_scope.file_scope.contents.tree;
- const fn_proto = tree.root_node.decls()[module_fn.owner_decl.src_index].castTag(.FnProto).?;
- const block = fn_proto.getBodyNode().?.castTag(.Block).?;
- const lbrace_src = tree.token_locs[block.lbrace].start;
- const rbrace_src = tree.token_locs[block.rbrace].start;
- break :blk .{ .lbrace_src = lbrace_src, .rbrace_src = rbrace_src, .source = tree.source };
- } else if (module_fn.owner_decl.scope.cast(Module.Scope.ZIRModule)) |zir_module| {
- const byte_off = zir_module.contents.module.decls[module_fn.owner_decl.src_index].inst.src;
- break :blk .{ .lbrace_src = byte_off, .rbrace_src = byte_off, .source = zir_module.source.bytes };
- } else {
- unreachable;
- }
- };
-
- var function = Self{
- .gpa = bin_file.allocator,
- .target = &bin_file.options.target,
- .bin_file = bin_file,
- .mod_fn = module_fn,
- .code = code,
- .debug_output = debug_output,
- .err_msg = null,
- .args = undefined, // populated after `resolveCallingConventionValues`
- .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
- .fn_type = fn_type,
- .arg_index = 0,
- .branch_stack = &branch_stack,
- .src = src,
- .stack_align = undefined,
- .prev_di_pc = 0,
- .prev_di_src = src_data.lbrace_src,
- .rbrace_src = src_data.rbrace_src,
- .source = src_data.source,
- };
- defer function.registers.deinit(bin_file.allocator);
- defer function.stack.deinit(bin_file.allocator);
- defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
-
- var call_info = function.resolveCallingConventionValues(src, fn_type) catch |err| switch (err) {
- error.CodegenFail => return Result{ .fail = function.err_msg.? },
- else => |e| return e,
- };
- defer call_info.deinit(&function);
-
- function.args = call_info.args;
- function.ret_mcv = call_info.return_value;
- function.stack_align = call_info.stack_align;
- function.max_end_stack = call_info.stack_byte_count;
-
- function.gen() catch |err| switch (err) {
- error.CodegenFail => return Result{ .fail = function.err_msg.? },
- else => |e| return e,
- };
-
- if (function.err_msg) |em| {
- return Result{ .fail = em };
- } else {
- return Result{ .appended = {} };
- }
- }
-
- fn gen(self: *Self) !void {
- switch (arch) {
- .x86_64 => {
- try self.code.ensureCapacity(self.code.items.len + 11);
-
- const cc = self.fn_type.fnCallingConvention();
- if (cc != .Naked) {
- // We want to subtract the aligned stack frame size from rsp here, but we don't
- // yet know how big it will be, so we leave room for a 4-byte stack size.
- // TODO During semantic analysis, check if there are no function calls. If there
- // are none, here we can omit the part where we subtract and then add rsp.
- self.code.appendSliceAssumeCapacity(&[_]u8{
- 0x55, // push rbp
- 0x48, 0x89, 0xe5, // mov rbp, rsp
- 0x48, 0x81, 0xec, // sub rsp, imm32 (with reloc)
- });
- const reloc_index = self.code.items.len;
- self.code.items.len += 4;
-
- try self.dbgSetPrologueEnd();
- try self.genBody(self.mod_fn.analysis.success);
-
- const stack_end = self.max_end_stack;
- if (stack_end > math.maxInt(i32))
- return self.fail(self.src, "too much stack used in call parameters", .{});
- const aligned_stack_end = mem.alignForward(stack_end, self.stack_align);
- mem.writeIntLittle(u32, self.code.items[reloc_index..][0..4], @intCast(u32, aligned_stack_end));
-
- if (self.code.items.len >= math.maxInt(i32)) {
- return self.fail(self.src, "unable to perform relocation: jump too far", .{});
- }
- for (self.exitlude_jump_relocs.items) |jmp_reloc| {
- const amt = self.code.items.len - (jmp_reloc + 4);
- // If it wouldn't jump at all, elide it.
- if (amt == 0) {
- self.code.items.len -= 5;
- continue;
- }
- const s32_amt = @intCast(i32, amt);
- mem.writeIntLittle(i32, self.code.items[jmp_reloc..][0..4], s32_amt);
- }
-
- // Important to be after the possible self.code.items.len -= 5 above.
- try self.dbgSetEpilogueBegin();
-
- try self.code.ensureCapacity(self.code.items.len + 9);
- // add rsp, x
- if (aligned_stack_end > math.maxInt(i8)) {
- // example: 48 81 c4 ff ff ff 7f add rsp,0x7fffffff
- self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x81, 0xc4 });
- const x = @intCast(u32, aligned_stack_end);
- mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), x);
- } else if (aligned_stack_end != 0) {
- // example: 48 83 c4 7f add rsp,0x7f
- const x = @intCast(u8, aligned_stack_end);
- self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x83, 0xc4, x });
- }
-
- self.code.appendSliceAssumeCapacity(&[_]u8{
- 0x5d, // pop rbp
- 0xc3, // ret
- });
- } else {
- try self.dbgSetPrologueEnd();
- try self.genBody(self.mod_fn.analysis.success);
- try self.dbgSetEpilogueBegin();
- }
- },
- else => {
- try self.dbgSetPrologueEnd();
- try self.genBody(self.mod_fn.analysis.success);
- try self.dbgSetEpilogueBegin();
- },
- }
- // Drop them off at the rbrace.
- try self.dbgAdvancePCAndLine(self.rbrace_src);
- }
-
- fn genBody(self: *Self, body: ir.Body) InnerError!void {
- for (body.instructions) |inst| {
- try self.ensureProcessDeathCapacity(@popCount(@TypeOf(inst.deaths), inst.deaths));
-
- const mcv = try self.genFuncInst(inst);
- if (!inst.isUnused()) {
- log.debug("{*} => {}", .{inst, mcv});
- const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
- try branch.inst_table.putNoClobber(self.gpa, inst, mcv);
- }
-
- var i: ir.Inst.DeathsBitIndex = 0;
- while (inst.getOperand(i)) |operand| : (i += 1) {
- if (inst.operandDies(i))
- self.processDeath(operand);
- }
- }
- }
-
- fn dbgSetPrologueEnd(self: *Self) InnerError!void {
- switch (self.debug_output) {
- .dwarf => |dbg_out| {
- try dbg_out.dbg_line.append(DW.LNS_set_prologue_end);
- try self.dbgAdvancePCAndLine(self.prev_di_src);
- },
- .none => {},
- }
- }
-
- fn dbgSetEpilogueBegin(self: *Self) InnerError!void {
- switch (self.debug_output) {
- .dwarf => |dbg_out| {
- try dbg_out.dbg_line.append(DW.LNS_set_epilogue_begin);
- try self.dbgAdvancePCAndLine(self.prev_di_src);
- },
- .none => {},
- }
- }
-
- fn dbgAdvancePCAndLine(self: *Self, src: usize) InnerError!void {
- self.prev_di_src = src;
- self.prev_di_pc = self.code.items.len;
- switch (self.debug_output) {
- .dwarf => |dbg_out| {
- // TODO Look into improving the performance here by adding a token-index-to-line
- // lookup table, and changing ir.Inst from storing byte offset to token. Currently
- // this involves scanning over the source code for newlines
- // (but only from the previous byte offset to the new one).
- const delta_line = std.zig.lineDelta(self.source, self.prev_di_src, src);
- const delta_pc = self.code.items.len - self.prev_di_pc;
- // TODO Look into using the DWARF special opcodes to compress this data. It lets you emit
- // single-byte opcodes that add different numbers to both the PC and the line number
- // at the same time.
- try dbg_out.dbg_line.ensureCapacity(dbg_out.dbg_line.items.len + 11);
- dbg_out.dbg_line.appendAssumeCapacity(DW.LNS_advance_pc);
- leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable;
- if (delta_line != 0) {
- dbg_out.dbg_line.appendAssumeCapacity(DW.LNS_advance_line);
- leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable;
- }
- dbg_out.dbg_line.appendAssumeCapacity(DW.LNS_copy);
- },
- .none => {},
- }
- }
-
- /// Asserts there is already capacity to insert into top branch inst_table.
- fn processDeath(self: *Self, inst: *ir.Inst) void {
- if (inst.tag == .constant) return; // Constants are immortal.
- // When editing this function, note that the logic must synchronize with `reuseOperand`.
- const prev_value = self.getResolvedInstValue(inst);
- const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
- branch.inst_table.putAssumeCapacity(inst, .dead);
- switch (prev_value) {
- .register => |reg| {
- const canon_reg = toCanonicalReg(reg);
- _ = self.registers.remove(canon_reg);
- self.markRegFree(canon_reg);
- },
- else => {}, // TODO process stack allocation death
- }
- }
-
- fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
- const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table;
- try table.ensureCapacity(self.gpa, table.items().len + additional_count);
- }
-
- /// Adds a Type to the .debug_info at the current position. The bytes will be populated later,
- /// after codegen for this symbol is done.
- fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void {
- switch (self.debug_output) {
- .dwarf => |dbg_out| {
- assert(ty.hasCodeGenBits());
- const index = dbg_out.dbg_info.items.len;
- try dbg_out.dbg_info.resize(index + 4); // DW.AT_type, DW.FORM_ref4
-
- const gop = try dbg_out.dbg_info_type_relocs.getOrPut(self.gpa, ty);
- if (!gop.found_existing) {
- gop.entry.value = .{
- .off = undefined,
- .relocs = .{},
- };
- }
- try gop.entry.value.relocs.append(self.gpa, @intCast(u32, index));
- },
- .none => {},
- }
- }
-
- fn genFuncInst(self: *Self, inst: *ir.Inst) !MCValue {
- switch (inst.tag) {
- .add => return self.genAdd(inst.castTag(.add).?),
- .alloc => return self.genAlloc(inst.castTag(.alloc).?),
- .arg => return self.genArg(inst.castTag(.arg).?),
- .assembly => return self.genAsm(inst.castTag(.assembly).?),
- .bitcast => return self.genBitCast(inst.castTag(.bitcast).?),
- .block => return self.genBlock(inst.castTag(.block).?),
- .br => return self.genBr(inst.castTag(.br).?),
- .breakpoint => return self.genBreakpoint(inst.src),
- .brvoid => return self.genBrVoid(inst.castTag(.brvoid).?),
- .call => return self.genCall(inst.castTag(.call).?),
- .cmp_lt => return self.genCmp(inst.castTag(.cmp_lt).?, .lt),
- .cmp_lte => return self.genCmp(inst.castTag(.cmp_lte).?, .lte),
- .cmp_eq => return self.genCmp(inst.castTag(.cmp_eq).?, .eq),
- .cmp_gte => return self.genCmp(inst.castTag(.cmp_gte).?, .gte),
- .cmp_gt => return self.genCmp(inst.castTag(.cmp_gt).?, .gt),
- .cmp_neq => return self.genCmp(inst.castTag(.cmp_neq).?, .neq),
- .condbr => return self.genCondBr(inst.castTag(.condbr).?),
- .constant => unreachable, // excluded from function bodies
- .dbg_stmt => return self.genDbgStmt(inst.castTag(.dbg_stmt).?),
- .floatcast => return self.genFloatCast(inst.castTag(.floatcast).?),
- .intcast => return self.genIntCast(inst.castTag(.intcast).?),
- .isnonnull => return self.genIsNonNull(inst.castTag(.isnonnull).?),
- .isnull => return self.genIsNull(inst.castTag(.isnull).?),
- .iserr => return self.genIsErr(inst.castTag(.iserr).?),
- .load => return self.genLoad(inst.castTag(.load).?),
- .loop => return self.genLoop(inst.castTag(.loop).?),
- .not => return self.genNot(inst.castTag(.not).?),
- .ptrtoint => return self.genPtrToInt(inst.castTag(.ptrtoint).?),
- .ref => return self.genRef(inst.castTag(.ref).?),
- .ret => return self.genRet(inst.castTag(.ret).?),
- .retvoid => return self.genRetVoid(inst.castTag(.retvoid).?),
- .store => return self.genStore(inst.castTag(.store).?),
- .sub => return self.genSub(inst.castTag(.sub).?),
- .unreach => return MCValue{ .unreach = {} },
- .unwrap_optional => return self.genUnwrapOptional(inst.castTag(.unwrap_optional).?),
- .wrap_optional => return self.genWrapOptional(inst.castTag(.wrap_optional).?),
- .varptr => return self.genVarPtr(inst.castTag(.varptr).?),
- }
- }
-
- fn allocMem(self: *Self, inst: *ir.Inst, abi_size: u32, abi_align: u32) !u32 {
- if (abi_align > self.stack_align)
- self.stack_align = abi_align;
- // TODO find a free slot instead of always appending
- const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align);
- self.next_stack_offset = offset + abi_size;
- if (self.next_stack_offset > self.max_end_stack)
- self.max_end_stack = self.next_stack_offset;
- try self.stack.putNoClobber(self.gpa, offset, .{
- .inst = inst,
- .size = abi_size,
- });
- return offset;
- }
-
- /// Use a pointer instruction as the basis for allocating stack memory.
- fn allocMemPtr(self: *Self, inst: *ir.Inst) !u32 {
- const elem_ty = inst.ty.elemType();
- const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
- return self.fail(inst.src, "type '{}' too big to fit into stack frame", .{elem_ty});
- };
- // TODO swap this for inst.ty.ptrAlign
- const abi_align = elem_ty.abiAlignment(self.target.*);
- return self.allocMem(inst, abi_size, abi_align);
- }
-
- fn allocRegOrMem(self: *Self, inst: *ir.Inst, reg_ok: bool) !MCValue {
- const elem_ty = inst.ty;
- const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
- return self.fail(inst.src, "type '{}' too big to fit into stack frame", .{elem_ty});
- };
- const abi_align = elem_ty.abiAlignment(self.target.*);
- if (abi_align > self.stack_align)
- self.stack_align = abi_align;
-
- if (reg_ok) {
- // Make sure the type can fit in a register before we try to allocate one.
- const ptr_bits = arch.ptrBitWidth();
- const ptr_bytes: u64 = @divExact(ptr_bits, 8);
- if (abi_size <= ptr_bytes) {
- try self.registers.ensureCapacity(self.gpa, self.registers.count() + 1);
- if (self.allocReg(inst)) |reg| {
- return MCValue{ .register = registerAlias(reg, abi_size) };
- }
- }
- }
- const stack_offset = try self.allocMem(inst, abi_size, abi_align);
- return MCValue{ .stack_offset = stack_offset };
- }
-
- /// Copies a value to a register without tracking the register. The register is not considered
- /// allocated. A second call to `copyToTmpRegister` may return the same register.
- /// This can have a side effect of spilling instructions to the stack to free up a register.
- fn copyToTmpRegister(self: *Self, src: usize, mcv: MCValue) !Register {
- const reg = self.findUnusedReg() orelse b: {
- // We'll take over the first register. Move the instruction that was previously
- // there to a stack allocation.
- const reg = callee_preserved_regs[0];
- const regs_entry = self.registers.remove(reg).?;
- const spilled_inst = regs_entry.value;
-
- const stack_mcv = try self.allocRegOrMem(spilled_inst, false);
- const reg_mcv = self.getResolvedInstValue(spilled_inst);
- assert(reg == toCanonicalReg(reg_mcv.register));
- const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
- try branch.inst_table.put(self.gpa, spilled_inst, stack_mcv);
- try self.genSetStack(src, spilled_inst.ty, stack_mcv.stack_offset, reg_mcv);
-
- break :b reg;
- };
- try self.genSetReg(src, reg, mcv);
- return reg;
- }
-
- /// Allocates a new register and copies `mcv` into it.
- /// `reg_owner` is the instruction that gets associated with the register in the register table.
- /// This can have a side effect of spilling instructions to the stack to free up a register.
- fn copyToNewRegister(self: *Self, reg_owner: *ir.Inst, mcv: MCValue) !MCValue {
- try self.registers.ensureCapacity(self.gpa, @intCast(u32, self.registers.count() + 1));
-
- const reg = self.allocReg(reg_owner) orelse b: {
- // We'll take over the first register. Move the instruction that was previously
- // there to a stack allocation.
- const reg = callee_preserved_regs[0];
- const regs_entry = self.registers.getEntry(reg).?;
- const spilled_inst = regs_entry.value;
- regs_entry.value = reg_owner;
-
- const stack_mcv = try self.allocRegOrMem(spilled_inst, false);
- const reg_mcv = self.getResolvedInstValue(spilled_inst);
- assert(reg == toCanonicalReg(reg_mcv.register));
- const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
- try branch.inst_table.put(self.gpa, spilled_inst, stack_mcv);
- try self.genSetStack(reg_owner.src, spilled_inst.ty, stack_mcv.stack_offset, reg_mcv);
-
- break :b reg;
- };
- try self.genSetReg(reg_owner.src, reg, mcv);
- return MCValue{ .register = reg };
- }
-
- fn genAlloc(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
- const stack_offset = try self.allocMemPtr(&inst.base);
- return MCValue{ .ptr_stack_offset = stack_offset };
- }
-
- fn genFloatCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
- // No side effects, so if it's unreferenced, do nothing.
- if (inst.base.isUnused())
- return MCValue.dead;
- switch (arch) {
- else => return self.fail(inst.base.src, "TODO implement floatCast for {}", .{self.target.cpu.arch}),
- }
- }
-
- fn genIntCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
- // No side effects, so if it's unreferenced, do nothing.
- if (inst.base.isUnused())
- return MCValue.dead;
-
- const operand = try self.resolveInst(inst.operand);
- const info_a = inst.operand.ty.intInfo(self.target.*);
- const info_b = inst.base.ty.intInfo(self.target.*);
- if (info_a.signed != info_b.signed)
- return self.fail(inst.base.src, "TODO gen intcast sign safety in semantic analysis", .{});
-
- if (info_a.bits == info_b.bits)
- return operand;
-
- switch (arch) {
- else => return self.fail(inst.base.src, "TODO implement intCast for {}", .{self.target.cpu.arch}),
- }
- }
-
- fn genNot(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
- // No side effects, so if it's unreferenced, do nothing.
- if (inst.base.isUnused())
- return MCValue.dead;
- const operand = try self.resolveInst(inst.operand);
- switch (operand) {
- .dead => unreachable,
- .unreach => unreachable,
- .compare_flags_unsigned => |op| return MCValue{
- .compare_flags_unsigned = switch (op) {
- .gte => .lt,
- .gt => .lte,
- .neq => .eq,
- .lt => .gte,
- .lte => .gt,
- .eq => .neq,
- },
- },
- .compare_flags_signed => |op| return MCValue{
- .compare_flags_signed = switch (op) {
- .gte => .lt,
- .gt => .lte,
- .neq => .eq,
- .lt => .gte,
- .lte => .gt,
- .eq => .neq,
- },
- },
- else => {},
- }
-
- switch (arch) {
- .x86_64 => {
- var imm = ir.Inst.Constant{
- .base = .{
- .tag = .constant,
- .deaths = 0,
- .ty = inst.operand.ty,
- .src = inst.operand.src,
- },
- .val = Value.initTag(.bool_true),
- };
- return try self.genX8664BinMath(&inst.base, inst.operand, &imm.base, 6, 0x30);
- },
- else => return self.fail(inst.base.src, "TODO implement NOT for {}", .{self.target.cpu.arch}),
- }
- }
-
- fn genAdd(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
- // No side effects, so if it's unreferenced, do nothing.
- if (inst.base.isUnused())
- return MCValue.dead;
- switch (arch) {
- .x86_64 => {
- return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs, 0, 0x00);
- },
- else => return self.fail(inst.base.src, "TODO implement add for {}", .{self.target.cpu.arch}),
- }
- }
-
- fn genUnwrapOptional(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
- // No side effects, so if it's unreferenced, do nothing.
- if (inst.base.isUnused())
- return MCValue.dead;
- switch (arch) {
- else => return self.fail(inst.base.src, "TODO implement unwrap optional for {}", .{self.target.cpu.arch}),
- }
- }
-
- fn genWrapOptional(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
- const optional_ty = inst.base.ty;
-
- // No side effects, so if it's unreferenced, do nothing.
- if (inst.base.isUnused())
- return MCValue.dead;
-
- // Optional type is just a boolean true
- if (optional_ty.abiSize(self.target.*) == 1)
- return MCValue{ .immediate = 1 };
-
- switch (arch) {
- else => return self.fail(inst.base.src, "TODO implement wrap optional for {}", .{self.target.cpu.arch}),
- }
- }
-
- fn genVarPtr(self: *Self, inst: *ir.Inst.VarPtr) !MCValue {
- // No side effects, so if it's unreferenced, do nothing.
- if (inst.base.isUnused())
- return MCValue.dead;
-
- switch (arch) {
- else => return self.fail(inst.base.src, "TODO implement varptr for {}", .{self.target.cpu.arch}),
- }
- }
-
- fn reuseOperand(self: *Self, inst: *ir.Inst, op_index: ir.Inst.DeathsBitIndex, mcv: MCValue) bool {
- if (!inst.operandDies(op_index))
- return false;
-
- switch (mcv) {
- .register => |reg| {
- // If it's in the registers table, need to associate the register with the
- // new instruction.
- if (self.registers.getEntry(toCanonicalReg(reg))) |entry| {
- entry.value = inst;
- }
- log.debug("reusing {} => {*}", .{reg, inst});
- },
- .stack_offset => |off| {
- log.debug("reusing stack offset {} => {*}", .{off, inst});
- return true;
- },
- else => return false,
- }
-
- // Prevent the operand deaths processing code from deallocating it.
- inst.clearOperandDeath(op_index);
-
- // That makes us responsible for doing the rest of the stuff that processDeath would have done.
- const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
- branch.inst_table.putAssumeCapacity(inst.getOperand(op_index).?, .dead);
-
- return true;
- }
-
- fn genLoad(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
- const elem_ty = inst.base.ty;
- if (!elem_ty.hasCodeGenBits())
- return MCValue.none;
- const ptr = try self.resolveInst(inst.operand);
- const is_volatile = inst.operand.ty.isVolatilePtr();
- if (inst.base.isUnused() and !is_volatile)
- return MCValue.dead;
- const dst_mcv: MCValue = blk: {
- if (self.reuseOperand(&inst.base, 0, ptr)) {
- // The MCValue that holds the pointer can be re-used as the value.
- break :blk ptr;
- } else {
- break :blk try self.allocRegOrMem(&inst.base, true);
- }
- };
- switch (ptr) {
- .none => unreachable,
- .undef => unreachable,
- .unreach => unreachable,
- .dead => unreachable,
- .compare_flags_unsigned => unreachable,
- .compare_flags_signed => unreachable,
- .immediate => |imm| try self.setRegOrMem(inst.base.src, elem_ty, dst_mcv, .{ .memory = imm }),
- .ptr_stack_offset => |off| try self.setRegOrMem(inst.base.src, elem_ty, dst_mcv, .{ .stack_offset = off }),
- .ptr_embedded_in_code => |off| {
- try self.setRegOrMem(inst.base.src, elem_ty, dst_mcv, .{ .embedded_in_code = off });
- },
- .embedded_in_code => {
- return self.fail(inst.base.src, "TODO implement loading from MCValue.embedded_in_code", .{});
- },
- .register => {
- return self.fail(inst.base.src, "TODO implement loading from MCValue.register", .{});
- },
- .memory => {
- return self.fail(inst.base.src, "TODO implement loading from MCValue.memory", .{});
- },
- .stack_offset => {
- return self.fail(inst.base.src, "TODO implement loading from MCValue.stack_offset", .{});
- },
- }
- return dst_mcv;
- }
-
- fn genStore(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
- const ptr = try self.resolveInst(inst.lhs);
- const value = try self.resolveInst(inst.rhs);
- const elem_ty = inst.rhs.ty;
- switch (ptr) {
- .none => unreachable,
- .undef => unreachable,
- .unreach => unreachable,
- .dead => unreachable,
- .compare_flags_unsigned => unreachable,
- .compare_flags_signed => unreachable,
- .immediate => |imm| {
- try self.setRegOrMem(inst.base.src, elem_ty, .{ .memory = imm }, value);
- },
- .ptr_stack_offset => |off| {
- try self.genSetStack(inst.base.src, elem_ty, off, value);
- },
- .ptr_embedded_in_code => |off| {
- try self.setRegOrMem(inst.base.src, elem_ty, .{ .embedded_in_code = off }, value);
- },
- .embedded_in_code => {
- return self.fail(inst.base.src, "TODO implement storing to MCValue.embedded_in_code", .{});
- },
- .register => {
- return self.fail(inst.base.src, "TODO implement storing to MCValue.register", .{});
- },
- .memory => {
- return self.fail(inst.base.src, "TODO implement storing to MCValue.memory", .{});
- },
- .stack_offset => {
- return self.fail(inst.base.src, "TODO implement storing to MCValue.stack_offset", .{});
- },
- }
- return .none;
- }
-
- fn genSub(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
- // No side effects, so if it's unreferenced, do nothing.
- if (inst.base.isUnused())
- return MCValue.dead;
- switch (arch) {
- .x86_64 => {
- return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs, 5, 0x28);
- },
- else => return self.fail(inst.base.src, "TODO implement sub for {}", .{self.target.cpu.arch}),
- }
- }
-
- /// ADD, SUB, XOR, OR, AND
- fn genX8664BinMath(self: *Self, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, opx: u8, mr: u8) !MCValue {
- try self.code.ensureCapacity(self.code.items.len + 8);
-
- const lhs = try self.resolveInst(op_lhs);
- const rhs = try self.resolveInst(op_rhs);
-
- // There are 2 operands, destination and source.
- // Either one, but not both, can be a memory operand.
- // Source operand can be an immediate, 8 bits or 32 bits.
- // So, if either one of the operands dies with this instruction, we can use it
- // as the result MCValue.
- var dst_mcv: MCValue = undefined;
- var src_mcv: MCValue = undefined;
- var src_inst: *ir.Inst = undefined;
- if (self.reuseOperand(inst, 0, lhs)) {
- // LHS dies; use it as the destination.
- // Both operands cannot be memory.
- src_inst = op_rhs;
- if (lhs.isMemory() and rhs.isMemory()) {
- dst_mcv = try self.copyToNewRegister(inst, lhs);
- src_mcv = rhs;
- } else {
- dst_mcv = lhs;
- src_mcv = rhs;
- }
- } else if (self.reuseOperand(inst, 1, rhs)) {
- // RHS dies; use it as the destination.
- // Both operands cannot be memory.
- src_inst = op_lhs;
- if (lhs.isMemory() and rhs.isMemory()) {
- dst_mcv = try self.copyToNewRegister(inst, rhs);
- src_mcv = lhs;
- } else {
- dst_mcv = rhs;
- src_mcv = lhs;
- }
- } else {
- if (lhs.isMemory()) {
- dst_mcv = try self.copyToNewRegister(inst, lhs);
- src_mcv = rhs;
- src_inst = op_rhs;
- } else {
- dst_mcv = try self.copyToNewRegister(inst, rhs);
- src_mcv = lhs;
- src_inst = op_lhs;
- }
- }
- // This instruction supports only signed 32-bit immediates at most. If the immediate
- // value is larger than this, we put it in a register.
- // A potential opportunity for future optimization here would be keeping track
- // of the fact that the instruction is available both as an immediate
- // and as a register.
- switch (src_mcv) {
- .immediate => |imm| {
- if (imm > math.maxInt(u31)) {
- src_mcv = MCValue{ .register = try self.copyToTmpRegister(src_inst.src, src_mcv) };
- }
- },
- else => {},
- }
-
- try self.genX8664BinMathCode(inst.src, inst.ty, dst_mcv, src_mcv, opx, mr);
-
- return dst_mcv;
- }
-
- fn genX8664BinMathCode(
- self: *Self,
- src: usize,
- dst_ty: Type,
- dst_mcv: MCValue,
- src_mcv: MCValue,
- opx: u8,
- mr: u8,
- ) !void {
- switch (dst_mcv) {
- .none => unreachable,
- .undef => unreachable,
- .dead, .unreach, .immediate => unreachable,
- .compare_flags_unsigned => unreachable,
- .compare_flags_signed => unreachable,
- .ptr_stack_offset => unreachable,
- .ptr_embedded_in_code => unreachable,
- .register => |dst_reg| {
- switch (src_mcv) {
- .none => unreachable,
- .undef => try self.genSetReg(src, dst_reg, .undef),
- .dead, .unreach => unreachable,
- .ptr_stack_offset => unreachable,
- .ptr_embedded_in_code => unreachable,
- .register => |src_reg| {
- self.rex(.{ .b = dst_reg.isExtended(), .r = src_reg.isExtended(), .w = dst_reg.size() == 64 });
- self.code.appendSliceAssumeCapacity(&[_]u8{ mr + 0x1, 0xC0 | (@as(u8, src_reg.id() & 0b111) << 3) | @as(u8, dst_reg.id() & 0b111) });
- },
- .immediate => |imm| {
- const imm32 = @intCast(u31, imm); // This case must be handled before calling genX8664BinMathCode.
- // 81 /opx id
- if (imm32 <= math.maxInt(u7)) {
- self.rex(.{ .b = dst_reg.isExtended(), .w = dst_reg.size() == 64 });
- self.code.appendSliceAssumeCapacity(&[_]u8{
- 0x83,
- 0xC0 | (opx << 3) | @truncate(u3, dst_reg.id()),
- @intCast(u8, imm32),
- });
- } else {
- self.rex(.{ .r = dst_reg.isExtended(), .w = dst_reg.size() == 64 });
- self.code.appendSliceAssumeCapacity(&[_]u8{
- 0x81,
- 0xC0 | (opx << 3) | @truncate(u3, dst_reg.id()),
- });
- std.mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), imm32);
- }
- },
- .embedded_in_code, .memory, .stack_offset => {
- return self.fail(src, "TODO implement x86 ADD/SUB/CMP source memory", .{});
- },
- .compare_flags_unsigned => {
- return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{});
- },
- .compare_flags_signed => {
- return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (signed)", .{});
- },
- }
- },
- .stack_offset => |off| {
- switch (src_mcv) {
- .none => unreachable,
- .undef => return self.genSetStack(src, dst_ty, off, .undef),
- .dead, .unreach => unreachable,
- .ptr_stack_offset => unreachable,
- .ptr_embedded_in_code => unreachable,
- .register => |src_reg| {
- try self.genX8664ModRMRegToStack(src, dst_ty, off, src_reg, mr + 0x1);
- },
- .immediate => |imm| {
- return self.fail(src, "TODO implement x86 ADD/SUB/CMP source immediate", .{});
- },
- .embedded_in_code, .memory, .stack_offset => {
- return self.fail(src, "TODO implement x86 ADD/SUB/CMP source memory", .{});
- },
- .compare_flags_unsigned => {
- return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{});
- },
- .compare_flags_signed => {
- return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (signed)", .{});
- },
- }
- },
- .embedded_in_code, .memory => {
- return self.fail(src, "TODO implement x86 ADD/SUB/CMP destination memory", .{});
- },
- }
- }
-
- fn genX8664ModRMRegToStack(self: *Self, src: usize, ty: Type, off: u32, reg: Register, opcode: u8) !void {
- const abi_size = ty.abiSize(self.target.*);
- const adj_off = off + abi_size;
- try self.code.ensureCapacity(self.code.items.len + 7);
- self.rex(.{ .w = reg.size() == 64, .r = reg.isExtended() });
- const reg_id: u8 = @truncate(u3, reg.id());
- if (adj_off <= 128) {
- // example: 48 89 55 7f mov QWORD PTR [rbp+0x7f],rdx
- const RM = @as(u8, 0b01_000_101) | (reg_id << 3);
- const negative_offset = @intCast(i8, -@intCast(i32, adj_off));
- const twos_comp = @bitCast(u8, negative_offset);
- self.code.appendSliceAssumeCapacity(&[_]u8{ opcode, RM, twos_comp });
- } else if (adj_off <= 2147483648) {
- // example: 48 89 95 80 00 00 00 mov QWORD PTR [rbp+0x80],rdx
- const RM = @as(u8, 0b10_000_101) | (reg_id << 3);
- const negative_offset = @intCast(i32, -@intCast(i33, adj_off));
- const twos_comp = @bitCast(u32, negative_offset);
- self.code.appendSliceAssumeCapacity(&[_]u8{ opcode, RM });
- mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), twos_comp);
- } else {
- return self.fail(src, "stack offset too large", .{});
- }
- }
-
- fn genArg(self: *Self, inst: *ir.Inst.Arg) !MCValue {
- if (FreeRegInt == u0) {
- return self.fail(inst.base.src, "TODO implement Register enum for {}", .{self.target.cpu.arch});
- }
- if (inst.base.isUnused())
- return MCValue.dead;
-
- try self.registers.ensureCapacity(self.gpa, self.registers.count() + 1);
-
- const result = self.args[self.arg_index];
- self.arg_index += 1;
-
- const name_with_null = inst.name[0..mem.lenZ(inst.name) + 1];
- switch (result) {
- .register => |reg| {
- self.registers.putAssumeCapacityNoClobber(toCanonicalReg(reg), &inst.base);
- self.markRegUsed(reg);
-
- switch (self.debug_output) {
- .dwarf => |dbg_out| {
- try dbg_out.dbg_info.ensureCapacity(dbg_out.dbg_info.items.len + 8 + name_with_null.len);
- dbg_out.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter);
- dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT_location, DW.FORM_exprloc
- 1, // ULEB128 dwarf expression length
- reg.dwarfLocOp(),
- });
- try self.addDbgInfoTypeReloc(inst.base.ty); // DW.AT_type, DW.FORM_ref4
- dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT_name, DW.FORM_string
- },
- .none => {},
- }
- },
- else => {},
- }
- return result;
- }
-
- fn genBreakpoint(self: *Self, src: usize) !MCValue {
- switch (arch) {
- .i386, .x86_64 => {
- try self.code.append(0xcc); // int3
- },
- .riscv64 => {
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ebreak.toU32());
- },
- .spu_2 => {
- try self.code.resize(self.code.items.len + 2);
- var instr = Instruction{ .condition = .always, .input0 = .zero, .input1 = .zero, .modify_flags = false, .output = .discard, .command = .undefined1 };
- mem.writeIntLittle(u16, self.code.items[self.code.items.len - 2 ..][0..2], @bitCast(u16, instr));
- },
- .arm => {
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bkpt(0).toU32());
- },
- .armeb => {
- mem.writeIntBig(u32, try self.code.addManyAsArray(4), Instruction.bkpt(0).toU32());
- },
- else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),
- }
- return .none;
- }
-
- fn genCall(self: *Self, inst: *ir.Inst.Call) !MCValue {
- var info = try self.resolveCallingConventionValues(inst.base.src, inst.func.ty);
- defer info.deinit(self);
-
- // Due to incremental compilation, how function calls are generated depends
- // on linking.
- if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) {
- switch (arch) {
- .x86_64 => {
- for (info.args) |mc_arg, arg_i| {
- const arg = inst.args[arg_i];
- const arg_mcv = try self.resolveInst(inst.args[arg_i]);
- // Here we do not use setRegOrMem even though the logic is similar, because
- // the function call will move the stack pointer, so the offsets are different.
- switch (mc_arg) {
- .none => continue,
- .register => |reg| {
- try self.genSetReg(arg.src, reg, arg_mcv);
- // TODO interact with the register allocator to mark the instruction as moved.
- },
- .stack_offset => {
- // Here we need to emit instructions like this:
- // mov qword ptr [rsp + stack_offset], x
- return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
- },
- .ptr_stack_offset => {
- return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});
- },
- .ptr_embedded_in_code => {
- return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
- },
- .undef => unreachable,
- .immediate => unreachable,
- .unreach => unreachable,
- .dead => unreachable,
- .embedded_in_code => unreachable,
- .memory => unreachable,
- .compare_flags_signed => unreachable,
- .compare_flags_unsigned => unreachable,
- }
- }
-
- if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
- if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
- const func = func_val.func;
-
- const ptr_bits = self.target.cpu.arch.ptrBitWidth();
- const ptr_bytes: u64 = @divExact(ptr_bits, 8);
- const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
- const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
- break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
- } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
- @intCast(u32, coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * ptr_bytes)
- else
- unreachable;
-
- // ff 14 25 xx xx xx xx call [addr]
- try self.code.ensureCapacity(self.code.items.len + 7);
- self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
- mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), got_addr);
- } else {
- return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
- }
- } else {
- return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
- }
- },
- .riscv64 => {
- if (info.args.len > 0) return self.fail(inst.base.src, "TODO implement fn args for {}", .{self.target.cpu.arch});
-
- if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
- if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
- const func = func_val.func;
-
- const ptr_bits = self.target.cpu.arch.ptrBitWidth();
- const ptr_bytes: u64 = @divExact(ptr_bits, 8);
- const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
- const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
- break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
- } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
- coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * ptr_bytes
- else
- unreachable;
-
- try self.genSetReg(inst.base.src, .ra, .{ .memory = got_addr });
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.ra, 0, .ra).toU32());
- } else {
- return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
- }
- } else {
- return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
- }
- },
- .spu_2 => {
- if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
- if (info.args.len != 0) {
- return self.fail(inst.base.src, "TODO implement call with more than 0 parameters", .{});
- }
- if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
- const func = func_val.func;
- const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
- const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
- break :blk @intCast(u16, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * 2);
- } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
- @intCast(u16, coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * 2)
- else
- unreachable;
-
- const return_type = func.owner_decl.typed_value.most_recent.typed_value.ty.fnReturnType();
- // First, push the return address, then jump; if noreturn, don't bother with the first step
- // TODO: implement packed struct -> u16 at comptime and move the bitcast here
- var instr = Instruction{ .condition = .always, .input0 = .immediate, .input1 = .zero, .modify_flags = false, .output = .jump, .command = .load16 };
- if (return_type.zigTypeTag() == .NoReturn) {
- try self.code.resize(self.code.items.len + 4);
- mem.writeIntLittle(u16, self.code.items[self.code.items.len - 4 ..][0..2], @bitCast(u16, instr));
- mem.writeIntLittle(u16, self.code.items[self.code.items.len - 2 ..][0..2], got_addr);
- return MCValue.unreach;
- } else {
- try self.code.resize(self.code.items.len + 8);
- var push = Instruction{ .condition = .always, .input0 = .immediate, .input1 = .zero, .modify_flags = false, .output = .push, .command = .ipget };
- mem.writeIntLittle(u16, self.code.items[self.code.items.len - 8 ..][0..2], @bitCast(u16, push));
- mem.writeIntLittle(u16, self.code.items[self.code.items.len - 6 ..][0..2], @as(u16, 4));
- mem.writeIntLittle(u16, self.code.items[self.code.items.len - 4 ..][0..2], @bitCast(u16, instr));
- mem.writeIntLittle(u16, self.code.items[self.code.items.len - 2 ..][0..2], got_addr);
- switch (return_type.zigTypeTag()) {
- .Void => return MCValue{ .none = {} },
- .NoReturn => unreachable,
- else => return self.fail(inst.base.src, "TODO implement fn call with non-void return value", .{}),
- }
- }
- } else {
- return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
- }
- } else {
- return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
- }
- },
- .arm => {
- if (info.args.len > 0) return self.fail(inst.base.src, "TODO implement fn args for {}", .{self.target.cpu.arch});
-
- if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
- if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
- const func = func_val.func;
- const ptr_bits = self.target.cpu.arch.ptrBitWidth();
- const ptr_bytes: u64 = @divExact(ptr_bits, 8);
- const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
- const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
- break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
- } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
- coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * ptr_bytes
- else
- unreachable;
-
- // TODO only works with leaf functions
- // at the moment, which works fine for
- // Hello World, but not for real code
- // of course. Add pushing lr to stack
- // and popping after call
- try self.genSetReg(inst.base.src, .lr, .{ .memory = got_addr });
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blx(.al, .lr).toU32());
- } else {
- return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
- }
- } else {
- return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
- }
- },
- else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),
- }
- } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
- switch (arch) {
- .x86_64 => {
- for (info.args) |mc_arg, arg_i| {
- const arg = inst.args[arg_i];
- const arg_mcv = try self.resolveInst(inst.args[arg_i]);
- // Here we do not use setRegOrMem even though the logic is similar, because
- // the function call will move the stack pointer, so the offsets are different.
- switch (mc_arg) {
- .none => continue,
- .register => |reg| {
- try self.genSetReg(arg.src, reg, arg_mcv);
- // TODO interact with the register allocator to mark the instruction as moved.
- },
- .stack_offset => {
- // Here we need to emit instructions like this:
- // mov qword ptr [rsp + stack_offset], x
- return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
- },
- .ptr_stack_offset => {
- return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});
- },
- .ptr_embedded_in_code => {
- return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
- },
- .undef => unreachable,
- .immediate => unreachable,
- .unreach => unreachable,
- .dead => unreachable,
- .embedded_in_code => unreachable,
- .memory => unreachable,
- .compare_flags_signed => unreachable,
- .compare_flags_unsigned => unreachable,
- }
- }
-
- if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
- if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
- const func = func_val.func;
- const got = &macho_file.sections.items[macho_file.got_section_index.?];
- const ptr_bytes = 8;
- const got_addr = @intCast(u32, got.addr + func.owner_decl.link.macho.offset_table_index.? * ptr_bytes);
- // ff 14 25 xx xx xx xx call [addr]
- try self.code.ensureCapacity(self.code.items.len + 7);
- self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
- mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), got_addr);
- } else {
- return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
- }
- } else {
- return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
- }
- },
- .aarch64 => return self.fail(inst.base.src, "TODO implement codegen for call when linking with MachO for aarch64 arch", .{}),
- else => unreachable,
- }
- } else {
- unreachable;
- }
-
- return info.return_value;
- }
-
- fn genRef(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
- const operand = try self.resolveInst(inst.operand);
- switch (operand) {
- .unreach => unreachable,
- .dead => unreachable,
- .none => return .none,
-
- .immediate,
- .register,
- .ptr_stack_offset,
- .ptr_embedded_in_code,
- .compare_flags_unsigned,
- .compare_flags_signed,
- => {
- const stack_offset = try self.allocMemPtr(&inst.base);
- try self.genSetStack(inst.base.src, inst.operand.ty, stack_offset, operand);
- return MCValue{ .ptr_stack_offset = stack_offset };
- },
-
- .stack_offset => |offset| return MCValue{ .ptr_stack_offset = offset },
- .embedded_in_code => |offset| return MCValue{ .ptr_embedded_in_code = offset },
- .memory => |vaddr| return MCValue{ .immediate = vaddr },
-
- .undef => return self.fail(inst.base.src, "TODO implement ref on an undefined value", .{}),
- }
- }
-
- fn ret(self: *Self, src: usize, mcv: MCValue) !MCValue {
- const ret_ty = self.fn_type.fnReturnType();
- try self.setRegOrMem(src, ret_ty, self.ret_mcv, mcv);
- switch (arch) {
- .i386 => {
- try self.code.append(0xc3); // ret
- },
- .x86_64 => {
- // TODO when implementing defer, this will need to jump to the appropriate defer expression.
- // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction
- // which is available if the jump is 127 bytes or less forward.
- try self.code.resize(self.code.items.len + 5);
- self.code.items[self.code.items.len - 5] = 0xe9; // jmp rel32
- try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4);
- },
- .riscv64 => {
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.zero, 0, .ra).toU32());
- },
- .arm => {
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bx(.al, .lr).toU32());
- },
- else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),
- }
- return .unreach;
- }
-
- fn genRet(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
- const operand = try self.resolveInst(inst.operand);
- return self.ret(inst.base.src, operand);
- }
-
- fn genRetVoid(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
- return self.ret(inst.base.src, .none);
- }
-
- fn genCmp(self: *Self, inst: *ir.Inst.BinOp, op: math.CompareOperator) !MCValue {
- // No side effects, so if it's unreferenced, do nothing.
- if (inst.base.isUnused())
- return MCValue.dead;
- switch (arch) {
- .x86_64 => {
- try self.code.ensureCapacity(self.code.items.len + 8);
-
- const lhs = try self.resolveInst(inst.lhs);
- const rhs = try self.resolveInst(inst.rhs);
-
- // There are 2 operands, destination and source.
- // Either one, but not both, can be a memory operand.
- // Source operand can be an immediate, 8 bits or 32 bits.
- const dst_mcv = if (lhs.isImmediate() or (lhs.isMemory() and rhs.isMemory()))
- try self.copyToNewRegister(&inst.base, lhs)
- else
- lhs;
- // This instruction supports only signed 32-bit immediates at most.
- const src_mcv = try self.limitImmediateType(inst.rhs, i32);
-
- try self.genX8664BinMathCode(inst.base.src, inst.base.ty, dst_mcv, src_mcv, 7, 0x38);
- const info = inst.lhs.ty.intInfo(self.target.*);
- if (info.signed) {
- return MCValue{ .compare_flags_signed = op };
- } else {
- return MCValue{ .compare_flags_unsigned = op };
- }
- },
- else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}),
- }
- }
-
- fn genDbgStmt(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
- try self.dbgAdvancePCAndLine(inst.base.src);
- assert(inst.base.isUnused());
- return MCValue.dead;
- }
-
- fn genCondBr(self: *Self, inst: *ir.Inst.CondBr) !MCValue {
- const cond = try self.resolveInst(inst.condition);
-
- const reloc: Reloc = switch (arch) {
- .i386, .x86_64 => reloc: {
- try self.code.ensureCapacity(self.code.items.len + 6);
-
- const opcode: u8 = switch (cond) {
- .compare_flags_signed => |cmp_op| blk: {
- // Here we map to the opposite opcode because the jump is to the false branch.
- const opcode: u8 = switch (cmp_op) {
- .gte => 0x8c,
- .gt => 0x8e,
- .neq => 0x84,
- .lt => 0x8d,
- .lte => 0x8f,
- .eq => 0x85,
- };
- break :blk opcode;
- },
- .compare_flags_unsigned => |cmp_op| blk: {
- // Here we map to the opposite opcode because the jump is to the false branch.
- const opcode: u8 = switch (cmp_op) {
- .gte => 0x82,
- .gt => 0x86,
- .neq => 0x84,
- .lt => 0x83,
- .lte => 0x87,
- .eq => 0x85,
- };
- break :blk opcode;
- },
- .register => |reg| blk: {
- // test reg, 1
- // TODO detect al, ax, eax
- try self.code.ensureCapacity(self.code.items.len + 4);
- // TODO audit this codegen: we force w = true here to make
- // the value affect the big register
- self.rex(.{ .b = reg.isExtended(), .w = true });
- self.code.appendSliceAssumeCapacity(&[_]u8{
- 0xf6,
- @as(u8, 0xC0) | (0 << 3) | @truncate(u3, reg.id()),
- 0x01,
- });
- break :blk 0x84;
- },
- else => return self.fail(inst.base.src, "TODO implement condbr {} when condition is {}", .{ self.target.cpu.arch, @tagName(cond) }),
- };
- self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode });
- const reloc = Reloc{ .rel32 = self.code.items.len };
- self.code.items.len += 4;
- break :reloc reloc;
- },
- else => return self.fail(inst.base.src, "TODO implement condbr {}", .{ self.target.cpu.arch }),
- };
-
- // Capture the state of register and stack allocation state so that we can revert to it.
- const parent_next_stack_offset = self.next_stack_offset;
- const parent_free_registers = self.free_registers;
- var parent_stack = try self.stack.clone(self.gpa);
- defer parent_stack.deinit(self.gpa);
- var parent_registers = try self.registers.clone(self.gpa);
- defer parent_registers.deinit(self.gpa);
-
- try self.branch_stack.append(.{});
-
- const then_deaths = inst.thenDeaths();
- try self.ensureProcessDeathCapacity(then_deaths.len);
- for (then_deaths) |operand| {
- self.processDeath(operand);
- }
- try self.genBody(inst.then_body);
-
- // Revert to the previous register and stack allocation state.
-
- var saved_then_branch = self.branch_stack.pop();
- defer saved_then_branch.deinit(self.gpa);
-
- self.registers.deinit(self.gpa);
- self.registers = parent_registers;
- parent_registers = .{};
-
- self.stack.deinit(self.gpa);
- self.stack = parent_stack;
- parent_stack = .{};
-
- self.next_stack_offset = parent_next_stack_offset;
- self.free_registers = parent_free_registers;
-
- try self.performReloc(inst.base.src, reloc);
- const else_branch = self.branch_stack.addOneAssumeCapacity();
- else_branch.* = .{};
-
- const else_deaths = inst.elseDeaths();
- try self.ensureProcessDeathCapacity(else_deaths.len);
- for (else_deaths) |operand| {
- self.processDeath(operand);
- }
- try self.genBody(inst.else_body);
-
- // At this point, each branch will possibly have conflicting values for where
- // each instruction is stored. They agree, however, on which instructions are alive/dead.
- // We use the first ("then") branch as canonical, and here emit
- // instructions into the second ("else") branch to make it conform.
- // We continue respect the data structure semantic guarantees of the else_branch so
- // that we can use all the code emitting abstractions. This is why at the bottom we
- // assert that parent_branch.free_registers equals the saved_then_branch.free_registers
- // rather than assigning it.
- const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 2];
- try parent_branch.inst_table.ensureCapacity(self.gpa, parent_branch.inst_table.items().len +
- else_branch.inst_table.items().len);
- for (else_branch.inst_table.items()) |else_entry| {
- const canon_mcv = if (saved_then_branch.inst_table.remove(else_entry.key)) |then_entry| blk: {
- // The instruction's MCValue is overridden in both branches.
- parent_branch.inst_table.putAssumeCapacity(else_entry.key, then_entry.value);
- if (else_entry.value == .dead) {
- assert(then_entry.value == .dead);
- continue;
- }
- break :blk then_entry.value;
- } else blk: {
- if (else_entry.value == .dead)
- continue;
- // The instruction is only overridden in the else branch.
- var i: usize = self.branch_stack.items.len - 2;
- while (true) {
- i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead?
- if (self.branch_stack.items[i].inst_table.get(else_entry.key)) |mcv| {
- assert(mcv != .dead);
- break :blk mcv;
- }
- }
- };
- log.debug("consolidating else_entry {*} {}=>{}", .{else_entry.key, else_entry.value, canon_mcv});
- // TODO make sure the destination stack offset / register does not already have something
- // going on there.
- try self.setRegOrMem(inst.base.src, else_entry.key.ty, canon_mcv, else_entry.value);
- // TODO track the new register / stack allocation
- }
- try parent_branch.inst_table.ensureCapacity(self.gpa, parent_branch.inst_table.items().len +
- saved_then_branch.inst_table.items().len);
- for (saved_then_branch.inst_table.items()) |then_entry| {
- // We already deleted the items from this table that matched the else_branch.
- // So these are all instructions that are only overridden in the then branch.
- parent_branch.inst_table.putAssumeCapacity(then_entry.key, then_entry.value);
- if (then_entry.value == .dead)
- continue;
- const parent_mcv = blk: {
- var i: usize = self.branch_stack.items.len - 2;
- while (true) {
- i -= 1;
- if (self.branch_stack.items[i].inst_table.get(then_entry.key)) |mcv| {
- assert(mcv != .dead);
- break :blk mcv;
- }
- }
- };
- log.debug("consolidating then_entry {*} {}=>{}", .{then_entry.key, parent_mcv, then_entry.value});
- // TODO make sure the destination stack offset / register does not already have something
- // going on there.
- try self.setRegOrMem(inst.base.src, then_entry.key.ty, parent_mcv, then_entry.value);
- // TODO track the new register / stack allocation
- }
-
- self.branch_stack.pop().deinit(self.gpa);
-
- return MCValue.unreach;
- }
-
- fn genIsNull(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
- switch (arch) {
- else => return self.fail(inst.base.src, "TODO implement isnull for {}", .{self.target.cpu.arch}),
- }
- }
-
- fn genIsNonNull(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
- // Here you can specialize this instruction if it makes sense to, otherwise the default
- // will call genIsNull and invert the result.
- switch (arch) {
- else => return self.fail(inst.base.src, "TODO call genIsNull and invert the result ", .{}),
- }
- }
-
- fn genIsErr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
- switch (arch) {
- else => return self.fail(inst.base.src, "TODO implement iserr for {}", .{self.target.cpu.arch}),
- }
- }
-
- fn genLoop(self: *Self, inst: *ir.Inst.Loop) !MCValue {
- // A loop is a setup to be able to jump back to the beginning.
- const start_index = self.code.items.len;
- try self.genBody(inst.body);
- try self.jump(inst.base.src, start_index);
- return MCValue.unreach;
- }
-
- /// Send control flow to the `index` of `self.code`.
- fn jump(self: *Self, src: usize, index: usize) !void {
- switch (arch) {
- .i386, .x86_64 => {
- try self.code.ensureCapacity(self.code.items.len + 5);
- if (math.cast(i8, @intCast(i32, index) - (@intCast(i32, self.code.items.len + 2)))) |delta| {
- self.code.appendAssumeCapacity(0xeb); // jmp rel8
- self.code.appendAssumeCapacity(@bitCast(u8, delta));
- } else |_| {
- const delta = @intCast(i32, index) - (@intCast(i32, self.code.items.len + 5));
- self.code.appendAssumeCapacity(0xe9); // jmp rel32
- mem.writeIntLittle(i32, self.code.addManyAsArrayAssumeCapacity(4), delta);
- }
- },
- else => return self.fail(src, "TODO implement jump for {}", .{self.target.cpu.arch}),
- }
- }
-
- fn genBlock(self: *Self, inst: *ir.Inst.Block) !MCValue {
- inst.codegen = .{
- // A block is a setup to be able to jump to the end.
- .relocs = .{},
- // It also acts as a receptical for break operands.
- // Here we use `MCValue.none` to represent a null value so that the first
- // break instruction will choose a MCValue for the block result and overwrite
- // this field. Following break instructions will use that MCValue to put their
- // block results.
- .mcv = @bitCast(AnyMCValue, MCValue { .none = {} }),
- };
- defer inst.codegen.relocs.deinit(self.gpa);
-
- try self.genBody(inst.body);
-
- for (inst.codegen.relocs.items) |reloc| try self.performReloc(inst.base.src, reloc);
-
- return @bitCast(MCValue, inst.codegen.mcv);
- }
-
- fn performReloc(self: *Self, src: usize, reloc: Reloc) !void {
- switch (reloc) {
- .rel32 => |pos| {
- const amt = self.code.items.len - (pos + 4);
- // Here it would be tempting to implement testing for amt == 0 and then elide the
- // jump. However, that will cause a problem because other jumps may assume that they
- // can jump to this code. Or maybe I didn't understand something when I was debugging.
- // It could be worth another look. Anyway, that's why that isn't done here. Probably the
- // best place to elide jumps will be in semantic analysis, by inlining blocks that only
- // only have 1 break instruction.
- const s32_amt = math.cast(i32, amt) catch
- return self.fail(src, "unable to perform relocation: jump too far", .{});
- mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt);
- },
- }
- }
-
- fn genBr(self: *Self, inst: *ir.Inst.Br) !MCValue {
- if (inst.operand.ty.hasCodeGenBits()) {
- const operand = try self.resolveInst(inst.operand);
- const block_mcv = @bitCast(MCValue, inst.block.codegen.mcv);
- if (block_mcv == .none) {
- inst.block.codegen.mcv = @bitCast(AnyMCValue, operand);
- } else {
- try self.setRegOrMem(inst.base.src, inst.block.base.ty, block_mcv, operand);
- }
- }
- return self.brVoid(inst.base.src, inst.block);
- }
-
- fn genBrVoid(self: *Self, inst: *ir.Inst.BrVoid) !MCValue {
- return self.brVoid(inst.base.src, inst.block);
- }
-
- fn brVoid(self: *Self, src: usize, block: *ir.Inst.Block) !MCValue {
- // Emit a jump with a relocation. It will be patched up after the block ends.
- try block.codegen.relocs.ensureCapacity(self.gpa, block.codegen.relocs.items.len + 1);
-
- switch (arch) {
- .i386, .x86_64 => {
- // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction
- // which is available if the jump is 127 bytes or less forward.
- try self.code.resize(self.code.items.len + 5);
- self.code.items[self.code.items.len - 5] = 0xe9; // jmp rel32
- // Leave the jump offset undefined
- block.codegen.relocs.appendAssumeCapacity(.{ .rel32 = self.code.items.len - 4 });
- },
- else => return self.fail(src, "TODO implement brvoid for {}", .{self.target.cpu.arch}),
- }
- return .none;
- }
-
- fn genAsm(self: *Self, inst: *ir.Inst.Assembly) !MCValue {
- if (!inst.is_volatile and inst.base.isUnused())
- return MCValue.dead;
- switch (arch) {
- .spu_2 => {
- if (inst.inputs.len > 0 or inst.output != null) {
- return self.fail(inst.base.src, "TODO implement inline asm inputs / outputs for SPU Mark II", .{});
- }
- if (mem.eql(u8, inst.asm_source, "undefined0")) {
- try self.code.resize(self.code.items.len + 2);
- var instr = Instruction{ .condition = .always, .input0 = .zero, .input1 = .zero, .modify_flags = false, .output = .discard, .command = .undefined0 };
- mem.writeIntLittle(u16, self.code.items[self.code.items.len - 2 ..][0..2], @bitCast(u16, instr));
- return MCValue.none;
- } else {
- return self.fail(inst.base.src, "TODO implement support for more SPU II assembly instructions", .{});
- }
- },
- .arm => {
- for (inst.inputs) |input, i| {
- if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
- return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});
- }
- const reg_name = input[1 .. input.len - 1];
- const reg = parseRegName(reg_name) orelse
- return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
- const arg = try self.resolveInst(inst.args[i]);
- try self.genSetReg(inst.base.src, reg, arg);
- }
-
- if (mem.eql(u8, inst.asm_source, "svc #0")) {
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(.al, 0).toU32());
- } else {
- return self.fail(inst.base.src, "TODO implement support for more arm assembly instructions", .{});
- }
-
- if (inst.output) |output| {
- if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
- return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});
- }
- const reg_name = output[2 .. output.len - 1];
- const reg = parseRegName(reg_name) orelse
- return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
- return MCValue{ .register = reg };
- } else {
- return MCValue.none;
- }
- },
- .riscv64 => {
- for (inst.inputs) |input, i| {
- if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
- return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});
- }
- const reg_name = input[1 .. input.len - 1];
- const reg = parseRegName(reg_name) orelse
- return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
- const arg = try self.resolveInst(inst.args[i]);
- try self.genSetReg(inst.base.src, reg, arg);
- }
-
- if (mem.eql(u8, inst.asm_source, "ecall")) {
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ecall.toU32());
- } else {
- return self.fail(inst.base.src, "TODO implement support for more riscv64 assembly instructions", .{});
- }
-
- if (inst.output) |output| {
- if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
- return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});
- }
- const reg_name = output[2 .. output.len - 1];
- const reg = parseRegName(reg_name) orelse
- return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
- return MCValue{ .register = reg };
- } else {
- return MCValue.none;
- }
- },
- .x86_64, .i386 => {
- for (inst.inputs) |input, i| {
- if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
- return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});
- }
- const reg_name = input[1 .. input.len - 1];
- const reg = parseRegName(reg_name) orelse
- return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
- const arg = try self.resolveInst(inst.args[i]);
- try self.genSetReg(inst.base.src, reg, arg);
- }
-
- if (mem.eql(u8, inst.asm_source, "syscall")) {
- try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });
- } else if (inst.asm_source.len != 0) {
- return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
- }
-
- if (inst.output) |output| {
- if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
- return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});
- }
- const reg_name = output[2 .. output.len - 1];
- const reg = parseRegName(reg_name) orelse
- return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
- return MCValue{ .register = reg };
- } else {
- return MCValue.none;
- }
- },
- else => return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{}),
- }
- }
-
- /// Encodes a REX prefix as specified, and appends it to the instruction
- /// stream. This only modifies the instruction stream if at least one bit
- /// is set true, which has a few implications:
- ///
- /// * The length of the instruction buffer will be modified *if* the
- /// resulting REX is meaningful, but will remain the same if it is not.
- /// * Deliberately inserting a "meaningless REX" requires explicit usage of
- /// 0x40, and cannot be done via this function.
- /// W => 64 bit mode
- /// R => extension to the MODRM.reg field
- /// X => extension to the SIB.index field
- /// B => extension to the MODRM.rm field or the SIB.base field
- fn rex(self: *Self, arg: struct { b: bool = false, w: bool = false, x: bool = false, r: bool = false }) void {
- comptime assert(arch == .x86_64);
- // From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB.
- var value: u8 = 0x40;
- if (arg.b) {
- value |= 0x1;
- }
- if (arg.x) {
- value |= 0x2;
- }
- if (arg.r) {
- value |= 0x4;
- }
- if (arg.w) {
- value |= 0x8;
- }
- if (value != 0x40) {
- self.code.appendAssumeCapacity(value);
- }
- }
-
- /// Sets the value without any modifications to register allocation metadata or stack allocation metadata.
- fn setRegOrMem(self: *Self, src: usize, ty: Type, loc: MCValue, val: MCValue) !void {
- switch (loc) {
- .none => return,
- .register => |reg| return self.genSetReg(src, reg, val),
- .stack_offset => |off| return self.genSetStack(src, ty, off, val),
- .memory => {
- return self.fail(src, "TODO implement setRegOrMem for memory", .{});
- },
- else => unreachable,
- }
- }
-
- fn genSetStack(self: *Self, src: usize, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
- switch (arch) {
- .x86_64 => switch (mcv) {
- .dead => unreachable,
- .ptr_stack_offset => unreachable,
- .ptr_embedded_in_code => unreachable,
- .unreach, .none => return, // Nothing to do.
- .undef => {
- if (!self.wantSafety())
- return; // The already existing value will do just fine.
- // TODO Upgrade this to a memset call when we have that available.
- switch (ty.abiSize(self.target.*)) {
- 1 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaa }),
- 2 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaa }),
- 4 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
- 8 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
- else => return self.fail(src, "TODO implement memset", .{}),
- }
- },
- .compare_flags_unsigned => |op| {
- return self.fail(src, "TODO implement set stack variable with compare flags value (unsigned)", .{});
- },
- .compare_flags_signed => |op| {
- return self.fail(src, "TODO implement set stack variable with compare flags value (signed)", .{});
- },
- .immediate => |x_big| {
- const abi_size = ty.abiSize(self.target.*);
- const adj_off = stack_offset + abi_size;
- if (adj_off > 128) {
- return self.fail(src, "TODO implement set stack variable with large stack offset", .{});
- }
- try self.code.ensureCapacity(self.code.items.len + 8);
- switch (abi_size) {
- 1 => {
- return self.fail(src, "TODO implement set abi_size=1 stack variable with immediate", .{});
- },
- 2 => {
- return self.fail(src, "TODO implement set abi_size=2 stack variable with immediate", .{});
- },
- 4 => {
- const x = @intCast(u32, x_big);
- // We have a positive stack offset value but we want a twos complement negative
- // offset from rbp, which is at the top of the stack frame.
- const negative_offset = @intCast(i8, -@intCast(i32, adj_off));
- const twos_comp = @bitCast(u8, negative_offset);
- // mov DWORD PTR [rbp+offset], immediate
- self.code.appendSliceAssumeCapacity(&[_]u8{ 0xc7, 0x45, twos_comp });
- mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), x);
- },
- 8 => {
- // We have a positive stack offset value but we want a twos complement negative
- // offset from rbp, which is at the top of the stack frame.
- const negative_offset = @intCast(i8, -@intCast(i32, adj_off));
- const twos_comp = @bitCast(u8, negative_offset);
-
- // 64 bit write to memory would take two mov's anyways so we
- // insted just use two 32 bit writes to avoid register allocation
- try self.code.ensureCapacity(self.code.items.len + 14);
- var buf: [8]u8 = undefined;
- mem.writeIntLittle(u64, &buf, x_big);
-
- // mov DWORD PTR [rbp+offset+4], immediate
- self.code.appendSliceAssumeCapacity(&[_]u8{ 0xc7, 0x45, twos_comp + 4});
- self.code.appendSliceAssumeCapacity(buf[4..8]);
-
- // mov DWORD PTR [rbp+offset], immediate
- self.code.appendSliceAssumeCapacity(&[_]u8{ 0xc7, 0x45, twos_comp });
- self.code.appendSliceAssumeCapacity(buf[0..4]);
- },
- else => {
- return self.fail(src, "TODO implement set abi_size=large stack variable with immediate", .{});
- },
- }
- },
- .embedded_in_code => |code_offset| {
- return self.fail(src, "TODO implement set stack variable from embedded_in_code", .{});
- },
- .register => |reg| {
- try self.genX8664ModRMRegToStack(src, ty, stack_offset, reg, 0x89);
- },
- .memory => |vaddr| {
- return self.fail(src, "TODO implement set stack variable from memory vaddr", .{});
- },
- .stack_offset => |off| {
- if (stack_offset == off)
- return; // Copy stack variable to itself; nothing to do.
-
- const reg = try self.copyToTmpRegister(src, mcv);
- return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });
- },
- },
- else => return self.fail(src, "TODO implement getSetStack for {}", .{self.target.cpu.arch}),
- }
- }
-
- fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) InnerError!void {
- switch (arch) {
- .arm => switch (mcv) {
- .dead => unreachable,
- .ptr_stack_offset => unreachable,
- .ptr_embedded_in_code => unreachable,
- .unreach, .none => return, // Nothing to do.
- .undef => {
- if (!self.wantSafety())
- return; // The already existing value will do just fine.
- // Write the debug undefined value.
- return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa });
- },
- .immediate => |x| {
- // TODO better analysis of x to determine the
- // least amount of necessary instructions (use
- // more intelligent rotating)
- if (x <= math.maxInt(u8)) {
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
- return;
- } else if (x <= math.maxInt(u16)) {
- // TODO Use movw Note: Not supported on
- // all ARM targets!
-
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());
- } else if (x <= math.maxInt(u32)) {
- // TODO Use movw and movt Note: Not
- // supported on all ARM targets! Also TODO
- // write constant to code and load
- // relative to pc
-
- // immediate: 0xaabbccdd
- // mov reg, #0xaa
- // orr reg, reg, #0xbb, 24
- // orr reg, reg, #0xcc, 16
- // orr reg, reg, #0xdd, 8
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 16), 8)).toU32());
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 24), 4)).toU32());
- return;
- } else {
- return self.fail(src, "ARM registers are 32-bit wide", .{});
- }
- },
- .memory => |addr| {
- // The value is in memory at a hard-coded address.
- // If the type is a pointer, it means the pointer address is at this memory location.
- try self.genSetReg(src, reg, .{ .immediate = addr });
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, Instruction.Offset.none).toU32());
- },
- else => return self.fail(src, "TODO implement getSetReg for arm {}", .{mcv}),
- },
- .riscv64 => switch (mcv) {
- .dead => unreachable,
- .ptr_stack_offset => unreachable,
- .ptr_embedded_in_code => unreachable,
- .unreach, .none => return, // Nothing to do.
- .undef => {
- if (!self.wantSafety())
- return; // The already existing value will do just fine.
- // Write the debug undefined value.
- return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa });
- },
- .immediate => |unsigned_x| {
- const x = @bitCast(i64, unsigned_x);
- if (math.minInt(i12) <= x and x <= math.maxInt(i12)) {
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.addi(reg, .zero, @truncate(i12, x)).toU32());
- return;
- }
- if (math.minInt(i32) <= x and x <= math.maxInt(i32)) {
- const lo12 = @truncate(i12, x);
- const carry: i32 = if (lo12 < 0) 1 else 0;
- const hi20 = @truncate(i20, (x >> 12) +% carry);
-
- // TODO: add test case for 32-bit immediate
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.lui(reg, hi20).toU32());
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.addi(reg, reg, lo12).toU32());
- return;
- }
- // li rd, immediate
- // "Myriad sequences"
- return self.fail(src, "TODO genSetReg 33-64 bit immediates for riscv64", .{}); // glhf
- },
- .memory => |addr| {
- // The value is in memory at a hard-coded address.
- // If the type is a pointer, it means the pointer address is at this memory location.
- try self.genSetReg(src, reg, .{ .immediate = addr });
-
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ld(reg, 0, reg).toU32());
- // LOAD imm=[i12 offset = 0], rs1 =
-
- // return self.fail("TODO implement genSetReg memory for riscv64");
- },
- else => return self.fail(src, "TODO implement getSetReg for riscv64 {}", .{mcv}),
- },
- .x86_64 => switch (mcv) {
- .dead => unreachable,
- .ptr_stack_offset => unreachable,
- .ptr_embedded_in_code => unreachable,
- .unreach, .none => return, // Nothing to do.
- .undef => {
- if (!self.wantSafety())
- return; // The already existing value will do just fine.
- // Write the debug undefined value.
- switch (reg.size()) {
- 8 => return self.genSetReg(src, reg, .{ .immediate = 0xaa }),
- 16 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaa }),
- 32 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa }),
- 64 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
- else => unreachable,
- }
- },
- .compare_flags_unsigned => |op| {
- try self.code.ensureCapacity(self.code.items.len + 3);
- // TODO audit this codegen: we force w = true here to make
- // the value affect the big register
- self.rex(.{ .b = reg.isExtended(), .w = true });
- const opcode: u8 = switch (op) {
- .gte => 0x93,
- .gt => 0x97,
- .neq => 0x95,
- .lt => 0x92,
- .lte => 0x96,
- .eq => 0x94,
- };
- const id = @as(u8, reg.id() & 0b111);
- self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode, 0xC0 | id });
- },
- .compare_flags_signed => |op| {
- return self.fail(src, "TODO set register with compare flags value (signed)", .{});
- },
- .immediate => |x| {
- // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit
- // register is the fastest way to zero a register.
- if (x == 0) {
- // The encoding for `xor r32, r32` is `0x31 /r`.
- // Section 3.1.1.1 of the Intel x64 Manual states that "/r indicates that the
- // ModR/M byte of the instruction contains a register operand and an r/m operand."
- //
- // R/M bytes are composed of two bits for the mode, then three bits for the register,
- // then three bits for the operand. Since we're zeroing a register, the two three-bit
- // values will be identical, and the mode is three (the raw register value).
- //
- // If we're accessing e.g. r8d, we need to use a REX prefix before the actual operation. Since
- // this is a 32-bit operation, the W flag is set to zero. X is also zero, as we're not using a SIB.
- // Both R and B are set, as we're extending, in effect, the register bits *and* the operand.
- try self.code.ensureCapacity(self.code.items.len + 3);
- self.rex(.{ .r = reg.isExtended(), .b = reg.isExtended() });
- const id = @as(u8, reg.id() & 0b111);
- self.code.appendSliceAssumeCapacity(&[_]u8{ 0x31, 0xC0 | id << 3 | id });
- return;
- }
- if (x <= math.maxInt(u32)) {
- // Next best case: if we set the lower four bytes, the upper four will be zeroed.
- //
- // The encoding for `mov IMM32 -> REG` is (0xB8 + R) IMM.
- if (reg.isExtended()) {
- // Just as with XORing, we need a REX prefix. This time though, we only
- // need the B bit set, as we're extending the opcode's register field,
- // and there is no Mod R/M byte.
- //
- // Thus, we need b01000001, or 0x41.
- try self.code.resize(self.code.items.len + 6);
- self.code.items[self.code.items.len - 6] = 0x41;
- } else {
- try self.code.resize(self.code.items.len + 5);
- }
- self.code.items[self.code.items.len - 5] = 0xB8 | @as(u8, reg.id() & 0b111);
- const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4];
- mem.writeIntLittle(u32, imm_ptr, @intCast(u32, x));
- return;
- }
- // Worst case: we need to load the 64-bit register with the IMM. GNU's assemblers calls
- // this `movabs`, though this is officially just a different variant of the plain `mov`
- // instruction.
- //
- // This encoding is, in fact, the *same* as the one used for 32-bit loads. The only
- // difference is that we set REX.W before the instruction, which extends the load to
- // 64-bit and uses the full bit-width of the register.
- //
- // Since we always need a REX here, let's just check if we also need to set REX.B.
- //
- // In this case, the encoding of the REX byte is 0b0100100B
- try self.code.ensureCapacity(self.code.items.len + 10);
- self.rex(.{ .w = reg.size() == 64, .b = reg.isExtended() });
- self.code.items.len += 9;
- self.code.items[self.code.items.len - 9] = 0xB8 | @as(u8, reg.id() & 0b111);
- const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8];
- mem.writeIntLittle(u64, imm_ptr, x);
- },
- .embedded_in_code => |code_offset| {
- // We need the offset from RIP in a signed i32 twos complement.
- // The instruction is 7 bytes long and RIP points to the next instruction.
- try self.code.ensureCapacity(self.code.items.len + 7);
- // 64-bit LEA is encoded as REX.W 8D /r. If the register is extended, the REX byte is modified,
- // but the operation size is unchanged. Since we're using a disp32, we want mode 0 and lower three
- // bits as five.
- // REX 0x8D 0b00RRR101, where RRR is the lower three bits of the id.
- self.rex(.{ .w = reg.size() == 64, .b = reg.isExtended() });
- self.code.items.len += 6;
- const rip = self.code.items.len;
- const big_offset = @intCast(i64, code_offset) - @intCast(i64, rip);
- const offset = @intCast(i32, big_offset);
- self.code.items[self.code.items.len - 6] = 0x8D;
- self.code.items[self.code.items.len - 5] = 0b101 | (@as(u8, reg.id() & 0b111) << 3);
- const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4];
- mem.writeIntLittle(i32, imm_ptr, offset);
- },
- .register => |src_reg| {
- // If the registers are the same, nothing to do.
- if (src_reg.id() == reg.id())
- return;
-
- // This is a variant of 8B /r. Since we're using 64-bit moves, we require a REX.
- // This is thus three bytes: REX 0x8B R/M.
- // If the destination is extended, the R field must be 1.
- // If the *source* is extended, the B field must be 1.
- // Since the register is being accessed directly, the R/M mode is three. The reg field (the middle
- // three bits) contain the destination, and the R/M field (the lower three bits) contain the source.
- try self.code.ensureCapacity(self.code.items.len + 3);
- self.rex(.{ .w = reg.size() == 64, .r = reg.isExtended(), .b = src_reg.isExtended() });
- const R = 0xC0 | (@as(u8, reg.id() & 0b111) << 3) | @as(u8, src_reg.id() & 0b111);
- self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, R });
- },
- .memory => |x| {
- if (x <= math.maxInt(u32)) {
- // Moving from memory to a register is a variant of `8B /r`.
- // Since we're using 64-bit moves, we require a REX.
- // This variant also requires a SIB, as it would otherwise be RIP-relative.
- // We want mode zero with the lower three bits set to four to indicate an SIB with no other displacement.
- // The SIB must be 0x25, to indicate a disp32 with no scaled index.
- // 0b00RRR100, where RRR is the lower three bits of the register ID.
- // The instruction is thus eight bytes; REX 0x8B 0b00RRR100 0x25 followed by a four-byte disp32.
- try self.code.ensureCapacity(self.code.items.len + 8);
- self.rex(.{ .w = reg.size() == 64, .b = reg.isExtended() });
- self.code.appendSliceAssumeCapacity(&[_]u8{
- 0x8B,
- 0x04 | (@as(u8, reg.id() & 0b111) << 3), // R
- 0x25,
- });
- mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), @intCast(u32, x));
- } else {
- // If this is RAX, we can use a direct load; otherwise, we need to load the address, then indirectly load
- // the value.
- if (reg.id() == 0) {
- // REX.W 0xA1 moffs64*
- // moffs64* is a 64-bit offset "relative to segment base", which really just means the
- // absolute address for all practical purposes.
- try self.code.resize(self.code.items.len + 10);
- // REX.W == 0x48
- self.code.items[self.code.items.len - 10] = 0x48;
- self.code.items[self.code.items.len - 9] = 0xA1;
- const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8];
- mem.writeIntLittle(u64, imm_ptr, x);
- } else {
- // This requires two instructions; a move imm as used above, followed by an indirect load using the register
- // as the address and the register as the destination.
- //
- // This cannot be used if the lower three bits of the id are equal to four or five, as there
- // is no way to possibly encode it. This means that RSP, RBP, R12, and R13 cannot be used with
- // this instruction.
- const id3 = @truncate(u3, reg.id());
- assert(id3 != 4 and id3 != 5);
-
- // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue.
- try self.genSetReg(src, reg, MCValue{ .immediate = x });
-
- // Now, the register contains the address of the value to load into it
- // Currently, we're only allowing 64-bit registers, so we need the `REX.W 8B /r` variant.
- // TODO: determine whether to allow other sized registers, and if so, handle them properly.
- // This operation requires three bytes: REX 0x8B R/M
- try self.code.ensureCapacity(self.code.items.len + 3);
- // For this operation, we want R/M mode *zero* (use register indirectly), and the two register
- // values must match. Thus, it's 00ABCABC where ABC is the lower three bits of the register ID.
- //
- // Furthermore, if this is an extended register, both B and R must be set in the REX byte, as *both*
- // register operands need to be marked as extended.
- self.rex(.{ .w = reg.size() == 64, .b = reg.isExtended(), .r = reg.isExtended() });
- const RM = (@as(u8, reg.id() & 0b111) << 3) | @truncate(u3, reg.id());
- self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, RM });
- }
- }
- },
- .stack_offset => |unadjusted_off| {
- try self.code.ensureCapacity(self.code.items.len + 7);
- const size_bytes = @divExact(reg.size(), 8);
- const off = unadjusted_off + size_bytes;
- self.rex(.{ .w = reg.size() == 64, .r = reg.isExtended() });
- const reg_id: u8 = @truncate(u3, reg.id());
- if (off <= 128) {
- // Example: 48 8b 4d 7f mov rcx,QWORD PTR [rbp+0x7f]
- const RM = @as(u8, 0b01_000_101) | (reg_id << 3);
- const negative_offset = @intCast(i8, -@intCast(i32, off));
- const twos_comp = @bitCast(u8, negative_offset);
- self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8b, RM, twos_comp });
- } else if (off <= 2147483648) {
- // Example: 48 8b 8d 80 00 00 00 mov rcx,QWORD PTR [rbp+0x80]
- const RM = @as(u8, 0b10_000_101) | (reg_id << 3);
- const negative_offset = @intCast(i32, -@intCast(i33, off));
- const twos_comp = @bitCast(u32, negative_offset);
- self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8b, RM });
- mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), twos_comp);
- } else {
- return self.fail(src, "stack offset too large", .{});
- }
- },
- },
- else => return self.fail(src, "TODO implement getSetReg for {}", .{self.target.cpu.arch}),
- }
- }
-
- fn genPtrToInt(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
- // no-op
- return self.resolveInst(inst.operand);
- }
-
- fn genBitCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
- const operand = try self.resolveInst(inst.operand);
- return operand;
- }
-
- fn resolveInst(self: *Self, inst: *ir.Inst) !MCValue {
- // If the type has no codegen bits, no need to store it.
- if (!inst.ty.hasCodeGenBits())
- return MCValue.none;
-
- // Constants have static lifetimes, so they are always memoized in the outer most table.
- if (inst.castTag(.constant)) |const_inst| {
- const branch = &self.branch_stack.items[0];
- const gop = try branch.inst_table.getOrPut(self.gpa, inst);
- if (!gop.found_existing) {
- gop.entry.value = try self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });
- }
- return gop.entry.value;
- }
-
- return self.getResolvedInstValue(inst);
- }
-
- fn getResolvedInstValue(self: *Self, inst: *ir.Inst) MCValue {
- // Treat each stack item as a "layer" on top of the previous one.
- var i: usize = self.branch_stack.items.len;
- while (true) {
- i -= 1;
- if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| {
- assert(mcv != .dead);
- return mcv;
- }
- }
- }
-
- /// If the MCValue is an immediate, and it does not fit within this type,
- /// we put it in a register.
- /// A potential opportunity for future optimization here would be keeping track
- /// of the fact that the instruction is available both as an immediate
- /// and as a register.
- fn limitImmediateType(self: *Self, inst: *ir.Inst, comptime T: type) !MCValue {
- const mcv = try self.resolveInst(inst);
- const ti = @typeInfo(T).Int;
- switch (mcv) {
- .immediate => |imm| {
- // This immediate is unsigned.
- const U = @Type(.{
- .Int = .{
- .bits = ti.bits - @boolToInt(ti.is_signed),
- .is_signed = false,
- },
- });
- if (imm >= math.maxInt(U)) {
- return MCValue{ .register = try self.copyToTmpRegister(inst.src, mcv) };
- }
- },
- else => {},
- }
- return mcv;
- }
-
- fn genTypedValue(self: *Self, src: usize, typed_value: TypedValue) InnerError!MCValue {
- if (typed_value.val.isUndef())
- return MCValue{ .undef = {} };
- const ptr_bits = self.target.cpu.arch.ptrBitWidth();
- const ptr_bytes: u64 = @divExact(ptr_bits, 8);
- switch (typed_value.ty.zigTypeTag()) {
- .Pointer => {
- if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {
- if (self.bin_file.cast(link.File.Elf)) |elf_file| {
- const decl = payload.decl;
- const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
- const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
- return MCValue{ .memory = got_addr };
- } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
- const decl = payload.decl;
- const got = &macho_file.sections.items[macho_file.got_section_index.?];
- const got_addr = got.addr + decl.link.macho.offset_table_index.? * ptr_bytes;
- return MCValue{ .memory = got_addr };
- } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
- const decl = payload.decl;
- const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
- return MCValue{ .memory = got_addr };
- } else {
- return self.fail(src, "TODO codegen non-ELF const Decl pointer", .{});
- }
- }
- return self.fail(src, "TODO codegen more kinds of const pointers", .{});
- },
- .Int => {
- const info = typed_value.ty.intInfo(self.target.*);
- if (info.bits > ptr_bits or info.signed) {
- return self.fail(src, "TODO const int bigger than ptr and signed int", .{});
- }
- return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
- },
- .Bool => {
- return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
- },
- .ComptimeInt => unreachable, // semantic analysis prevents this
- .ComptimeFloat => unreachable, // semantic analysis prevents this
- .Optional => {
- if (typed_value.ty.isPtrLikeOptional()) {
- if (typed_value.val.isNull())
- return MCValue{ .immediate = 0 };
-
- var buf: Type.Payload.PointerSimple = undefined;
- return self.genTypedValue(src, .{
- .ty = typed_value.ty.optionalChild(&buf),
- .val = typed_value.val,
- });
- } else if (typed_value.ty.abiSize(self.target.*) == 1) {
- return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) };
- }
- return self.fail(src, "TODO non pointer optionals", .{});
- },
- else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}),
- }
- }
-
- const CallMCValues = struct {
- args: []MCValue,
- return_value: MCValue,
- stack_byte_count: u32,
- stack_align: u32,
-
- fn deinit(self: *CallMCValues, func: *Self) void {
- func.gpa.free(self.args);
- self.* = undefined;
- }
- };
-
- /// Caller must call `CallMCValues.deinit`.
- fn resolveCallingConventionValues(self: *Self, src: usize, fn_ty: Type) !CallMCValues {
- const cc = fn_ty.fnCallingConvention();
- const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
- defer self.gpa.free(param_types);
- fn_ty.fnParamTypes(param_types);
- var result: CallMCValues = .{
- .args = try self.gpa.alloc(MCValue, param_types.len),
- // These undefined values must be populated before returning from this function.
- .return_value = undefined,
- .stack_byte_count = undefined,
- .stack_align = undefined,
- };
- errdefer self.gpa.free(result.args);
-
- const ret_ty = fn_ty.fnReturnType();
-
- switch (arch) {
- .x86_64 => {
- switch (cc) {
- .Naked => {
- assert(result.args.len == 0);
- result.return_value = .{ .unreach = {} };
- result.stack_byte_count = 0;
- result.stack_align = 1;
- return result;
- },
- .Unspecified, .C => {
- var next_int_reg: usize = 0;
- var next_stack_offset: u32 = 0;
-
- for (param_types) |ty, i| {
- switch (ty.zigTypeTag()) {
- .Bool, .Int => {
- const param_size = @intCast(u32, ty.abiSize(self.target.*));
- if (next_int_reg >= c_abi_int_param_regs.len) {
- result.args[i] = .{ .stack_offset = next_stack_offset };
- next_stack_offset += param_size;
- } else {
- const aliased_reg = registerAlias(
- c_abi_int_param_regs[next_int_reg],
- param_size,
- );
- result.args[i] = .{ .register = aliased_reg };
- next_int_reg += 1;
- }
- },
- else => return self.fail(src, "TODO implement function parameters of type {}", .{@tagName(ty.zigTypeTag())}),
- }
- }
- result.stack_byte_count = next_stack_offset;
- result.stack_align = 16;
- },
- else => return self.fail(src, "TODO implement function parameters for {} on x86_64", .{cc}),
- }
- },
- else => if (param_types.len != 0)
- return self.fail(src, "TODO implement codegen parameters for {}", .{self.target.cpu.arch}),
- }
-
- if (ret_ty.zigTypeTag() == .NoReturn) {
- result.return_value = .{ .unreach = {} };
- } else if (!ret_ty.hasCodeGenBits()) {
- result.return_value = .{ .none = {} };
- } else switch (arch) {
- .x86_64 => switch (cc) {
- .Naked => unreachable,
- .Unspecified, .C => {
- const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
- const aliased_reg = registerAlias(c_abi_int_return_regs[0], ret_ty_size);
- result.return_value = .{ .register = aliased_reg };
- },
- else => return self.fail(src, "TODO implement function return values for {}", .{cc}),
- },
- else => return self.fail(src, "TODO implement codegen return values for {}", .{self.target.cpu.arch}),
- }
- return result;
- }
-
- /// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`.
- fn wantSafety(self: *Self) bool {
- return switch (self.bin_file.options.optimize_mode) {
- .Debug => true,
- .ReleaseSafe => true,
- .ReleaseFast => false,
- .ReleaseSmall => false,
- };
- }
-
- fn fail(self: *Self, src: usize, comptime format: []const u8, args: anytype) InnerError {
- @setCold(true);
- assert(self.err_msg == null);
- self.err_msg = try ErrorMsg.create(self.bin_file.allocator, src, format, args);
- return error.CodegenFail;
- }
-
- usingnamespace switch (arch) {
- .i386 => @import("codegen/x86.zig"),
- .x86_64 => @import("codegen/x86_64.zig"),
- .riscv64 => @import("codegen/riscv64.zig"),
- .spu_2 => @import("codegen/spu-mk2.zig"),
- .arm => @import("codegen/arm.zig"),
- .armeb => @import("codegen/arm.zig"),
- else => struct {
- pub const Register = enum {
- dummy,
-
- pub fn allocIndex(self: Register) ?u4 {
- return null;
- }
- };
- pub const callee_preserved_regs = [_]Register{};
- },
- };
-
- /// An integer whose bits represent all the registers and whether they are free.
- const FreeRegInt = @Type(.{ .Int = .{ .is_signed = false, .bits = callee_preserved_regs.len } });
-
- fn parseRegName(name: []const u8) ?Register {
- if (@hasDecl(Register, "parseRegName")) {
- return Register.parseRegName(name);
- }
- return std.meta.stringToEnum(Register, name);
- }
-
- fn registerAlias(reg: Register, size_bytes: u32) Register {
- switch (arch) {
- // For x86_64 we have to pick a smaller register alias depending on abi size.
- .x86_64 => switch (size_bytes) {
- 1 => return reg.to8(),
- 2 => return reg.to16(),
- 4 => return reg.to32(),
- 8 => return reg.to64(),
- else => unreachable,
- },
- else => return reg,
- }
- }
-
- /// For most architectures this does nothing. For x86_64 it resolves any aliased registers
- /// to the 64-bit wide ones.
- fn toCanonicalReg(reg: Register) Register {
- return switch (arch) {
- .x86_64 => reg.to64(),
- else => reg,
- };
- }
- };
-}
diff --git a/src-self-hosted/codegen/arm.zig b/src-self-hosted/codegen/arm.zig
deleted file mode 100644
index 05178ea7d37afb551ea9baed8f1ba09cdad739fa..0000000000000000000000000000000000000000
--- a/src-self-hosted/codegen/arm.zig
+++ /dev/null
@@ -1,607 +0,0 @@
-const std = @import("std");
-const DW = std.dwarf;
-const testing = std.testing;
-
-/// The condition field specifies the flags neccessary for an
-/// Instruction to be executed
-pub const Condition = enum(u4) {
- /// equal
- eq,
- /// not equal
- ne,
- /// unsigned higher or same
- cs,
- /// unsigned lower
- cc,
- /// negative
- mi,
- /// positive or zero
- pl,
- /// overflow
- vs,
- /// no overflow
- vc,
- /// unsigned higer
- hi,
- /// unsigned lower or same
- ls,
- /// greater or equal
- ge,
- /// less than
- lt,
- /// greater than
- gt,
- /// less than or equal
- le,
- /// always
- al,
-};
-
-/// Represents a register in the ARM instruction set architecture
-pub const Register = enum(u5) {
- r0,
- r1,
- r2,
- r3,
- r4,
- r5,
- r6,
- r7,
- r8,
- r9,
- r10,
- r11,
- r12,
- r13,
- r14,
- r15,
-
- /// Argument / result / scratch register 1
- a1,
- /// Argument / result / scratch register 2
- a2,
- /// Argument / scratch register 3
- a3,
- /// Argument / scratch register 4
- a4,
- /// Variable-register 1
- v1,
- /// Variable-register 2
- v2,
- /// Variable-register 3
- v3,
- /// Variable-register 4
- v4,
- /// Variable-register 5
- v5,
- /// Platform register
- v6,
- /// Variable-register 7
- v7,
- /// Frame pointer or Variable-register 8
- fp,
- /// Intra-Procedure-call scratch register
- ip,
- /// Stack pointer
- sp,
- /// Link register
- lr,
- /// Program counter
- pc,
-
- /// Returns the unique 4-bit ID of this register which is used in
- /// the machine code
- pub fn id(self: Register) u4 {
- return @truncate(u4, @enumToInt(self));
- }
-
- /// Returns the index into `callee_preserved_regs`.
- pub fn allocIndex(self: Register) ?u4 {
- inline for (callee_preserved_regs) |cpreg, i| {
- if (self.id() == cpreg.id()) return i;
- }
- return null;
- }
-
- pub fn dwarfLocOp(self: Register) u8 {
- return @as(u8, self.id()) + DW.OP_reg0;
- }
-};
-
-test "Register.id" {
- testing.expectEqual(@as(u4, 15), Register.r15.id());
- testing.expectEqual(@as(u4, 15), Register.pc.id());
-}
-
-pub const callee_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3, .r4, .r5, .r6, .r7, .r8, .r10 };
-pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 };
-pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 };
-
-/// Represents an instruction in the ARM instruction set architecture
-pub const Instruction = union(enum) {
- DataProcessing: packed struct {
- // Note to self: The order of the fields top-to-bottom is
- // right-to-left in the actual 32-bit int representation
- op2: u12,
- rd: u4,
- rn: u4,
- s: u1,
- opcode: u4,
- i: u1,
- fixed: u2 = 0b00,
- cond: u4,
- },
- SingleDataTransfer: packed struct {
- offset: u12,
- rd: u4,
- rn: u4,
- l: u1,
- w: u1,
- b: u1,
- u: u1,
- p: u1,
- i: u1,
- fixed: u2 = 0b01,
- cond: u4,
- },
- Branch: packed struct {
- offset: u24,
- link: u1,
- fixed: u3 = 0b101,
- cond: u4,
- },
- BranchExchange: packed struct {
- rn: u4,
- fixed_1: u1 = 0b1,
- link: u1,
- fixed_2: u22 = 0b0001_0010_1111_1111_1111_00,
- cond: u4,
- },
- SupervisorCall: packed struct {
- comment: u24,
- fixed: u4 = 0b1111,
- cond: u4,
- },
- Breakpoint: packed struct {
- imm4: u4,
- fixed_1: u4 = 0b0111,
- imm12: u12,
- fixed_2_and_cond: u12 = 0b1110_0001_0010,
- },
-
- /// Represents the possible operations which can be performed by a
- /// DataProcessing instruction
- const Opcode = enum(u4) {
- // Rd := Op1 AND Op2
- @"and",
- // Rd := Op1 EOR Op2
- eor,
- // Rd := Op1 - Op2
- sub,
- // Rd := Op2 - Op1
- rsb,
- // Rd := Op1 + Op2
- add,
- // Rd := Op1 + Op2 + C
- adc,
- // Rd := Op1 - Op2 + C - 1
- sbc,
- // Rd := Op2 - Op1 + C - 1
- rsc,
- // set condition codes on Op1 AND Op2
- tst,
- // set condition codes on Op1 EOR Op2
- teq,
- // set condition codes on Op1 - Op2
- cmp,
- // set condition codes on Op1 + Op2
- cmn,
- // Rd := Op1 OR Op2
- orr,
- // Rd := Op2
- mov,
- // Rd := Op1 AND NOT Op2
- bic,
- // Rd := NOT Op2
- mvn,
- };
-
- /// Represents the second operand to a data processing instruction
- /// which can either be content from a register or an immediate
- /// value
- pub const Operand = union(enum) {
- Register: packed struct {
- rm: u4,
- shift: u8,
- },
- Immediate: packed struct {
- imm: u8,
- rotate: u4,
- },
-
- /// Represents multiple ways a register can be shifted. A
- /// register can be shifted by a specific immediate value or
- /// by the contents of another register
- pub const Shift = union(enum) {
- Immediate: packed struct {
- fixed: u1 = 0b0,
- typ: u2,
- amount: u5,
- },
- Register: packed struct {
- fixed_1: u1 = 0b1,
- typ: u2,
- fixed_2: u1 = 0b0,
- rs: u4,
- },
-
- const Type = enum(u2) {
- LogicalLeft,
- LogicalRight,
- ArithmeticRight,
- RotateRight,
- };
-
- const none = Shift{
- .Immediate = .{
- .amount = 0,
- .typ = 0,
- },
- };
-
- pub fn toU8(self: Shift) u8 {
- return switch (self) {
- .Register => |v| @bitCast(u8, v),
- .Immediate => |v| @bitCast(u8, v),
- };
- }
-
- pub fn reg(rs: Register, typ: Type) Shift {
- return Shift{
- .Register = .{
- .rs = rs.id(),
- .typ = @enumToInt(typ),
- },
- };
- }
-
- pub fn imm(amount: u5, typ: Type) Shift {
- return Shift{
- .Immediate = .{
- .amount = amount,
- .typ = @enumToInt(typ),
- },
- };
- }
- };
-
- pub fn toU12(self: Operand) u12 {
- return switch (self) {
- .Register => |v| @bitCast(u12, v),
- .Immediate => |v| @bitCast(u12, v),
- };
- }
-
- pub fn reg(rm: Register, shift: Shift) Operand {
- return Operand{
- .Register = .{
- .rm = rm.id(),
- .shift = shift.toU8(),
- },
- };
- }
-
- pub fn imm(immediate: u8, rotate: u4) Operand {
- return Operand{
- .Immediate = .{
- .imm = immediate,
- .rotate = rotate,
- },
- };
- }
- };
-
- /// Represents the offset operand of a load or store
- /// instruction. Data can be loaded from memory with either an
- /// immediate offset or an offset that is stored in some register.
- pub const Offset = union(enum) {
- Immediate: u12,
- Register: packed struct {
- rm: u4,
- shift: u8,
- },
-
- pub const none = Offset{
- .Immediate = 0,
- };
-
- pub fn toU12(self: Offset) u12 {
- return switch (self) {
- .Register => |v| @bitCast(u12, v),
- .Immediate => |v| v,
- };
- }
-
- pub fn reg(rm: Register, shift: u8) Offset {
- return Offset{
- .Register = .{
- .rm = rm.id(),
- .shift = shift,
- },
- };
- }
-
- pub fn imm(immediate: u8) Offset {
- return Offset{
- .Immediate = immediate,
- };
- }
- };
-
- pub fn toU32(self: Instruction) u32 {
- return switch (self) {
- .DataProcessing => |v| @bitCast(u32, v),
- .SingleDataTransfer => |v| @bitCast(u32, v),
- .Branch => |v| @bitCast(u32, v),
- .BranchExchange => |v| @bitCast(u32, v),
- .SupervisorCall => |v| @bitCast(u32, v),
- .Breakpoint => |v| @intCast(u32, v.imm4) | (@intCast(u32, v.fixed_1) << 4) | (@intCast(u32, v.imm12) << 8) | (@intCast(u32, v.fixed_2_and_cond) << 20),
- };
- }
-
- // Helper functions for the "real" functions below
-
- fn dataProcessing(
- cond: Condition,
- opcode: Opcode,
- s: u1,
- rd: Register,
- rn: Register,
- op2: Operand,
- ) Instruction {
- return Instruction{
- .DataProcessing = .{
- .cond = @enumToInt(cond),
- .i = if (op2 == .Immediate) 1 else 0,
- .opcode = @enumToInt(opcode),
- .s = s,
- .rn = rn.id(),
- .rd = rd.id(),
- .op2 = op2.toU12(),
- },
- };
- }
-
- fn singleDataTransfer(
- cond: Condition,
- rd: Register,
- rn: Register,
- offset: Offset,
- pre_post: u1,
- up_down: u1,
- byte_word: u1,
- writeback: u1,
- load_store: u1,
- ) Instruction {
- return Instruction{
- .SingleDataTransfer = .{
- .cond = @enumToInt(cond),
- .rn = rn.id(),
- .rd = rd.id(),
- .offset = offset.toU12(),
- .l = load_store,
- .w = writeback,
- .b = byte_word,
- .u = up_down,
- .p = pre_post,
- .i = if (offset == .Immediate) 0 else 1,
- },
- };
- }
-
- fn branch(cond: Condition, offset: i24, link: u1) Instruction {
- return Instruction{
- .Branch = .{
- .cond = @enumToInt(cond),
- .link = link,
- .offset = @bitCast(u24, offset),
- },
- };
- }
-
- fn branchExchange(cond: Condition, rn: Register, link: u1) Instruction {
- return Instruction{
- .BranchExchange = .{
- .cond = @enumToInt(cond),
- .link = link,
- .rn = rn.id(),
- },
- };
- }
-
- fn supervisorCall(cond: Condition, comment: u24) Instruction {
- return Instruction{
- .SupervisorCall = .{
- .cond = @enumToInt(cond),
- .comment = comment,
- },
- };
- }
-
- fn breakpoint(imm: u16) Instruction {
- return Instruction{
- .Breakpoint = .{
- .imm12 = @truncate(u12, imm >> 4),
- .imm4 = @truncate(u4, imm),
- },
- };
- }
-
- // Public functions replicating assembler syntax as closely as
- // possible
-
- // Data processing
-
- pub fn @"and"(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .@"and", s, rd, rn, op2);
- }
-
- pub fn eor(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .eor, s, rd, rn, op2);
- }
-
- pub fn sub(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .sub, s, rd, rn, op2);
- }
-
- pub fn rsb(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .rsb, s, rd, rn, op2);
- }
-
- pub fn add(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .add, s, rd, rn, op2);
- }
-
- pub fn adc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .adc, s, rd, rn, op2);
- }
-
- pub fn sbc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .sbc, s, rd, rn, op2);
- }
-
- pub fn rsc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .rsc, s, rd, rn, op2);
- }
-
- pub fn tst(cond: Condition, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .tst, 1, .r0, rn, op2);
- }
-
- pub fn teq(cond: Condition, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .teq, 1, .r0, rn, op2);
- }
-
- pub fn cmp(cond: Condition, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .cmp, 1, .r0, rn, op2);
- }
-
- pub fn cmn(cond: Condition, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .cmn, 1, .r0, rn, op2);
- }
-
- pub fn orr(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .orr, s, rd, rn, op2);
- }
-
- pub fn mov(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .mov, s, rd, .r0, op2);
- }
-
- pub fn bic(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .bic, s, rd, rn, op2);
- }
-
- pub fn mvn(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .mvn, s, rd, .r0, op2);
- }
-
- // Single data transfer
-
- pub fn ldr(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction {
- return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 1);
- }
-
- pub fn str(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction {
- return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 0);
- }
-
- // Branch
-
- pub fn b(cond: Condition, offset: i24) Instruction {
- return branch(cond, offset, 0);
- }
-
- pub fn bl(cond: Condition, offset: i24) Instruction {
- return branch(cond, offset, 1);
- }
-
- // Branch and exchange
-
- pub fn bx(cond: Condition, rn: Register) Instruction {
- return branchExchange(cond, rn, 0);
- }
-
- pub fn blx(cond: Condition, rn: Register) Instruction {
- return branchExchange(cond, rn, 1);
- }
-
- // Supervisor Call
-
- pub const swi = svc;
-
- pub fn svc(cond: Condition, comment: u24) Instruction {
- return supervisorCall(cond, comment);
- }
-
- // Breakpoint
-
- pub fn bkpt(imm: u16) Instruction {
- return breakpoint(imm);
- }
-};
-
-test "serialize instructions" {
- const Testcase = struct {
- inst: Instruction,
- expected: u32,
- };
-
- const testcases = [_]Testcase{
- .{ // add r0, r0, r0
- .inst = Instruction.add(.al, 0, .r0, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)),
- .expected = 0b1110_00_0_0100_0_0000_0000_00000000_0000,
- },
- .{ // mov r4, r2
- .inst = Instruction.mov(.al, 0, .r4, Instruction.Operand.reg(.r2, Instruction.Operand.Shift.none)),
- .expected = 0b1110_00_0_1101_0_0000_0100_00000000_0010,
- },
- .{ // mov r0, #42
- .inst = Instruction.mov(.al, 0, .r0, Instruction.Operand.imm(42, 0)),
- .expected = 0b1110_00_1_1101_0_0000_0000_0000_00101010,
- },
- .{ // ldr r0, [r2, #42]
- .inst = Instruction.ldr(.al, .r0, .r2, Instruction.Offset.imm(42)),
- .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010,
- },
- .{ // str r0, [r3]
- .inst = Instruction.str(.al, .r0, .r3, Instruction.Offset.none),
- .expected = 0b1110_01_0_1_1_0_0_0_0011_0000_000000000000,
- },
- .{ // b #12
- .inst = Instruction.b(.al, 12),
- .expected = 0b1110_101_0_0000_0000_0000_0000_0000_1100,
- },
- .{ // bl #-4
- .inst = Instruction.bl(.al, -4),
- .expected = 0b1110_101_1_1111_1111_1111_1111_1111_1100,
- },
- .{ // bx lr
- .inst = Instruction.bx(.al, .lr),
- .expected = 0b1110_0001_0010_1111_1111_1111_0001_1110,
- },
- .{ // svc #0
- .inst = Instruction.svc(.al, 0),
- .expected = 0b1110_1111_0000_0000_0000_0000_0000_0000,
- },
- .{ // bkpt #42
- .inst = Instruction.bkpt(42),
- .expected = 0b1110_0001_0010_000000000010_0111_1010,
- },
- };
-
- for (testcases) |case| {
- const actual = case.inst.toU32();
- testing.expectEqual(case.expected, actual);
- }
-}
diff --git a/src-self-hosted/codegen/c.zig b/src-self-hosted/codegen/c.zig
deleted file mode 100644
index 34ddcfbb3b33bf9925cd90234324821fc9d1fdc2..0000000000000000000000000000000000000000
--- a/src-self-hosted/codegen/c.zig
+++ /dev/null
@@ -1,299 +0,0 @@
-const std = @import("std");
-
-const link = @import("../link.zig");
-const Module = @import("../Module.zig");
-
-const Inst = @import("../ir.zig").Inst;
-const Value = @import("../value.zig").Value;
-const Type = @import("../type.zig").Type;
-
-const C = link.File.C;
-const Decl = Module.Decl;
-const mem = std.mem;
-
-/// Maps a name from Zig source to C. Currently, this will always give the same
-/// output for any given input, sometimes resulting in broken identifiers.
-fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 {
- return allocator.dupe(u8, name);
-}
-
-fn renderType(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type) !void {
- switch (T.zigTypeTag()) {
- .NoReturn => {
- try writer.writeAll("zig_noreturn void");
- },
- .Void => try writer.writeAll("void"),
- .Int => {
- if (T.tag() == .u8) {
- ctx.file.need_stdint = true;
- try writer.writeAll("uint8_t");
- } else if (T.tag() == .usize) {
- ctx.file.need_stddef = true;
- try writer.writeAll("size_t");
- } else {
- return ctx.file.fail(ctx.decl.src(), "TODO implement int types", .{});
- }
- },
- else => |e| return ctx.file.fail(ctx.decl.src(), "TODO implement type {}", .{e}),
- }
-}
-
-fn renderValue(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type, val: Value) !void {
- switch (T.zigTypeTag()) {
- .Int => {
- if (T.isSignedInt())
- return writer.print("{}", .{val.toSignedInt()});
- return writer.print("{}", .{val.toUnsignedInt()});
- },
- else => |e| return ctx.file.fail(ctx.decl.src(), "TODO implement value {}", .{e}),
- }
-}
-
-fn renderFunctionSignature(ctx: *Context, writer: std.ArrayList(u8).Writer, decl: *Decl) !void {
- const tv = decl.typed_value.most_recent.typed_value;
- try renderType(ctx, writer, tv.ty.fnReturnType());
- const name = try map(ctx.file.base.allocator, mem.spanZ(decl.name));
- defer ctx.file.base.allocator.free(name);
- try writer.print(" {}(", .{name});
- var param_len = tv.ty.fnParamLen();
- if (param_len == 0)
- try writer.writeAll("void")
- else {
- var index: usize = 0;
- while (index < param_len) : (index += 1) {
- if (index > 0) {
- try writer.writeAll(", ");
- }
- try renderType(ctx, writer, tv.ty.fnParamType(index));
- try writer.print(" arg{}", .{index});
- }
- }
- try writer.writeByte(')');
-}
-
-pub fn generate(file: *C, decl: *Decl) !void {
- switch (decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) {
- .Fn => try genFn(file, decl),
- .Array => try genArray(file, decl),
- else => |e| return file.fail(decl.src(), "TODO {}", .{e}),
- }
-}
-
-fn genArray(file: *C, decl: *Decl) !void {
- const tv = decl.typed_value.most_recent.typed_value;
- // TODO: prevent inline asm constants from being emitted
- const name = try map(file.base.allocator, mem.span(decl.name));
- defer file.base.allocator.free(name);
- if (tv.val.cast(Value.Payload.Bytes)) |payload|
- if (tv.ty.sentinel()) |sentinel|
- if (sentinel.toUnsignedInt() == 0)
- try file.constants.writer().print("const char *const {} = \"{}\";\n", .{ name, payload.data })
- else
- return file.fail(decl.src(), "TODO byte arrays with non-zero sentinels", .{})
- else
- return file.fail(decl.src(), "TODO byte arrays without sentinels", .{})
- else
- return file.fail(decl.src(), "TODO non-byte arrays", .{});
-}
-
-const Context = struct {
- file: *C,
- decl: *Decl,
- inst_map: std.AutoHashMap(*Inst, []u8),
- argdex: usize = 0,
- unnamed_index: usize = 0,
-
- fn name(self: *Context) ![]u8 {
- const val = try std.fmt.allocPrint(self.file.base.allocator, "__temp_{}", .{self.unnamed_index});
- self.unnamed_index += 1;
- return val;
- }
-
- fn deinit(self: *Context) void {
- var it = self.inst_map.iterator();
- while (it.next()) |kv| {
- self.file.base.allocator.free(kv.value);
- }
- self.inst_map.deinit();
- self.* = undefined;
- }
-};
-
-fn genFn(file: *C, decl: *Decl) !void {
- const writer = file.main.writer();
- const tv = decl.typed_value.most_recent.typed_value;
-
- var ctx = Context{
- .file = file,
- .decl = decl,
- .inst_map = std.AutoHashMap(*Inst, []u8).init(file.base.allocator),
- };
- defer ctx.deinit();
-
- try renderFunctionSignature(&ctx, writer, decl);
-
- try writer.writeAll(" {");
-
- const func: *Module.Fn = tv.val.cast(Value.Payload.Function).?.func;
- const instructions = func.analysis.success.instructions;
- if (instructions.len > 0) {
- try writer.writeAll("\n");
- for (instructions) |inst| {
- if (switch (inst.tag) {
- .assembly => try genAsm(&ctx, inst.castTag(.assembly).?),
- .call => try genCall(&ctx, inst.castTag(.call).?),
- .ret => try genRet(&ctx, inst.castTag(.ret).?),
- .retvoid => try genRetVoid(&ctx),
- .arg => try genArg(&ctx),
- .dbg_stmt => try genDbgStmt(&ctx, inst.castTag(.dbg_stmt).?),
- .breakpoint => try genBreak(&ctx, inst.castTag(.breakpoint).?),
- .unreach => try genUnreach(&ctx, inst.castTag(.unreach).?),
- .intcast => try genIntCast(&ctx, inst.castTag(.intcast).?),
- else => |e| return file.fail(decl.src(), "TODO implement C codegen for {}", .{e}),
- }) |name| {
- try ctx.inst_map.putNoClobber(inst, name);
- }
- }
- }
-
- try writer.writeAll("}\n\n");
-}
-
-fn genArg(ctx: *Context) !?[]u8 {
- const name = try std.fmt.allocPrint(ctx.file.base.allocator, "arg{}", .{ctx.argdex});
- ctx.argdex += 1;
- return name;
-}
-
-fn genRetVoid(ctx: *Context) !?[]u8 {
- try ctx.file.main.writer().print(" return;\n", .{});
- return null;
-}
-
-fn genRet(ctx: *Context, inst: *Inst.UnOp) !?[]u8 {
- return ctx.file.fail(ctx.decl.src(), "TODO return", .{});
-}
-
-fn genIntCast(ctx: *Context, inst: *Inst.UnOp) !?[]u8 {
- if (inst.base.isUnused())
- return null;
- const op = inst.operand;
- const writer = ctx.file.main.writer();
- const name = try ctx.name();
- const from = ctx.inst_map.get(op) orelse
- return ctx.file.fail(ctx.decl.src(), "Internal error in C backend: intCast argument not found in inst_map", .{});
- try writer.writeAll(" const ");
- try renderType(ctx, writer, inst.base.ty);
- try writer.print(" {} = (", .{name});
- try renderType(ctx, writer, inst.base.ty);
- try writer.print("){};\n", .{from});
- return name;
-}
-
-fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 {
- const writer = ctx.file.main.writer();
- const header = ctx.file.header.writer();
- try writer.writeAll(" ");
- if (inst.func.castTag(.constant)) |func_inst| {
- if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
- const target = func_val.func.owner_decl;
- const target_ty = target.typed_value.most_recent.typed_value.ty;
- const ret_ty = target_ty.fnReturnType().tag();
- if (target_ty.fnReturnType().hasCodeGenBits() and inst.base.isUnused()) {
- try writer.print("(void)", .{});
- }
- const tname = mem.spanZ(target.name);
- if (ctx.file.called.get(tname) == null) {
- try ctx.file.called.put(tname, void{});
- try renderFunctionSignature(ctx, header, target);
- try header.writeAll(";\n");
- }
- try writer.print("{}(", .{tname});
- if (inst.args.len != 0) {
- for (inst.args) |arg, i| {
- if (i > 0) {
- try writer.writeAll(", ");
- }
- if (arg.cast(Inst.Constant)) |con| {
- try renderValue(ctx, writer, arg.ty, con.val);
- } else {
- return ctx.file.fail(ctx.decl.src(), "TODO call pass arg {}", .{arg});
- }
- }
- }
- try writer.writeAll(");\n");
- } else {
- return ctx.file.fail(ctx.decl.src(), "TODO non-function call target?", .{});
- }
- } else {
- return ctx.file.fail(ctx.decl.src(), "TODO non-constant call inst?", .{});
- }
- return null;
-}
-
-fn genDbgStmt(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {
- // TODO emit #line directive here with line number and filename
- return null;
-}
-
-fn genBreak(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {
- // TODO ??
- return null;
-}
-
-fn genUnreach(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {
- try ctx.file.main.writer().writeAll(" zig_unreachable();\n");
- return null;
-}
-
-fn genAsm(ctx: *Context, as: *Inst.Assembly) !?[]u8 {
- const writer = ctx.file.main.writer();
- try writer.writeAll(" ");
- for (as.inputs) |i, index| {
- if (i[0] == '{' and i[i.len - 1] == '}') {
- const reg = i[1 .. i.len - 1];
- const arg = as.args[index];
- try writer.writeAll("register ");
- try renderType(ctx, writer, arg.ty);
- try writer.print(" {}_constant __asm__(\"{}\") = ", .{ reg, reg });
- // TODO merge constant handling into inst_map as well
- if (arg.castTag(.constant)) |c| {
- try renderValue(ctx, writer, arg.ty, c.val);
- try writer.writeAll(";\n ");
- } else {
- const gop = try ctx.inst_map.getOrPut(arg);
- if (!gop.found_existing) {
- return ctx.file.fail(ctx.decl.src(), "Internal error in C backend: asm argument not found in inst_map", .{});
- }
- try writer.print("{};\n ", .{gop.entry.value});
- }
- } else {
- return ctx.file.fail(ctx.decl.src(), "TODO non-explicit inline asm regs", .{});
- }
- }
- try writer.print("__asm {} (\"{}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source });
- if (as.output) |o| {
- return ctx.file.fail(ctx.decl.src(), "TODO inline asm output", .{});
- }
- if (as.inputs.len > 0) {
- if (as.output == null) {
- try writer.writeAll(" :");
- }
- try writer.writeAll(": ");
- for (as.inputs) |i, index| {
- if (i[0] == '{' and i[i.len - 1] == '}') {
- const reg = i[1 .. i.len - 1];
- const arg = as.args[index];
- if (index > 0) {
- try writer.writeAll(", ");
- }
- try writer.print("\"\"({}_constant)", .{reg});
- } else {
- // This is blocked by the earlier test
- unreachable;
- }
- }
- }
- try writer.writeAll(");\n");
- return null;
-}
diff --git a/src-self-hosted/codegen/llvm.zig b/src-self-hosted/codegen/llvm.zig
deleted file mode 100644
index 01fa0baf0293f829fefb6774cf9fc4e0dafefe5c..0000000000000000000000000000000000000000
--- a/src-self-hosted/codegen/llvm.zig
+++ /dev/null
@@ -1,125 +0,0 @@
-const std = @import("std");
-const Allocator = std.mem.Allocator;
-
-pub fn targetTriple(allocator: *Allocator, target: std.Target) ![]u8 {
- const llvm_arch = switch (target.cpu.arch) {
- .arm => "arm",
- .armeb => "armeb",
- .aarch64 => "aarch64",
- .aarch64_be => "aarch64_be",
- .aarch64_32 => "aarch64_32",
- .arc => "arc",
- .avr => "avr",
- .bpfel => "bpfel",
- .bpfeb => "bpfeb",
- .hexagon => "hexagon",
- .mips => "mips",
- .mipsel => "mipsel",
- .mips64 => "mips64",
- .mips64el => "mips64el",
- .msp430 => "msp430",
- .powerpc => "powerpc",
- .powerpc64 => "powerpc64",
- .powerpc64le => "powerpc64le",
- .r600 => "r600",
- .amdgcn => "amdgcn",
- .riscv32 => "riscv32",
- .riscv64 => "riscv64",
- .sparc => "sparc",
- .sparcv9 => "sparcv9",
- .sparcel => "sparcel",
- .s390x => "s390x",
- .tce => "tce",
- .tcele => "tcele",
- .thumb => "thumb",
- .thumbeb => "thumbeb",
- .i386 => "i386",
- .x86_64 => "x86_64",
- .xcore => "xcore",
- .nvptx => "nvptx",
- .nvptx64 => "nvptx64",
- .le32 => "le32",
- .le64 => "le64",
- .amdil => "amdil",
- .amdil64 => "amdil64",
- .hsail => "hsail",
- .hsail64 => "hsail64",
- .spir => "spir",
- .spir64 => "spir64",
- .kalimba => "kalimba",
- .shave => "shave",
- .lanai => "lanai",
- .wasm32 => "wasm32",
- .wasm64 => "wasm64",
- .renderscript32 => "renderscript32",
- .renderscript64 => "renderscript64",
- .ve => "ve",
- .spu_2 => return error.LLVMBackendDoesNotSupportSPUMarkII,
- };
- // TODO Add a sub-arch for some architectures depending on CPU features.
-
- const llvm_os = switch (target.os.tag) {
- .freestanding => "unknown",
- .ananas => "ananas",
- .cloudabi => "cloudabi",
- .dragonfly => "dragonfly",
- .freebsd => "freebsd",
- .fuchsia => "fuchsia",
- .ios => "ios",
- .kfreebsd => "kfreebsd",
- .linux => "linux",
- .lv2 => "lv2",
- .macosx => "macosx",
- .netbsd => "netbsd",
- .openbsd => "openbsd",
- .solaris => "solaris",
- .windows => "windows",
- .haiku => "haiku",
- .minix => "minix",
- .rtems => "rtems",
- .nacl => "nacl",
- .cnk => "cnk",
- .aix => "aix",
- .cuda => "cuda",
- .nvcl => "nvcl",
- .amdhsa => "amdhsa",
- .ps4 => "ps4",
- .elfiamcu => "elfiamcu",
- .tvos => "tvos",
- .watchos => "watchos",
- .mesa3d => "mesa3d",
- .contiki => "contiki",
- .amdpal => "amdpal",
- .hermit => "hermit",
- .hurd => "hurd",
- .wasi => "wasi",
- .emscripten => "emscripten",
- .uefi => "windows",
- .other => "unknown",
- };
-
- const llvm_abi = switch (target.abi) {
- .none => "unknown",
- .gnu => "gnu",
- .gnuabin32 => "gnuabin32",
- .gnuabi64 => "gnuabi64",
- .gnueabi => "gnueabi",
- .gnueabihf => "gnueabihf",
- .gnux32 => "gnux32",
- .code16 => "code16",
- .eabi => "eabi",
- .eabihf => "eabihf",
- .android => "android",
- .musl => "musl",
- .musleabi => "musleabi",
- .musleabihf => "musleabihf",
- .msvc => "msvc",
- .itanium => "itanium",
- .cygnus => "cygnus",
- .coreclr => "coreclr",
- .simulator => "simulator",
- .macabi => "macabi",
- };
-
- return std.fmt.allocPrint(allocator, "{}-unknown-{}-{}", .{ llvm_arch, llvm_os, llvm_abi });
-}
diff --git a/src-self-hosted/codegen/riscv64.zig b/src-self-hosted/codegen/riscv64.zig
deleted file mode 100644
index 96b9c58f9c3b041263e44cd215ae5a71df63aa37..0000000000000000000000000000000000000000
--- a/src-self-hosted/codegen/riscv64.zig
+++ /dev/null
@@ -1,433 +0,0 @@
-const std = @import("std");
-const DW = std.dwarf;
-
-// TODO: this is only tagged to facilitate the monstrosity.
-// Once packed structs work make it packed.
-pub const Instruction = union(enum) {
- R: packed struct {
- opcode: u7,
- rd: u5,
- funct3: u3,
- rs1: u5,
- rs2: u5,
- funct7: u7,
- },
- I: packed struct {
- opcode: u7,
- rd: u5,
- funct3: u3,
- rs1: u5,
- imm0_11: u12,
- },
- S: packed struct {
- opcode: u7,
- imm0_4: u5,
- funct3: u3,
- rs1: u5,
- rs2: u5,
- imm5_11: u7,
- },
- B: packed struct {
- opcode: u7,
- imm11: u1,
- imm1_4: u4,
- funct3: u3,
- rs1: u5,
- rs2: u5,
- imm5_10: u6,
- imm12: u1,
- },
- U: packed struct {
- opcode: u7,
- rd: u5,
- imm12_31: u20,
- },
- J: packed struct {
- opcode: u7,
- rd: u5,
- imm12_19: u8,
- imm11: u1,
- imm1_10: u10,
- imm20: u1,
- },
-
- // TODO: once packed structs work we can remove this monstrosity.
- pub fn toU32(self: Instruction) u32 {
- return switch (self) {
- .R => |v| @bitCast(u32, v),
- .I => |v| @bitCast(u32, v),
- .S => |v| @bitCast(u32, v),
- .B => |v| @intCast(u32, v.opcode) + (@intCast(u32, v.imm11) << 7) + (@intCast(u32, v.imm1_4) << 8) + (@intCast(u32, v.funct3) << 12) + (@intCast(u32, v.rs1) << 15) + (@intCast(u32, v.rs2) << 20) + (@intCast(u32, v.imm5_10) << 25) + (@intCast(u32, v.imm12) << 31),
- .U => |v| @bitCast(u32, v),
- .J => |v| @bitCast(u32, v),
- };
- }
-
- fn rType(op: u7, fn3: u3, fn7: u7, rd: Register, r1: Register, r2: Register) Instruction {
- return Instruction{
- .R = .{
- .opcode = op,
- .funct3 = fn3,
- .funct7 = fn7,
- .rd = @enumToInt(rd),
- .rs1 = @enumToInt(r1),
- .rs2 = @enumToInt(r2),
- },
- };
- }
-
- // RISC-V is all signed all the time -- convert immediates to unsigned for processing
- fn iType(op: u7, fn3: u3, rd: Register, r1: Register, imm: i12) Instruction {
- const umm = @bitCast(u12, imm);
-
- return Instruction{
- .I = .{
- .opcode = op,
- .funct3 = fn3,
- .rd = @enumToInt(rd),
- .rs1 = @enumToInt(r1),
- .imm0_11 = umm,
- },
- };
- }
-
- fn sType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i12) Instruction {
- const umm = @bitCast(u12, imm);
-
- return Instruction{
- .S = .{
- .opcode = op,
- .funct3 = fn3,
- .rs1 = @enumToInt(r1),
- .rs2 = @enumToInt(r2),
- .imm0_4 = @truncate(u5, umm),
- .imm5_11 = @truncate(u7, umm >> 5),
- },
- };
- }
-
- // Use significance value rather than bit value, same for J-type
- // -- less burden on callsite, bonus semantic checking
- fn bType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i13) Instruction {
- const umm = @bitCast(u13, imm);
- if (umm % 2 != 0) @panic("Internal error: misaligned branch target");
-
- return Instruction{
- .B = .{
- .opcode = op,
- .funct3 = fn3,
- .rs1 = @enumToInt(r1),
- .rs2 = @enumToInt(r2),
- .imm1_4 = @truncate(u4, umm >> 1),
- .imm5_10 = @truncate(u6, umm >> 5),
- .imm11 = @truncate(u1, umm >> 11),
- .imm12 = @truncate(u1, umm >> 12),
- },
- };
- }
-
- // We have to extract the 20 bits anyway -- let's not make it more painful
- fn uType(op: u7, rd: Register, imm: i20) Instruction {
- const umm = @bitCast(u20, imm);
-
- return Instruction{
- .U = .{
- .opcode = op,
- .rd = @enumToInt(rd),
- .imm12_31 = umm,
- },
- };
- }
-
- fn jType(op: u7, rd: Register, imm: i21) Instruction {
- const umm = @bitcast(u21, imm);
- if (umm % 2 != 0) @panic("Internal error: misaligned jump target");
-
- return Instruction{
- .J = .{
- .opcode = op,
- .rd = @enumToInt(rd),
- .imm1_10 = @truncate(u10, umm >> 1),
- .imm11 = @truncate(u1, umm >> 1),
- .imm12_19 = @truncate(u8, umm >> 12),
- .imm20 = @truncate(u1, umm >> 20),
- },
- };
- }
-
- // The meat and potatoes. Arguments are in the order in which they would appear in assembly code.
-
- // Arithmetic/Logical, Register-Register
-
- pub fn add(rd: Register, r1: Register, r2: Register) Instruction {
- return rType(0b0110011, 0b000, 0b0000000, rd, r1, r2);
- }
-
- pub fn sub(rd: Register, r1: Register, r2: Register) Instruction {
- return rType(0b0110011, 0b000, 0b0100000, rd, r1, r2);
- }
-
- pub fn @"and"(rd: Register, r1: Register, r2: Register) Instruction {
- return rType(0b0110011, 0b111, 0b0000000, rd, r1, r2);
- }
-
- pub fn @"or"(rd: Register, r1: Register, r2: Register) Instruction {
- return rType(0b0110011, 0b110, 0b0000000, rd, r1, r2);
- }
-
- pub fn xor(rd: Register, r1: Register, r2: Register) Instruction {
- return rType(0b0110011, 0b100, 0b0000000, rd, r1, r2);
- }
-
- pub fn sll(rd: Register, r1: Register, r2: Register) Instruction {
- return rType(0b0110011, 0b001, 0b0000000, rd, r1, r2);
- }
-
- pub fn srl(rd: Register, r1: Register, r2: Register) Instruction {
- return rType(0b0110011, 0b101, 0b0000000, rd, r1, r2);
- }
-
- pub fn sra(rd: Register, r1: Register, r2: Register) Instruction {
- return rType(0b0110011, 0b101, 0b0100000, rd, r1, r2);
- }
-
- pub fn slt(rd: Register, r1: Register, r2: Register) Instruction {
- return rType(0b0110011, 0b010, 0b0000000, rd, r1, r2);
- }
-
- pub fn sltu(rd: Register, r1: Register, r2: Register) Instruction {
- return rType(0b0110011, 0b011, 0b0000000, rd, r1, r2);
- }
-
- // Arithmetic/Logical, Register-Register (32-bit)
-
- pub fn addw(rd: Register, r1: Register, r2: Register) Instruction {
- return rType(0b0111011, 0b000, rd, r1, r2);
- }
-
- pub fn subw(rd: Register, r1: Register, r2: Register) Instruction {
- return rType(0b0111011, 0b000, 0b0100000, rd, r1, r2);
- }
-
- pub fn sllw(rd: Register, r1: Register, r2: Register) Instruction {
- return rType(0b0111011, 0b001, 0b0000000, rd, r1, r2);
- }
-
- pub fn srlw(rd: Register, r1: Register, r2: Register) Instruction {
- return rType(0b0111011, 0b101, 0b0000000, rd, r1, r2);
- }
-
- pub fn sraw(rd: Register, r1: Register, r2: Register) Instruction {
- return rType(0b0111011, 0b101, 0b0100000, rd, r1, r2);
- }
-
- // Arithmetic/Logical, Register-Immediate
-
- pub fn addi(rd: Register, r1: Register, imm: i12) Instruction {
- return iType(0b0010011, 0b000, rd, r1, imm);
- }
-
- pub fn andi(rd: Register, r1: Register, imm: i12) Instruction {
- return iType(0b0010011, 0b111, rd, r1, imm);
- }
-
- pub fn ori(rd: Register, r1: Register, imm: i12) Instruction {
- return iType(0b0010011, 0b110, rd, r1, imm);
- }
-
- pub fn xori(rd: Register, r1: Register, imm: i12) Instruction {
- return iType(0b0010011, 0b100, rd, r1, imm);
- }
-
- pub fn slli(rd: Register, r1: Register, shamt: u6) Instruction {
- return iType(0b0010011, 0b001, rd, r1, shamt);
- }
-
- pub fn srli(rd: Register, r1: Register, shamt: u6) Instruction {
- return iType(0b0010011, 0b101, rd, r1, shamt);
- }
-
- pub fn srai(rd: Register, r1: Register, shamt: u6) Instruction {
- return iType(0b0010011, 0b101, rd, r1, (1 << 10) + shamt);
- }
-
- pub fn slti(rd: Register, r1: Register, imm: i12) Instruction {
- return iType(0b0010011, 0b010, rd, r1, imm);
- }
-
- pub fn sltiu(rd: Register, r1: Register, imm: u12) Instruction {
- return iType(0b0010011, 0b011, rd, r1, @bitCast(i12, imm));
- }
-
- // Arithmetic/Logical, Register-Immediate (32-bit)
-
- pub fn addiw(rd: Register, r1: Register, imm: i12) Instruction {
- return iType(0b0011011, 0b000, rd, r1, imm);
- }
-
- pub fn slliw(rd: Register, r1: Register, shamt: u5) Instruction {
- return iType(0b0011011, 0b001, rd, r1, shamt);
- }
-
- pub fn srliw(rd: Register, r1: Register, shamt: u5) Instruction {
- return iType(0b0011011, 0b101, rd, r1, shamt);
- }
-
- pub fn sraiw(rd: Register, r1: Register, shamt: u5) Instruction {
- return iType(0b0011011, 0b101, rd, r1, (1 << 10) + shamt);
- }
-
- // Upper Immediate
-
- pub fn lui(rd: Register, imm: i20) Instruction {
- return uType(0b0110111, rd, imm);
- }
-
- pub fn auipc(rd: Register, imm: i20) Instruction {
- return uType(0b0010111, rd, imm);
- }
-
- // Load
-
- pub fn ld(rd: Register, offset: i12, base: Register) Instruction {
- return iType(0b0000011, 0b011, rd, base, offset);
- }
-
- pub fn lw(rd: Register, offset: i12, base: Register) Instruction {
- return iType(0b0000011, 0b010, rd, base, offset);
- }
-
- pub fn lwu(rd: Register, offset: i12, base: Register) Instruction {
- return iType(0b0000011, 0b110, rd, base, offset);
- }
-
- pub fn lh(rd: Register, offset: i12, base: Register) Instruction {
- return iType(0b0000011, 0b001, rd, base, offset);
- }
-
- pub fn lhu(rd: Register, offset: i12, base: Register) Instruction {
- return iType(0b0000011, 0b101, rd, base, offset);
- }
-
- pub fn lb(rd: Register, offset: i12, base: Register) Instruction {
- return iType(0b0000011, 0b000, rd, base, offset);
- }
-
- pub fn lbu(rd: Register, offset: i12, base: Register) Instruction {
- return iType(0b0000011, 0b100, rd, base, offset);
- }
-
- // Store
-
- pub fn sd(rs: Register, offset: i12, base: Register) Instruction {
- return sType(0b0100011, 0b011, base, rs, offset);
- }
-
- pub fn sw(rs: Register, offset: i12, base: Register) Instruction {
- return sType(0b0100011, 0b010, base, rs, offset);
- }
-
- pub fn sh(rs: Register, offset: i12, base: Register) Instruction {
- return sType(0b0100011, 0b001, base, rs, offset);
- }
-
- pub fn sb(rs: Register, offset: i12, base: Register) Instruction {
- return sType(0b0100011, 0b000, base, rs, offset);
- }
-
- // Fence
- // TODO: implement fence
-
- // Branch
-
- pub fn beq(r1: Register, r2: Register, offset: u13) Instruction {
- return bType(0b1100011, 0b000, r1, r2, offset);
- }
-
- pub fn bne(r1: Register, r2: Register, offset: u13) Instruction {
- return bType(0b1100011, 0b001, r1, r2, offset);
- }
-
- pub fn blt(r1: Register, r2: Register, offset: u13) Instruction {
- return bType(0b1100011, 0b100, r1, r2, offset);
- }
-
- pub fn bge(r1: Register, r2: Register, offset: u13) Instruction {
- return bType(0b1100011, 0b101, r1, r2, offset);
- }
-
- pub fn bltu(r1: Register, r2: Register, offset: u13) Instruction {
- return bType(0b1100011, 0b110, r1, r2, offset);
- }
-
- pub fn bgeu(r1: Register, r2: Register, offset: u13) Instruction {
- return bType(0b1100011, 0b111, r1, r2, offset);
- }
-
- // Jump
-
- pub fn jal(link: Register, offset: i21) Instruction {
- return jType(0b1101111, link, offset);
- }
-
- pub fn jalr(link: Register, offset: i12, base: Register) Instruction {
- return iType(0b1100111, 0b000, link, base, offset);
- }
-
- // System
-
- pub const ecall = iType(0b1110011, 0b000, .zero, .zero, 0x000);
- pub const ebreak = iType(0b1110011, 0b000, .zero, .zero, 0x001);
-};
-
-// zig fmt: off
-pub const RawRegister = enum(u5) {
- x0, x1, x2, x3, x4, x5, x6, x7,
- x8, x9, x10, x11, x12, x13, x14, x15,
- x16, x17, x18, x19, x20, x21, x22, x23,
- x24, x25, x26, x27, x28, x29, x30, x31,
-
- pub fn dwarfLocOp(reg: RawRegister) u8 {
- return @enumToInt(reg) + DW.OP_reg0;
- }
-};
-
-pub const Register = enum(u5) {
- // 64 bit registers
- zero, // zero
- ra, // return address. caller saved
- sp, // stack pointer. callee saved.
- gp, // global pointer
- tp, // thread pointer
- t0, t1, t2, // temporaries. caller saved.
- s0, // s0/fp, callee saved.
- s1, // callee saved.
- a0, a1, // fn args/return values. caller saved.
- a2, a3, a4, a5, a6, a7, // fn args. caller saved.
- s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, // saved registers. callee saved.
- t3, t4, t5, t6, // caller saved
-
- pub fn parseRegName(name: []const u8) ?Register {
- if(std.meta.stringToEnum(Register, name)) |reg| return reg;
- if(std.meta.stringToEnum(RawRegister, name)) |rawreg| return @intToEnum(Register, @enumToInt(rawreg));
- return null;
- }
-
- /// Returns the index into `callee_preserved_regs`.
- pub fn allocIndex(self: Register) ?u4 {
- inline for(callee_preserved_regs) |cpreg, i| {
- if(self == cpreg) return i;
- }
- return null;
- }
-
- pub fn dwarfLocOp(reg: Register) u8 {
- return @as(u8, @enumToInt(reg)) + DW.OP_reg0;
- }
-};
-
-// zig fmt: on
-
-pub const callee_preserved_regs = [_]Register{
- .s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11,
-};
diff --git a/src-self-hosted/codegen/spu-mk2.zig b/src-self-hosted/codegen/spu-mk2.zig
deleted file mode 100644
index 542862cacaa444033616d002c8fcfb2133fea1a0..0000000000000000000000000000000000000000
--- a/src-self-hosted/codegen/spu-mk2.zig
+++ /dev/null
@@ -1,170 +0,0 @@
-const std = @import("std");
-
-pub const Interpreter = @import("spu-mk2/interpreter.zig").Interpreter;
-
-pub const ExecutionCondition = enum(u3) {
- always = 0,
- when_zero = 1,
- not_zero = 2,
- greater_zero = 3,
- less_than_zero = 4,
- greater_or_equal_zero = 5,
- less_or_equal_zero = 6,
- overflow = 7,
-};
-
-pub const InputBehaviour = enum(u2) {
- zero = 0,
- immediate = 1,
- peek = 2,
- pop = 3,
-};
-
-pub const OutputBehaviour = enum(u2) {
- discard = 0,
- push = 1,
- jump = 2,
- jump_relative = 3,
-};
-
-pub const Command = enum(u5) {
- copy = 0,
- ipget = 1,
- get = 2,
- set = 3,
- store8 = 4,
- store16 = 5,
- load8 = 6,
- load16 = 7,
- undefined0 = 8,
- undefined1 = 9,
- frget = 10,
- frset = 11,
- bpget = 12,
- bpset = 13,
- spget = 14,
- spset = 15,
- add = 16,
- sub = 17,
- mul = 18,
- div = 19,
- mod = 20,
- @"and" = 21,
- @"or" = 22,
- xor = 23,
- not = 24,
- signext = 25,
- rol = 26,
- ror = 27,
- bswap = 28,
- asr = 29,
- lsl = 30,
- lsr = 31,
-};
-
-pub const Instruction = packed struct {
- condition: ExecutionCondition,
- input0: InputBehaviour,
- input1: InputBehaviour,
- modify_flags: bool,
- output: OutputBehaviour,
- command: Command,
- reserved: u1 = 0,
-
- pub fn format(instr: Instruction, comptime fmt: []const u8, options: std.fmt.FormatOptions, out: anytype) !void {
- try std.fmt.format(out, "0x{x:0<4} ", .{@bitCast(u16, instr)});
- try out.writeAll(switch (instr.condition) {
- .always => " ",
- .when_zero => "== 0",
- .not_zero => "!= 0",
- .greater_zero => " > 0",
- .less_than_zero => " < 0",
- .greater_or_equal_zero => ">= 0",
- .less_or_equal_zero => "<= 0",
- .overflow => "ovfl",
- });
- try out.writeAll(" ");
- try out.writeAll(switch (instr.input0) {
- .zero => "zero",
- .immediate => "imm ",
- .peek => "peek",
- .pop => "pop ",
- });
- try out.writeAll(" ");
- try out.writeAll(switch (instr.input1) {
- .zero => "zero",
- .immediate => "imm ",
- .peek => "peek",
- .pop => "pop ",
- });
- try out.writeAll(" ");
- try out.writeAll(switch (instr.command) {
- .copy => "copy ",
- .ipget => "ipget ",
- .get => "get ",
- .set => "set ",
- .store8 => "store8 ",
- .store16 => "store16 ",
- .load8 => "load8 ",
- .load16 => "load16 ",
- .undefined0 => "undefined",
- .undefined1 => "undefined",
- .frget => "frget ",
- .frset => "frset ",
- .bpget => "bpget ",
- .bpset => "bpset ",
- .spget => "spget ",
- .spset => "spset ",
- .add => "add ",
- .sub => "sub ",
- .mul => "mul ",
- .div => "div ",
- .mod => "mod ",
- .@"and" => "and ",
- .@"or" => "or ",
- .xor => "xor ",
- .not => "not ",
- .signext => "signext ",
- .rol => "rol ",
- .ror => "ror ",
- .bswap => "bswap ",
- .asr => "asr ",
- .lsl => "lsl ",
- .lsr => "lsr ",
- });
- try out.writeAll(" ");
- try out.writeAll(switch (instr.output) {
- .discard => "discard",
- .push => "push ",
- .jump => "jmp ",
- .jump_relative => "rjmp ",
- });
- try out.writeAll(" ");
- try out.writeAll(if (instr.modify_flags)
- "+ flags"
- else
- " ");
- }
-};
-
-pub const FlagRegister = packed struct {
- zero: bool,
- negative: bool,
- carry: bool,
- carry_enabled: bool,
- interrupt0_enabled: bool,
- interrupt1_enabled: bool,
- interrupt2_enabled: bool,
- interrupt3_enabled: bool,
- reserved: u8 = 0,
-};
-
-pub const Register = enum {
- dummy,
-
- pub fn allocIndex(self: Register) ?u4 {
- return null;
- }
-};
-
-pub const callee_preserved_regs = [_]Register{};
diff --git a/src-self-hosted/codegen/spu-mk2/interpreter.zig b/src-self-hosted/codegen/spu-mk2/interpreter.zig
deleted file mode 100644
index 1ec99546c6cbe1ee30d1473e455824d2f6dc9421..0000000000000000000000000000000000000000
--- a/src-self-hosted/codegen/spu-mk2/interpreter.zig
+++ /dev/null
@@ -1,166 +0,0 @@
-const std = @import("std");
-const log = std.log.scoped(.SPU_2_Interpreter);
-const spu = @import("../spu-mk2.zig");
-const FlagRegister = spu.FlagRegister;
-const Instruction = spu.Instruction;
-const ExecutionCondition = spu.ExecutionCondition;
-
-pub fn Interpreter(comptime Bus: type) type {
- return struct {
- ip: u16 = 0,
- sp: u16 = undefined,
- bp: u16 = undefined,
- fr: FlagRegister = @bitCast(FlagRegister, @as(u16, 0)),
- /// This is set to true when we hit an undefined0 instruction, allowing it to
- /// be used as a trap for testing purposes
- undefined0: bool = false,
- /// This is set to true when we hit an undefined1 instruction, allowing it to
- /// be used as a trap for testing purposes. undefined1 is used as a breakpoint.
- undefined1: bool = false,
- bus: Bus,
-
- pub fn ExecuteBlock(self: *@This(), comptime size: ?u32) !void {
- var count: usize = 0;
- while (size == null or count < size.?) {
- count += 1;
- var instruction = @bitCast(Instruction, self.bus.read16(self.ip));
-
- log.debug("Executing {}\n", .{instruction});
-
- self.ip +%= 2;
-
- const execute = switch (instruction.condition) {
- .always => true,
- .not_zero => !self.fr.zero,
- .when_zero => self.fr.zero,
- .overflow => self.fr.carry,
- ExecutionCondition.greater_or_equal_zero => !self.fr.negative,
- else => return error.Unimplemented,
- };
-
- if (execute) {
- const val0 = switch (instruction.input0) {
- .zero => @as(u16, 0),
- .immediate => i: {
- const val = self.bus.read16(@intCast(u16, self.ip));
- self.ip +%= 2;
- break :i val;
- },
- else => |e| e: {
- // peek or pop; show value at current SP, and if pop, increment sp
- const val = self.bus.read16(self.sp);
- if (e == .pop) {
- self.sp +%= 2;
- }
- break :e val;
- },
- };
- const val1 = switch (instruction.input1) {
- .zero => @as(u16, 0),
- .immediate => i: {
- const val = self.bus.read16(@intCast(u16, self.ip));
- self.ip +%= 2;
- break :i val;
- },
- else => |e| e: {
- // peek or pop; show value at current SP, and if pop, increment sp
- const val = self.bus.read16(self.sp);
- if (e == .pop) {
- self.sp +%= 2;
- }
- break :e val;
- },
- };
-
- const output: u16 = switch (instruction.command) {
- .get => self.bus.read16(self.bp +% (2 *% val0)),
- .set => a: {
- self.bus.write16(self.bp +% 2 *% val0, val1);
- break :a val1;
- },
- .load8 => self.bus.read8(val0),
- .load16 => self.bus.read16(val0),
- .store8 => a: {
- const val = @truncate(u8, val1);
- self.bus.write8(val0, val);
- break :a val;
- },
- .store16 => a: {
- self.bus.write16(val0, val1);
- break :a val1;
- },
- .copy => val0,
- .add => a: {
- var val: u16 = undefined;
- self.fr.carry = @addWithOverflow(u16, val0, val1, &val);
- break :a val;
- },
- .sub => a: {
- var val: u16 = undefined;
- self.fr.carry = @subWithOverflow(u16, val0, val1, &val);
- break :a val;
- },
- .spset => a: {
- self.sp = val0;
- break :a val0;
- },
- .bpset => a: {
- self.bp = val0;
- break :a val0;
- },
- .frset => a: {
- const val = (@bitCast(u16, self.fr) & val1) | (val0 & ~val1);
- self.fr = @bitCast(FlagRegister, val);
- break :a val;
- },
- .bswap => (val0 >> 8) | (val0 << 8),
- .bpget => self.bp,
- .spget => self.sp,
- .ipget => self.ip +% (2 *% val0),
- .lsl => val0 << 1,
- .lsr => val0 >> 1,
- .@"and" => val0 & val1,
- .@"or" => val0 | val1,
- .xor => val0 ^ val1,
- .not => ~val0,
- .undefined0 => {
- self.undefined0 = true;
- // Break out of the loop, and let the caller decide what to do
- return;
- },
- .undefined1 => {
- self.undefined1 = true;
- // Break out of the loop, and let the caller decide what to do
- return;
- },
- .signext => if ((val0 & 0x80) != 0)
- (val0 & 0xFF) | 0xFF00
- else
- (val0 & 0xFF),
- else => return error.Unimplemented,
- };
-
- switch (instruction.output) {
- .discard => {},
- .push => {
- self.sp -%= 2;
- self.bus.write16(self.sp, output);
- },
- .jump => {
- self.ip = output;
- },
- else => return error.Unimplemented,
- }
- if (instruction.modify_flags) {
- self.fr.negative = (output & 0x8000) != 0;
- self.fr.zero = (output == 0x0000);
- }
- } else {
- if (instruction.input0 == .immediate) self.ip +%= 2;
- if (instruction.input1 == .immediate) self.ip +%= 2;
- break;
- }
- }
- }
- };
-}
diff --git a/src-self-hosted/codegen/wasm.zig b/src-self-hosted/codegen/wasm.zig
deleted file mode 100644
index 4ea883840941ad47109538eba6395172df83d238..0000000000000000000000000000000000000000
--- a/src-self-hosted/codegen/wasm.zig
+++ /dev/null
@@ -1,142 +0,0 @@
-const std = @import("std");
-const Allocator = std.mem.Allocator;
-const ArrayList = std.ArrayList;
-const assert = std.debug.assert;
-const leb = std.debug.leb;
-const mem = std.mem;
-
-const Module = @import("../Module.zig");
-const Decl = Module.Decl;
-const Inst = @import("../ir.zig").Inst;
-const Type = @import("../type.zig").Type;
-const Value = @import("../value.zig").Value;
-
-fn genValtype(ty: Type) u8 {
- return switch (ty.tag()) {
- .u32, .i32 => 0x7F,
- .u64, .i64 => 0x7E,
- .f32 => 0x7D,
- .f64 => 0x7C,
- else => @panic("TODO: Implement more types for wasm."),
- };
-}
-
-pub fn genFunctype(buf: *ArrayList(u8), decl: *Decl) !void {
- const ty = decl.typed_value.most_recent.typed_value.ty;
- const writer = buf.writer();
-
- // functype magic
- try writer.writeByte(0x60);
-
- // param types
- try leb.writeULEB128(writer, @intCast(u32, ty.fnParamLen()));
- if (ty.fnParamLen() != 0) {
- const params = try buf.allocator.alloc(Type, ty.fnParamLen());
- defer buf.allocator.free(params);
- ty.fnParamTypes(params);
- for (params) |param_type| try writer.writeByte(genValtype(param_type));
- }
-
- // return type
- const return_type = ty.fnReturnType();
- switch (return_type.tag()) {
- .void, .noreturn => try leb.writeULEB128(writer, @as(u32, 0)),
- else => {
- try leb.writeULEB128(writer, @as(u32, 1));
- try writer.writeByte(genValtype(return_type));
- },
- }
-}
-
-pub fn genCode(buf: *ArrayList(u8), decl: *Decl) !void {
- assert(buf.items.len == 0);
- const writer = buf.writer();
-
- // Reserve space to write the size after generating the code
- try buf.resize(5);
-
- // Write the size of the locals vec
- // TODO: implement locals
- try leb.writeULEB128(writer, @as(u32, 0));
-
- // Write instructions
- // TODO: check for and handle death of instructions
- const tv = decl.typed_value.most_recent.typed_value;
- const mod_fn = tv.val.cast(Value.Payload.Function).?.func;
- for (mod_fn.analysis.success.instructions) |inst| try genInst(buf, decl, inst);
-
- // Write 'end' opcode
- try writer.writeByte(0x0B);
-
- // Fill in the size of the generated code to the reserved space at the
- // beginning of the buffer.
- const size = buf.items.len - 5 + decl.fn_link.wasm.?.idx_refs.items.len * 5;
- leb.writeUnsignedFixed(5, buf.items[0..5], @intCast(u32, size));
-}
-
-fn genInst(buf: *ArrayList(u8), decl: *Decl, inst: *Inst) !void {
- return switch (inst.tag) {
- .call => genCall(buf, decl, inst.castTag(.call).?),
- .constant => genConstant(buf, decl, inst.castTag(.constant).?),
- .dbg_stmt => {},
- .ret => genRet(buf, decl, inst.castTag(.ret).?),
- .retvoid => {},
- else => error.TODOImplementMoreWasmCodegen,
- };
-}
-
-fn genConstant(buf: *ArrayList(u8), decl: *Decl, inst: *Inst.Constant) !void {
- const writer = buf.writer();
- switch (inst.base.ty.tag()) {
- .u32 => {
- try writer.writeByte(0x41); // i32.const
- try leb.writeILEB128(writer, inst.val.toUnsignedInt());
- },
- .i32 => {
- try writer.writeByte(0x41); // i32.const
- try leb.writeILEB128(writer, inst.val.toSignedInt());
- },
- .u64 => {
- try writer.writeByte(0x42); // i64.const
- try leb.writeILEB128(writer, inst.val.toUnsignedInt());
- },
- .i64 => {
- try writer.writeByte(0x42); // i64.const
- try leb.writeILEB128(writer, inst.val.toSignedInt());
- },
- .f32 => {
- try writer.writeByte(0x43); // f32.const
- // TODO: enforce LE byte order
- try writer.writeAll(mem.asBytes(&inst.val.toFloat(f32)));
- },
- .f64 => {
- try writer.writeByte(0x44); // f64.const
- // TODO: enforce LE byte order
- try writer.writeAll(mem.asBytes(&inst.val.toFloat(f64)));
- },
- .void => {},
- else => return error.TODOImplementMoreWasmCodegen,
- }
-}
-
-fn genRet(buf: *ArrayList(u8), decl: *Decl, inst: *Inst.UnOp) !void {
- try genInst(buf, decl, inst.operand);
-}
-
-fn genCall(buf: *ArrayList(u8), decl: *Decl, inst: *Inst.Call) !void {
- const func_inst = inst.func.castTag(.constant).?;
- const func_val = func_inst.val.cast(Value.Payload.Function).?;
- const target = func_val.func.owner_decl;
- const target_ty = target.typed_value.most_recent.typed_value.ty;
-
- if (inst.args.len != 0) return error.TODOImplementMoreWasmCodegen;
-
- try buf.append(0x10); // call
-
- // The function index immediate argument will be filled in using this data
- // in link.Wasm.flush().
- try decl.fn_link.wasm.?.idx_refs.append(buf.allocator, .{
- .offset = @intCast(u32, buf.items.len),
- .decl = target,
- });
-}
diff --git a/src-self-hosted/codegen/x86.zig b/src-self-hosted/codegen/x86.zig
deleted file mode 100644
index fdad4e56db6139258e72b615a4d11dd8246b0d19..0000000000000000000000000000000000000000
--- a/src-self-hosted/codegen/x86.zig
+++ /dev/null
@@ -1,123 +0,0 @@
-const std = @import("std");
-const DW = std.dwarf;
-
-// zig fmt: off
-pub const Register = enum(u8) {
- // 0 through 7, 32-bit registers. id is int value
- eax, ecx, edx, ebx, esp, ebp, esi, edi,
-
- // 8-15, 16-bit registers. id is int value - 8.
- ax, cx, dx, bx, sp, bp, si, di,
-
- // 16-23, 8-bit registers. id is int value - 16.
- al, cl, dl, bl, ah, ch, dh, bh,
-
- /// Returns the bit-width of the register.
- pub fn size(self: @This()) u7 {
- return switch (@enumToInt(self)) {
- 0...7 => 32,
- 8...15 => 16,
- 16...23 => 8,
- else => unreachable,
- };
- }
-
- /// Returns the register's id. This is used in practically every opcode the
- /// x86 has. It is embedded in some instructions, such as the `B8 +rd` move
- /// instruction, and is used in the R/M byte.
- pub fn id(self: @This()) u3 {
- return @truncate(u3, @enumToInt(self));
- }
-
- /// Returns the index into `callee_preserved_regs`.
- pub fn allocIndex(self: Register) ?u4 {
- return switch (self) {
- .eax, .ax, .al => 0,
- .ecx, .cx, .cl => 1,
- .edx, .dx, .dl => 2,
- .esi, .si => 3,
- .edi, .di => 4,
- else => null,
- };
- }
-
- /// Convert from any register to its 32 bit alias.
- pub fn to32(self: Register) Register {
- return @intToEnum(Register, @as(u8, self.id()));
- }
-
- /// Convert from any register to its 16 bit alias.
- pub fn to16(self: Register) Register {
- return @intToEnum(Register, @as(u8, self.id()) + 8);
- }
-
- /// Convert from any register to its 8 bit alias.
- pub fn to8(self: Register) Register {
- return @intToEnum(Register, @as(u8, self.id()) + 16);
- }
-
-
- pub fn dwarfLocOp(reg: Register) u8 {
- return switch (reg.to32()) {
- .eax => DW.OP_reg0,
- .ecx => DW.OP_reg1,
- .edx => DW.OP_reg2,
- .ebx => DW.OP_reg3,
- .esp => DW.OP_reg4,
- .ebp => DW.OP_reg5,
- .esi => DW.OP_reg6,
- .edi => DW.OP_reg7,
- else => unreachable,
- };
- }
-};
-
-// zig fmt: on
-
-pub const callee_preserved_regs = [_]Register{ .eax, .ecx, .edx, .esi, .edi };
-
-// TODO add these to Register enum and corresponding dwarfLocOp
-// // Return Address register. This is stored in `0(%esp, "")` and is not a physical register.
-// RA = (8, "RA"),
-//
-// ST0 = (11, "st0"),
-// ST1 = (12, "st1"),
-// ST2 = (13, "st2"),
-// ST3 = (14, "st3"),
-// ST4 = (15, "st4"),
-// ST5 = (16, "st5"),
-// ST6 = (17, "st6"),
-// ST7 = (18, "st7"),
-//
-// XMM0 = (21, "xmm0"),
-// XMM1 = (22, "xmm1"),
-// XMM2 = (23, "xmm2"),
-// XMM3 = (24, "xmm3"),
-// XMM4 = (25, "xmm4"),
-// XMM5 = (26, "xmm5"),
-// XMM6 = (27, "xmm6"),
-// XMM7 = (28, "xmm7"),
-//
-// MM0 = (29, "mm0"),
-// MM1 = (30, "mm1"),
-// MM2 = (31, "mm2"),
-// MM3 = (32, "mm3"),
-// MM4 = (33, "mm4"),
-// MM5 = (34, "mm5"),
-// MM6 = (35, "mm6"),
-// MM7 = (36, "mm7"),
-//
-// MXCSR = (39, "mxcsr"),
-//
-// ES = (40, "es"),
-// CS = (41, "cs"),
-// SS = (42, "ss"),
-// DS = (43, "ds"),
-// FS = (44, "fs"),
-// GS = (45, "gs"),
-//
-// TR = (48, "tr"),
-// LDTR = (49, "ldtr"),
-//
-// FS_BASE = (93, "fs.base"),
-// GS_BASE = (94, "gs.base"),
diff --git a/src-self-hosted/codegen/x86_64.zig b/src-self-hosted/codegen/x86_64.zig
deleted file mode 100644
index dea39f82cdbdd2c4d6d623c6d3b697c00d9bfda1..0000000000000000000000000000000000000000
--- a/src-self-hosted/codegen/x86_64.zig
+++ /dev/null
@@ -1,220 +0,0 @@
-const std = @import("std");
-const Type = @import("../Type.zig");
-const DW = std.dwarf;
-
-// zig fmt: off
-
-/// Definitions of all of the x64 registers. The order is semantically meaningful.
-/// The registers are defined such that IDs go in descending order of 64-bit,
-/// 32-bit, 16-bit, and then 8-bit, and each set contains exactly sixteen
-/// registers. This results in some useful properties:
-///
-/// Any 64-bit register can be turned into its 32-bit form by adding 16, and
-/// vice versa. This also works between 32-bit and 16-bit forms. With 8-bit, it
-/// works for all except for sp, bp, si, and di, which do *not* have an 8-bit
-/// form.
-///
-/// If (register & 8) is set, the register is extended.
-///
-/// The ID can be easily determined by figuring out what range the register is
-/// in, and then subtracting the base.
-pub const Register = enum(u8) {
- // 0 through 15, 64-bit registers. 8-15 are extended.
- // id is just the int value.
- rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi,
- r8, r9, r10, r11, r12, r13, r14, r15,
-
- // 16 through 31, 32-bit registers. 24-31 are extended.
- // id is int value - 16.
- eax, ecx, edx, ebx, esp, ebp, esi, edi,
- r8d, r9d, r10d, r11d, r12d, r13d, r14d, r15d,
-
- // 32-47, 16-bit registers. 40-47 are extended.
- // id is int value - 32.
- ax, cx, dx, bx, sp, bp, si, di,
- r8w, r9w, r10w, r11w, r12w, r13w, r14w, r15w,
-
- // 48-63, 8-bit registers. 56-63 are extended.
- // id is int value - 48.
- al, cl, dl, bl, ah, ch, dh, bh,
- r8b, r9b, r10b, r11b, r12b, r13b, r14b, r15b,
-
- /// Returns the bit-width of the register.
- pub fn size(self: Register) u7 {
- return switch (@enumToInt(self)) {
- 0...15 => 64,
- 16...31 => 32,
- 32...47 => 16,
- 48...64 => 8,
- else => unreachable,
- };
- }
-
- /// Returns whether the register is *extended*. Extended registers are the
- /// new registers added with amd64, r8 through r15. This also includes any
- /// other variant of access to those registers, such as r8b, r15d, and so
- /// on. This is needed because access to these registers requires special
- /// handling via the REX prefix, via the B or R bits, depending on context.
- pub fn isExtended(self: Register) bool {
- return @enumToInt(self) & 0x08 != 0;
- }
-
- /// This returns the 4-bit register ID, which is used in practically every
- /// opcode. Note that bit 3 (the highest bit) is *never* used directly in
- /// an instruction (@see isExtended), and requires special handling. The
- /// lower three bits are often embedded directly in instructions (such as
- /// the B8 variant of moves), or used in R/M bytes.
- pub fn id(self: Register) u4 {
- return @truncate(u4, @enumToInt(self));
- }
-
- /// Returns the index into `callee_preserved_regs`.
- pub fn allocIndex(self: Register) ?u4 {
- return switch (self) {
- .rax, .eax, .ax, .al => 0,
- .rcx, .ecx, .cx, .cl => 1,
- .rdx, .edx, .dx, .dl => 2,
- .rsi, .esi, .si => 3,
- .rdi, .edi, .di => 4,
- .r8, .r8d, .r8w, .r8b => 5,
- .r9, .r9d, .r9w, .r9b => 6,
- .r10, .r10d, .r10w, .r10b => 7,
- .r11, .r11d, .r11w, .r11b => 8,
- else => null,
- };
- }
-
- /// Convert from any register to its 64 bit alias.
- pub fn to64(self: Register) Register {
- return @intToEnum(Register, self.id());
- }
-
- /// Convert from any register to its 32 bit alias.
- pub fn to32(self: Register) Register {
- return @intToEnum(Register, @as(u8, self.id()) + 16);
- }
-
- /// Convert from any register to its 16 bit alias.
- pub fn to16(self: Register) Register {
- return @intToEnum(Register, @as(u8, self.id()) + 32);
- }
-
- /// Convert from any register to its 8 bit alias.
- pub fn to8(self: Register) Register {
- return @intToEnum(Register, @as(u8, self.id()) + 48);
- }
-
- pub fn dwarfLocOp(self: Register) u8 {
- return switch (self.to64()) {
- .rax => DW.OP_reg0,
- .rdx => DW.OP_reg1,
- .rcx => DW.OP_reg2,
- .rbx => DW.OP_reg3,
- .rsi => DW.OP_reg4,
- .rdi => DW.OP_reg5,
- .rbp => DW.OP_reg6,
- .rsp => DW.OP_reg7,
-
- .r8 => DW.OP_reg8,
- .r9 => DW.OP_reg9,
- .r10 => DW.OP_reg10,
- .r11 => DW.OP_reg11,
- .r12 => DW.OP_reg12,
- .r13 => DW.OP_reg13,
- .r14 => DW.OP_reg14,
- .r15 => DW.OP_reg15,
-
- else => unreachable,
- };
- }
-};
-
-// zig fmt: on
-
-/// These registers belong to the called function.
-pub const callee_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 };
-pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
-pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };
-
-// TODO add these registers to the enum and populate dwarfLocOp
-// // Return Address register. This is stored in `0(%rsp, "")` and is not a physical register.
-// RA = (16, "RA"),
-//
-// XMM0 = (17, "xmm0"),
-// XMM1 = (18, "xmm1"),
-// XMM2 = (19, "xmm2"),
-// XMM3 = (20, "xmm3"),
-// XMM4 = (21, "xmm4"),
-// XMM5 = (22, "xmm5"),
-// XMM6 = (23, "xmm6"),
-// XMM7 = (24, "xmm7"),
-//
-// XMM8 = (25, "xmm8"),
-// XMM9 = (26, "xmm9"),
-// XMM10 = (27, "xmm10"),
-// XMM11 = (28, "xmm11"),
-// XMM12 = (29, "xmm12"),
-// XMM13 = (30, "xmm13"),
-// XMM14 = (31, "xmm14"),
-// XMM15 = (32, "xmm15"),
-//
-// ST0 = (33, "st0"),
-// ST1 = (34, "st1"),
-// ST2 = (35, "st2"),
-// ST3 = (36, "st3"),
-// ST4 = (37, "st4"),
-// ST5 = (38, "st5"),
-// ST6 = (39, "st6"),
-// ST7 = (40, "st7"),
-//
-// MM0 = (41, "mm0"),
-// MM1 = (42, "mm1"),
-// MM2 = (43, "mm2"),
-// MM3 = (44, "mm3"),
-// MM4 = (45, "mm4"),
-// MM5 = (46, "mm5"),
-// MM6 = (47, "mm6"),
-// MM7 = (48, "mm7"),
-//
-// RFLAGS = (49, "rFLAGS"),
-// ES = (50, "es"),
-// CS = (51, "cs"),
-// SS = (52, "ss"),
-// DS = (53, "ds"),
-// FS = (54, "fs"),
-// GS = (55, "gs"),
-//
-// FS_BASE = (58, "fs.base"),
-// GS_BASE = (59, "gs.base"),
-//
-// TR = (62, "tr"),
-// LDTR = (63, "ldtr"),
-// MXCSR = (64, "mxcsr"),
-// FCW = (65, "fcw"),
-// FSW = (66, "fsw"),
-//
-// XMM16 = (67, "xmm16"),
-// XMM17 = (68, "xmm17"),
-// XMM18 = (69, "xmm18"),
-// XMM19 = (70, "xmm19"),
-// XMM20 = (71, "xmm20"),
-// XMM21 = (72, "xmm21"),
-// XMM22 = (73, "xmm22"),
-// XMM23 = (74, "xmm23"),
-// XMM24 = (75, "xmm24"),
-// XMM25 = (76, "xmm25"),
-// XMM26 = (77, "xmm26"),
-// XMM27 = (78, "xmm27"),
-// XMM28 = (79, "xmm28"),
-// XMM29 = (80, "xmm29"),
-// XMM30 = (81, "xmm30"),
-// XMM31 = (82, "xmm31"),
-//
-// K0 = (118, "k0"),
-// K1 = (119, "k1"),
-// K2 = (120, "k2"),
-// K3 = (121, "k3"),
-// K4 = (122, "k4"),
-// K5 = (123, "k5"),
-// K6 = (124, "k6"),
-// K7 = (125, "k7"),
diff --git a/src-self-hosted/glibc.zig b/src-self-hosted/glibc.zig
deleted file mode 100644
index 9ede436f63e39eefdc615d23b546a2f2835c2bea..0000000000000000000000000000000000000000
--- a/src-self-hosted/glibc.zig
+++ /dev/null
@@ -1,1013 +0,0 @@
-const std = @import("std");
-const Allocator = std.mem.Allocator;
-const mem = std.mem;
-const path = std.fs.path;
-const assert = std.debug.assert;
-
-const target_util = @import("target.zig");
-const Compilation = @import("Compilation.zig");
-const build_options = @import("build_options");
-const trace = @import("tracy.zig").trace;
-const Cache = @import("Cache.zig");
-const Package = @import("Package.zig");
-
-pub const Lib = struct {
- name: []const u8,
- sover: u8,
-};
-
-pub const Fn = struct {
- name: []const u8,
- lib: *const Lib,
-};
-
-pub const VerList = struct {
- /// 7 is just the max number, we know statically it's big enough.
- versions: [7]u8,
- len: u8,
-};
-
-pub const ABI = struct {
- all_versions: []const std.builtin.Version,
- all_functions: []const Fn,
- /// The value is a pointer to all_functions.len items and each item is an index into all_functions.
- version_table: std.AutoHashMapUnmanaged(target_util.ArchOsAbi, [*]VerList),
- arena_state: std.heap.ArenaAllocator.State,
-
- pub fn destroy(abi: *ABI, gpa: *Allocator) void {
- abi.version_table.deinit(gpa);
- abi.arena_state.promote(gpa).deinit(); // Frees the ABI memory too.
- }
-};
-
-pub const libs = [_]Lib{
- .{ .name = "c", .sover = 6 },
- .{ .name = "m", .sover = 6 },
- .{ .name = "pthread", .sover = 0 },
- .{ .name = "dl", .sover = 2 },
- .{ .name = "rt", .sover = 1 },
- .{ .name = "ld", .sover = 2 },
- .{ .name = "util", .sover = 1 },
-};
-
-pub const LoadMetaDataError = error{
- /// The files that ship with the Zig compiler were unable to be read, or otherwise had malformed data.
- ZigInstallationCorrupt,
- OutOfMemory,
-};
-
-/// This function will emit a log error when there is a problem with the zig installation and then return
-/// `error.ZigInstallationCorrupt`.
-pub fn loadMetaData(gpa: *Allocator, zig_lib_dir: std.fs.Dir) LoadMetaDataError!*ABI {
- const tracy = trace(@src());
- defer tracy.end();
-
- var arena_allocator = std.heap.ArenaAllocator.init(gpa);
- errdefer arena_allocator.deinit();
- const arena = &arena_allocator.allocator;
-
- var all_versions = std.ArrayListUnmanaged(std.builtin.Version){};
- var all_functions = std.ArrayListUnmanaged(Fn){};
- var version_table = std.AutoHashMapUnmanaged(target_util.ArchOsAbi, [*]VerList){};
- errdefer version_table.deinit(gpa);
-
- var glibc_dir = zig_lib_dir.openDir("libc" ++ path.sep_str ++ "glibc", .{}) catch |err| {
- std.log.err("unable to open glibc dir: {}", .{@errorName(err)});
- return error.ZigInstallationCorrupt;
- };
- defer glibc_dir.close();
-
- const max_txt_size = 500 * 1024; // Bigger than this and something is definitely borked.
- const vers_txt_contents = glibc_dir.readFileAlloc(gpa, "vers.txt", max_txt_size) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- else => {
- std.log.err("unable to read vers.txt: {}", .{@errorName(err)});
- return error.ZigInstallationCorrupt;
- },
- };
- defer gpa.free(vers_txt_contents);
-
- // Arena allocated because the result contains references to function names.
- const fns_txt_contents = glibc_dir.readFileAlloc(arena, "fns.txt", max_txt_size) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- else => {
- std.log.err("unable to read fns.txt: {}", .{@errorName(err)});
- return error.ZigInstallationCorrupt;
- },
- };
-
- const abi_txt_contents = glibc_dir.readFileAlloc(gpa, "abi.txt", max_txt_size) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- else => {
- std.log.err("unable to read abi.txt: {}", .{@errorName(err)});
- return error.ZigInstallationCorrupt;
- },
- };
- defer gpa.free(abi_txt_contents);
-
- {
- var it = mem.tokenize(vers_txt_contents, "\r\n");
- var line_i: usize = 1;
- while (it.next()) |line| : (line_i += 1) {
- const prefix = "GLIBC_";
- if (!mem.startsWith(u8, line, prefix)) {
- std.log.err("vers.txt:{}: expected 'GLIBC_' prefix", .{line_i});
- return error.ZigInstallationCorrupt;
- }
- const adjusted_line = line[prefix.len..];
- const ver = std.builtin.Version.parse(adjusted_line) catch |err| {
- std.log.err("vers.txt:{}: unable to parse glibc version '{}': {}", .{ line_i, line, @errorName(err) });
- return error.ZigInstallationCorrupt;
- };
- try all_versions.append(arena, ver);
- }
- }
- {
- var file_it = mem.tokenize(fns_txt_contents, "\r\n");
- var line_i: usize = 1;
- while (file_it.next()) |line| : (line_i += 1) {
- var line_it = mem.tokenize(line, " ");
- const fn_name = line_it.next() orelse {
- std.log.err("fns.txt:{}: expected function name", .{line_i});
- return error.ZigInstallationCorrupt;
- };
- const lib_name = line_it.next() orelse {
- std.log.err("fns.txt:{}: expected library name", .{line_i});
- return error.ZigInstallationCorrupt;
- };
- const lib = findLib(lib_name) orelse {
- std.log.err("fns.txt:{}: unknown library name: {}", .{ line_i, lib_name });
- return error.ZigInstallationCorrupt;
- };
- try all_functions.append(arena, .{
- .name = fn_name,
- .lib = lib,
- });
- }
- }
- {
- var file_it = mem.split(abi_txt_contents, "\n");
- var line_i: usize = 0;
- while (true) {
- const ver_list_base: []VerList = blk: {
- const line = file_it.next() orelse break;
- if (line.len == 0) break;
- line_i += 1;
- const ver_list_base = try arena.alloc(VerList, all_functions.items.len);
- var line_it = mem.tokenize(line, " ");
- while (line_it.next()) |target_string| {
- var component_it = mem.tokenize(target_string, "-");
- const arch_name = component_it.next() orelse {
- std.log.err("abi.txt:{}: expected arch name", .{line_i});
- return error.ZigInstallationCorrupt;
- };
- const os_name = component_it.next() orelse {
- std.log.err("abi.txt:{}: expected OS name", .{line_i});
- return error.ZigInstallationCorrupt;
- };
- const abi_name = component_it.next() orelse {
- std.log.err("abi.txt:{}: expected ABI name", .{line_i});
- return error.ZigInstallationCorrupt;
- };
- const arch_tag = std.meta.stringToEnum(std.Target.Cpu.Arch, arch_name) orelse {
- std.log.err("abi.txt:{}: unrecognized arch: '{}'", .{ line_i, arch_name });
- return error.ZigInstallationCorrupt;
- };
- if (!mem.eql(u8, os_name, "linux")) {
- std.log.err("abi.txt:{}: expected OS 'linux', found '{}'", .{ line_i, os_name });
- return error.ZigInstallationCorrupt;
- }
- const abi_tag = std.meta.stringToEnum(std.Target.Abi, abi_name) orelse {
- std.log.err("abi.txt:{}: unrecognized ABI: '{}'", .{ line_i, abi_name });
- return error.ZigInstallationCorrupt;
- };
-
- const triple = target_util.ArchOsAbi{
- .arch = arch_tag,
- .os = .linux,
- .abi = abi_tag,
- };
- try version_table.put(gpa, triple, ver_list_base.ptr);
- }
- break :blk ver_list_base;
- };
- for (ver_list_base) |*ver_list| {
- const line = file_it.next() orelse {
- std.log.err("abi.txt:{}: missing version number line", .{line_i});
- return error.ZigInstallationCorrupt;
- };
- line_i += 1;
-
- ver_list.* = .{
- .versions = undefined,
- .len = 0,
- };
- var line_it = mem.tokenize(line, " ");
- while (line_it.next()) |version_index_string| {
- if (ver_list.len >= ver_list.versions.len) {
- // If this happens with legit data, increase the array len in the type.
- std.log.err("abi.txt:{}: too many versions", .{line_i});
- return error.ZigInstallationCorrupt;
- }
- const version_index = std.fmt.parseInt(u8, version_index_string, 10) catch |err| {
- // If this happens with legit data, increase the size of the integer type in the struct.
- std.log.err("abi.txt:{}: unable to parse version: {}", .{ line_i, @errorName(err) });
- return error.ZigInstallationCorrupt;
- };
-
- ver_list.versions[ver_list.len] = version_index;
- ver_list.len += 1;
- }
- }
- }
- }
-
- const abi = try arena.create(ABI);
- abi.* = .{
- .all_versions = all_versions.items,
- .all_functions = all_functions.items,
- .version_table = version_table,
- .arena_state = arena_allocator.state,
- };
- return abi;
-}
-
-fn findLib(name: []const u8) ?*const Lib {
- for (libs) |*lib| {
- if (mem.eql(u8, lib.name, name)) {
- return lib;
- }
- }
- return null;
-}
-
-pub const CRTFile = enum {
- crti_o,
- crtn_o,
- scrt1_o,
- libc_nonshared_a,
-};
-
-pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
- if (!build_options.have_llvm) {
- return error.ZigCompilerNotBuiltWithLLVMExtensions;
- }
- const gpa = comp.gpa;
- var arena_allocator = std.heap.ArenaAllocator.init(gpa);
- defer arena_allocator.deinit();
- const arena = &arena_allocator.allocator;
-
- switch (crt_file) {
- .crti_o => {
- var args = std.ArrayList([]const u8).init(arena);
- try add_include_dirs(comp, arena, &args);
- try args.appendSlice(&[_][]const u8{
- "-D_LIBC_REENTRANT",
- "-include",
- try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-modules.h"),
- "-DMODULE_NAME=libc",
- "-Wno-nonportable-include-path",
- "-include",
- try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-symbols.h"),
- "-DTOP_NAMESPACE=glibc",
- "-DASSEMBLER",
- "-g",
- "-Wa,--noexecstack",
- });
- return build_crt_file(comp, "crti.o", .Obj, &[1]Compilation.CSourceFile{
- .{
- .src_path = try start_asm_path(comp, arena, "crti.S"),
- .extra_flags = args.items,
- },
- });
- },
- .crtn_o => {
- var args = std.ArrayList([]const u8).init(arena);
- try add_include_dirs(comp, arena, &args);
- try args.appendSlice(&[_][]const u8{
- "-D_LIBC_REENTRANT",
- "-DMODULE_NAME=libc",
- "-DTOP_NAMESPACE=glibc",
- "-DASSEMBLER",
- "-g",
- "-Wa,--noexecstack",
- });
- return build_crt_file(comp, "crtn.o", .Obj, &[1]Compilation.CSourceFile{
- .{
- .src_path = try start_asm_path(comp, arena, "crtn.S"),
- .extra_flags = args.items,
- },
- });
- },
- .scrt1_o => {
- const start_os: Compilation.CSourceFile = blk: {
- var args = std.ArrayList([]const u8).init(arena);
- try add_include_dirs(comp, arena, &args);
- try args.appendSlice(&[_][]const u8{
- "-D_LIBC_REENTRANT",
- "-include",
- try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-modules.h"),
- "-DMODULE_NAME=libc",
- "-Wno-nonportable-include-path",
- "-include",
- try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-symbols.h"),
- "-DPIC",
- "-DSHARED",
- "-DTOP_NAMESPACE=glibc",
- "-DASSEMBLER",
- "-g",
- "-Wa,--noexecstack",
- });
- break :blk .{
- .src_path = try start_asm_path(comp, arena, "start.S"),
- .extra_flags = args.items,
- };
- };
- const abi_note_o: Compilation.CSourceFile = blk: {
- var args = std.ArrayList([]const u8).init(arena);
- try args.appendSlice(&[_][]const u8{
- "-I",
- try lib_path(comp, arena, lib_libc_glibc ++ "csu"),
- });
- try add_include_dirs(comp, arena, &args);
- try args.appendSlice(&[_][]const u8{
- "-D_LIBC_REENTRANT",
- "-DMODULE_NAME=libc",
- "-DTOP_NAMESPACE=glibc",
- "-DASSEMBLER",
- "-g",
- "-Wa,--noexecstack",
- });
- break :blk .{
- .src_path = try lib_path(comp, arena, lib_libc_glibc ++ "csu" ++ path.sep_str ++ "abi-note.S"),
- .extra_flags = args.items,
- };
- };
- return build_crt_file(comp, "Scrt1.o", .Obj, &[_]Compilation.CSourceFile{ start_os, abi_note_o });
- },
- .libc_nonshared_a => {
- const deps = [_][]const u8{
- lib_libc_glibc ++ "stdlib" ++ path.sep_str ++ "atexit.c",
- lib_libc_glibc ++ "stdlib" ++ path.sep_str ++ "at_quick_exit.c",
- lib_libc_glibc ++ "io" ++ path.sep_str ++ "stat.c",
- lib_libc_glibc ++ "io" ++ path.sep_str ++ "fstat.c",
- lib_libc_glibc ++ "io" ++ path.sep_str ++ "lstat.c",
- lib_libc_glibc ++ "io" ++ path.sep_str ++ "stat64.c",
- lib_libc_glibc ++ "io" ++ path.sep_str ++ "fstat64.c",
- lib_libc_glibc ++ "io" ++ path.sep_str ++ "lstat64.c",
- lib_libc_glibc ++ "io" ++ path.sep_str ++ "fstatat.c",
- lib_libc_glibc ++ "io" ++ path.sep_str ++ "fstatat64.c",
- lib_libc_glibc ++ "io" ++ path.sep_str ++ "mknod.c",
- lib_libc_glibc ++ "io" ++ path.sep_str ++ "mknodat.c",
- lib_libc_glibc ++ "nptl" ++ path.sep_str ++ "pthread_atfork.c",
- lib_libc_glibc ++ "debug" ++ path.sep_str ++ "stack_chk_fail_local.c",
- };
-
- var c_source_files: [deps.len + 1]Compilation.CSourceFile = undefined;
-
- c_source_files[0] = blk: {
- var args = std.ArrayList([]const u8).init(arena);
- try args.appendSlice(&[_][]const u8{
- "-std=gnu11",
- "-fgnu89-inline",
- "-g",
- "-O2",
- "-fmerge-all-constants",
- "-fno-stack-protector",
- "-fmath-errno",
- "-fno-stack-protector",
- "-I",
- try lib_path(comp, arena, lib_libc_glibc ++ "csu"),
- });
- try add_include_dirs(comp, arena, &args);
- try args.appendSlice(&[_][]const u8{
- "-DSTACK_PROTECTOR_LEVEL=0",
- "-fPIC",
- "-fno-stack-protector",
- "-ftls-model=initial-exec",
- "-D_LIBC_REENTRANT",
- "-include",
- try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-modules.h"),
- "-DMODULE_NAME=libc",
- "-Wno-nonportable-include-path",
- "-include",
- try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-symbols.h"),
- "-DPIC",
- "-DLIBC_NONSHARED=1",
- "-DTOP_NAMESPACE=glibc",
- });
- break :blk .{
- .src_path = try lib_path(comp, arena, lib_libc_glibc ++ "csu" ++ path.sep_str ++ "elf-init.c"),
- .extra_flags = args.items,
- };
- };
-
- for (deps) |dep, i| {
- var args = std.ArrayList([]const u8).init(arena);
- try args.appendSlice(&[_][]const u8{
- "-std=gnu11",
- "-fgnu89-inline",
- "-g",
- "-O2",
- "-fmerge-all-constants",
- "-fno-stack-protector",
- "-fmath-errno",
- "-ftls-model=initial-exec",
- "-Wno-ignored-attributes",
- });
- try add_include_dirs(comp, arena, &args);
- try args.appendSlice(&[_][]const u8{
- "-D_LIBC_REENTRANT",
- "-include",
- try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-modules.h"),
- "-DMODULE_NAME=libc",
- "-Wno-nonportable-include-path",
- "-include",
- try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-symbols.h"),
- "-DPIC",
- "-DLIBC_NONSHARED=1",
- "-DTOP_NAMESPACE=glibc",
- });
- c_source_files[i + 1] = .{
- .src_path = try lib_path(comp, arena, dep),
- .extra_flags = args.items,
- };
- }
- return build_crt_file(comp, "libc_nonshared.a", .Lib, &c_source_files);
- },
- }
-}
-
-fn start_asm_path(comp: *Compilation, arena: *Allocator, basename: []const u8) ![]const u8 {
- const arch = comp.getTarget().cpu.arch;
- const is_ppc = arch == .powerpc or arch == .powerpc64 or arch == .powerpc64le;
- const is_aarch64 = arch == .aarch64 or arch == .aarch64_be;
- const is_sparc = arch == .sparc or arch == .sparcel or arch == .sparcv9;
- const is_64 = arch.ptrBitWidth() == 64;
-
- const s = path.sep_str;
-
- var result = std.ArrayList(u8).init(arena);
- try result.appendSlice(comp.zig_lib_directory.path.?);
- try result.appendSlice(s ++ "libc" ++ s ++ "glibc" ++ s ++ "sysdeps" ++ s);
- if (is_sparc) {
- if (is_64) {
- try result.appendSlice("sparc" ++ s ++ "sparc64");
- } else {
- try result.appendSlice("sparc" ++ s ++ "sparc32");
- }
- } else if (arch.isARM()) {
- try result.appendSlice("arm");
- } else if (arch.isMIPS()) {
- try result.appendSlice("mips");
- } else if (arch == .x86_64) {
- try result.appendSlice("x86_64");
- } else if (arch == .i386) {
- try result.appendSlice("i386");
- } else if (is_aarch64) {
- try result.appendSlice("aarch64");
- } else if (arch.isRISCV()) {
- try result.appendSlice("riscv");
- } else if (is_ppc) {
- if (is_64) {
- try result.appendSlice("powerpc" ++ s ++ "powerpc64");
- } else {
- try result.appendSlice("powerpc" ++ s ++ "powerpc32");
- }
- }
-
- try result.appendSlice(s);
- try result.appendSlice(basename);
- return result.items;
-}
-
-fn add_include_dirs(comp: *Compilation, arena: *Allocator, args: *std.ArrayList([]const u8)) error{OutOfMemory}!void {
- const target = comp.getTarget();
- const arch = target.cpu.arch;
- const opt_nptl: ?[]const u8 = if (target.os.tag == .linux) "nptl" else "htl";
- const glibc = try lib_path(comp, arena, lib_libc ++ "glibc");
-
- const s = path.sep_str;
-
- try args.append("-I");
- try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "include"));
-
- if (target.os.tag == .linux) {
- try add_include_dirs_arch(arena, args, arch, null, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix" ++ s ++ "sysv" ++ s ++ "linux"));
- }
-
- if (opt_nptl) |nptl| {
- try add_include_dirs_arch(arena, args, arch, nptl, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps"));
- }
-
- if (target.os.tag == .linux) {
- try args.append("-I");
- try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++
- "unix" ++ s ++ "sysv" ++ s ++ "linux" ++ s ++ "generic"));
-
- try args.append("-I");
- try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++
- "unix" ++ s ++ "sysv" ++ s ++ "linux" ++ s ++ "include"));
- try args.append("-I");
- try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++
- "unix" ++ s ++ "sysv" ++ s ++ "linux"));
- }
- if (opt_nptl) |nptl| {
- try args.append("-I");
- try args.append(try path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, lib_libc_glibc ++ "sysdeps", nptl }));
- }
-
- try args.append("-I");
- try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "pthread"));
-
- try args.append("-I");
- try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix" ++ s ++ "sysv"));
-
- try add_include_dirs_arch(arena, args, arch, null, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix"));
-
- try args.append("-I");
- try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix"));
-
- try add_include_dirs_arch(arena, args, arch, null, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps"));
-
- try args.append("-I");
- try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "generic"));
-
- try args.append("-I");
- try args.append(try path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, lib_libc ++ "glibc" }));
-
- try args.append("-I");
- try args.append(try std.fmt.allocPrint(arena, "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-{}-{}", .{
- comp.zig_lib_directory.path.?, @tagName(arch), @tagName(target.os.tag), @tagName(target.abi),
- }));
-
- try args.append("-I");
- try args.append(try lib_path(comp, arena, lib_libc ++ "include" ++ s ++ "generic-glibc"));
-
- try args.append("-I");
- try args.append(try std.fmt.allocPrint(arena, "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-linux-any", .{
- comp.zig_lib_directory.path.?, @tagName(arch),
- }));
-
- try args.append("-I");
- try args.append(try lib_path(comp, arena, lib_libc ++ "include" ++ s ++ "any-linux-any"));
-}
-
-fn add_include_dirs_arch(
- arena: *Allocator,
- args: *std.ArrayList([]const u8),
- arch: std.Target.Cpu.Arch,
- opt_nptl: ?[]const u8,
- dir: []const u8,
-) error{OutOfMemory}!void {
- const is_x86 = arch == .i386 or arch == .x86_64;
- const is_aarch64 = arch == .aarch64 or arch == .aarch64_be;
- const is_ppc = arch == .powerpc or arch == .powerpc64 or arch == .powerpc64le;
- const is_sparc = arch == .sparc or arch == .sparcel or arch == .sparcv9;
- const is_64 = arch.ptrBitWidth() == 64;
-
- const s = path.sep_str;
-
- if (is_x86) {
- if (arch == .x86_64) {
- if (opt_nptl) |nptl| {
- try args.append("-I");
- try args.append(try path.join(arena, &[_][]const u8{ dir, "x86_64", nptl }));
- } else {
- try args.append("-I");
- try args.append(try path.join(arena, &[_][]const u8{ dir, "x86_64" }));
- }
- } else if (arch == .i386) {
- if (opt_nptl) |nptl| {
- try args.append("-I");
- try args.append(try path.join(arena, &[_][]const u8{ dir, "i386", nptl }));
- } else {
- try args.append("-I");
- try args.append(try path.join(arena, &[_][]const u8{ dir, "i386" }));
- }
- }
- if (opt_nptl) |nptl| {
- try args.append("-I");
- try args.append(try path.join(arena, &[_][]const u8{ dir, "x86", nptl }));
- } else {
- try args.append("-I");
- try args.append(try path.join(arena, &[_][]const u8{ dir, "x86" }));
- }
- } else if (arch.isARM()) {
- if (opt_nptl) |nptl| {
- try args.append("-I");
- try args.append(try path.join(arena, &[_][]const u8{ dir, "arm", nptl }));
- } else {
- try args.append("-I");
- try args.append(try path.join(arena, &[_][]const u8{ dir, "arm" }));
- }
- } else if (arch.isMIPS()) {
- if (opt_nptl) |nptl| {
- try args.append("-I");
- try args.append(try path.join(arena, &[_][]const u8{ dir, "mips", nptl }));
- } else {
- if (is_64) {
- try args.append("-I");
- try args.append(try path.join(arena, &[_][]const u8{ dir, "mips" ++ s ++ "mips64" }));
- } else {
- try args.append("-I");
- try args.append(try path.join(arena, &[_][]const u8{ dir, "mips" ++ s ++ "mips32" }));
- }
- try args.append("-I");
- try args.append(try path.join(arena, &[_][]const u8{ dir, "mips" }));
- }
- } else if (is_sparc) {
- if (opt_nptl) |nptl| {
- try args.append("-I");
- try args.append(try path.join(arena, &[_][]const u8{ dir, "sparc", nptl }));
- } else {
- if (is_64) {
- try args.append("-I");
- try args.append(try path.join(arena, &[_][]const u8{ dir, "sparc" ++ s ++ "sparc64" }));
- } else {
- try args.append("-I");
- try args.append(try path.join(arena, &[_][]const u8{ dir, "sparc" ++ s ++ "sparc32" }));
- }
- try args.append("-I");
- try args.append(try path.join(arena, &[_][]const u8{ dir, "sparc" }));
- }
- } else if (is_aarch64) {
- if (opt_nptl) |nptl| {
- try args.append("-I");
- try args.append(try path.join(arena, &[_][]const u8{ dir, "aarch64", nptl }));
- } else {
- try args.append("-I");
- try args.append(try path.join(arena, &[_][]const u8{ dir, "aarch64" }));
- }
- } else if (is_ppc) {
- if (opt_nptl) |nptl| {
- try args.append("-I");
- try args.append(try path.join(arena, &[_][]const u8{ dir, "powerpc", nptl }));
- } else {
- if (is_64) {
- try args.append("-I");
- try args.append(try path.join(arena, &[_][]const u8{ dir, "powerpc" ++ s ++ "powerpc64" }));
- } else {
- try args.append("-I");
- try args.append(try path.join(arena, &[_][]const u8{ dir, "powerpc" ++ s ++ "powerpc32" }));
- }
- try args.append("-I");
- try args.append(try path.join(arena, &[_][]const u8{ dir, "powerpc" }));
- }
- } else if (arch.isRISCV()) {
- if (opt_nptl) |nptl| {
- try args.append("-I");
- try args.append(try path.join(arena, &[_][]const u8{ dir, "riscv", nptl }));
- } else {
- try args.append("-I");
- try args.append(try path.join(arena, &[_][]const u8{ dir, "riscv" }));
- }
- }
-}
-
-fn path_from_lib(comp: *Compilation, arena: *Allocator, sub_path: []const u8) ![]const u8 {
- return path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, sub_path });
-}
-
-const lib_libc = "libc" ++ path.sep_str;
-const lib_libc_glibc = lib_libc ++ "glibc" ++ path.sep_str;
-
-fn lib_path(comp: *Compilation, arena: *Allocator, sub_path: []const u8) ![]const u8 {
- return path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, sub_path });
-}
-
-fn build_crt_file(
- comp: *Compilation,
- basename: []const u8,
- output_mode: std.builtin.OutputMode,
- c_source_files: []const Compilation.CSourceFile,
-) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- // TODO: This is extracted into a local variable to work around a stage1 miscompilation.
- const emit_bin = Compilation.EmitLoc{
- .directory = null, // Put it in the cache directory.
- .basename = basename,
- };
- const sub_compilation = try Compilation.create(comp.gpa, .{
- // TODO use the global cache directory here
- .zig_cache_directory = comp.zig_cache_directory,
- .zig_lib_directory = comp.zig_lib_directory,
- .target = comp.getTarget(),
- .root_name = mem.split(basename, ".").next().?,
- .root_pkg = null,
- .output_mode = output_mode,
- .rand = comp.rand,
- .libc_installation = comp.bin_file.options.libc_installation,
- .emit_bin = emit_bin,
- .optimize_mode = comp.bin_file.options.optimize_mode,
- .want_sanitize_c = false,
- .want_stack_check = false,
- .want_valgrind = false,
- .want_pic = comp.bin_file.options.pic,
- .emit_h = null,
- .strip = comp.bin_file.options.strip,
- .is_native_os = comp.bin_file.options.is_native_os,
- .self_exe_path = comp.self_exe_path,
- .c_source_files = c_source_files,
- .verbose_cc = comp.verbose_cc,
- .verbose_link = comp.bin_file.options.verbose_link,
- .verbose_tokenize = comp.verbose_tokenize,
- .verbose_ast = comp.verbose_ast,
- .verbose_ir = comp.verbose_ir,
- .verbose_llvm_ir = comp.verbose_llvm_ir,
- .verbose_cimport = comp.verbose_cimport,
- .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
- .clang_passthrough_mode = comp.clang_passthrough_mode,
- });
- defer sub_compilation.destroy();
-
- try sub_compilation.updateSubCompilation();
-
- try comp.crt_files.ensureCapacity(comp.gpa, comp.crt_files.count() + 1);
- const artifact_path = if (sub_compilation.bin_file.options.directory.path) |p|
- try path.join(comp.gpa, &[_][]const u8{ p, basename })
- else
- try comp.gpa.dupe(u8, basename);
-
- comp.crt_files.putAssumeCapacityNoClobber(basename, .{
- .full_object_path = artifact_path,
- .lock = sub_compilation.bin_file.toOwnedLock(),
- });
-}
-
-pub const BuiltSharedObjects = struct {
- lock: Cache.Lock,
- dir_path: []u8,
-
- pub fn deinit(self: *BuiltSharedObjects, gpa: *Allocator) void {
- self.lock.release();
- gpa.free(self.dir_path);
- self.* = undefined;
- }
-};
-
-const all_map_basename = "all.map";
-
-// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented.
-// zig fmt: off
-
-pub fn buildSharedObjects(comp: *Compilation) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- if (!build_options.have_llvm) {
- return error.ZigCompilerNotBuiltWithLLVMExtensions;
- }
-
- var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
- defer arena_allocator.deinit();
- const arena = &arena_allocator.allocator;
-
- const target = comp.getTarget();
- const target_version = target.os.version_range.linux.glibc;
-
- // TODO use the global cache directory here
- var cache_parent: Cache = .{
- .gpa = comp.gpa,
- .manifest_dir = comp.cache_parent.manifest_dir,
- };
- var cache = cache_parent.obtain();
- defer cache.deinit();
- cache.hash.addBytes(build_options.version);
- cache.hash.addBytes(comp.zig_lib_directory.path orelse ".");
- cache.hash.add(target.cpu.arch);
- cache.hash.add(target.abi);
- cache.hash.add(target_version);
-
- const hit = try cache.hit();
- const digest = cache.final();
- const o_sub_path = try path.join(arena, &[_][]const u8{ "o", &digest });
-
- // Even if we get a hit, it doesn't guarantee that we finished the job last time.
- // We use the presence of an "ok" file to determine if it is a true hit.
-
- var o_directory: Compilation.Directory = .{
- .handle = try comp.zig_cache_directory.handle.makeOpenPath(o_sub_path, .{}),
- .path = try path.join(arena, &[_][]const u8{ comp.zig_cache_directory.path.?, o_sub_path }),
- };
- defer o_directory.handle.close();
-
- const ok_basename = "ok";
- const actual_hit = if (hit) blk: {
- o_directory.handle.access(ok_basename, .{}) catch |err| switch (err) {
- error.FileNotFound => break :blk false,
- else => |e| return e,
- };
- break :blk true;
- } else false;
-
- if (!actual_hit) {
- const metadata = try loadMetaData(comp.gpa, comp.zig_lib_directory.handle);
- defer metadata.destroy(comp.gpa);
-
- const ver_list_base = metadata.version_table.get(.{
- .arch = target.cpu.arch,
- .os = target.os.tag,
- .abi = target.abi,
- }) orelse return error.GLibCUnavailableForThisTarget;
- const target_ver_index = for (metadata.all_versions) |ver, i| {
- switch (ver.order(target_version)) {
- .eq => break i,
- .lt => continue,
- .gt => {
- // TODO Expose via compile error mechanism instead of log.
- std.log.warn("invalid target glibc version: {}", .{target_version});
- return error.InvalidTargetGLibCVersion;
- },
- }
- } else blk: {
- const latest_index = metadata.all_versions.len - 1;
- std.log.warn("zig cannot build new glibc version {}; providing instead {}", .{
- target_version, metadata.all_versions[latest_index],
- });
- break :blk latest_index;
- };
- {
- var map_contents = std.ArrayList(u8).init(arena);
- for (metadata.all_versions) |ver| {
- if (ver.patch == 0) {
- try map_contents.writer().print("GLIBC_{d}.{d} {{ }};\n", .{ ver.major, ver.minor });
- } else {
- try map_contents.writer().print("GLIBC_{d}.{d}.{d} {{ }};\n", .{ ver.major, ver.minor, ver.patch });
- }
- }
- try o_directory.handle.writeFile(all_map_basename, map_contents.items);
- map_contents.deinit(); // The most recent allocation of an arena can be freed :)
- }
- var zig_body = std.ArrayList(u8).init(comp.gpa);
- defer zig_body.deinit();
- for (libs) |*lib| {
- zig_body.shrinkRetainingCapacity(0);
-
- for (metadata.all_functions) |*libc_fn, fn_i| {
- if (libc_fn.lib != lib) continue;
-
- const ver_list = ver_list_base[fn_i];
- // Pick the default symbol version:
- // - If there are no versions, don't emit it
- // - Take the greatest one <= than the target one
- // - If none of them is <= than the
- // specified one don't pick any default version
- if (ver_list.len == 0) continue;
- var chosen_def_ver_index: u8 = 255;
- {
- var ver_i: u8 = 0;
- while (ver_i < ver_list.len) : (ver_i += 1) {
- const ver_index = ver_list.versions[ver_i];
- if ((chosen_def_ver_index == 255 or ver_index > chosen_def_ver_index) and
- target_ver_index >= ver_index)
- {
- chosen_def_ver_index = ver_index;
- }
- }
- }
- {
- var ver_i: u8 = 0;
- while (ver_i < ver_list.len) : (ver_i += 1) {
- // Example:
- // .globl _Exit_2_2_5
- // .type _Exit_2_2_5, @function;
- // .symver _Exit_2_2_5, _Exit@@GLIBC_2.2.5
- // .hidden _Exit_2_2_5
- // _Exit_2_2_5:
- const ver_index = ver_list.versions[ver_i];
- const ver = metadata.all_versions[ver_index];
- const sym_name = libc_fn.name;
- // Default symbol version definition vs normal symbol version definition
- const want_two_ats = chosen_def_ver_index != 255 and ver_index == chosen_def_ver_index;
- const at_sign_str = "@@"[0 .. @boolToInt(want_two_ats) + @as(usize, 1)];
-
- if (ver.patch == 0) {
- const sym_plus_ver = try std.fmt.allocPrint(
- arena, "{s}_{d}_{d}",
- .{sym_name, ver.major, ver.minor},
- );
- try zig_body.writer().print(
- \\.globl {s}
- \\.type {s}, @function;
- \\.symver {s}, {s}{s}GLIBC_{d}.{d}
- \\.hidden {s}
- \\{s}:
- \\
- , .{
- sym_plus_ver,
- sym_plus_ver,
- sym_plus_ver, sym_name, at_sign_str, ver.major, ver.minor,
- sym_plus_ver,
- sym_plus_ver,
- });
- } else {
- const sym_plus_ver = try std.fmt.allocPrint(arena, "{s}_{d}_{d}_{d}",
- .{sym_name, ver.major, ver.minor, ver.patch},
- );
- try zig_body.writer().print(
- \\.globl {s}
- \\.type {s}, @function;
- \\.symver {s}, {s}{s}GLIBC_{d}.{d}.{d}
- \\.hidden {s}
- \\{s}:
- \\
- , .{
- sym_plus_ver,
- sym_plus_ver,
- sym_plus_ver, sym_name, at_sign_str, ver.major, ver.minor, ver.patch,
- sym_plus_ver,
- sym_plus_ver,
- });
- }
- }
- }
- }
-
- var lib_name_buf: [32]u8 = undefined; // Larger than each of the names "c", "pthread", etc.
- const asm_file_basename = std.fmt.bufPrint(&lib_name_buf, "{s}.s", .{lib.name}) catch unreachable;
- try o_directory.handle.writeFile(asm_file_basename, zig_body.items);
-
- try buildSharedLib(comp, arena, comp.zig_cache_directory, o_directory, asm_file_basename, lib);
- }
- // No need to write the manifest because there are no file inputs associated with this cache hash.
- // However we do need to write the ok file now.
- if (o_directory.handle.createFile(ok_basename, .{})) |file| {
- file.close();
- } else |err| {
- std.log.warn("glibc shared objects: failed to mark completion: {}", .{@errorName(err)});
- }
- }
-
- assert(comp.glibc_so_files == null);
- comp.glibc_so_files = BuiltSharedObjects{
- .lock = cache.toOwnedLock(),
- .dir_path = try path.join(comp.gpa, &[_][]const u8{ comp.zig_cache_directory.path.?, o_sub_path }),
- };
-}
-
-// zig fmt: on
-
-fn buildSharedLib(
- comp: *Compilation,
- arena: *Allocator,
- zig_cache_directory: Compilation.Directory,
- bin_directory: Compilation.Directory,
- asm_file_basename: []const u8,
- lib: *const Lib,
-) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- const emit_bin = Compilation.EmitLoc{
- .directory = bin_directory,
- .basename = try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ lib.name, lib.sover }),
- };
- const version: std.builtin.Version = .{ .major = lib.sover, .minor = 0, .patch = 0 };
- const ld_basename = path.basename(comp.getTarget().standardDynamicLinkerPath().get().?);
- const override_soname = if (mem.eql(u8, lib.name, "ld")) ld_basename else null;
- const map_file_path = try path.join(arena, &[_][]const u8{ bin_directory.path.?, all_map_basename });
- const c_source_files = [1]Compilation.CSourceFile{
- .{
- .src_path = try path.join(arena, &[_][]const u8{ bin_directory.path.?, asm_file_basename }),
- },
- };
- const sub_compilation = try Compilation.create(comp.gpa, .{
- .zig_cache_directory = zig_cache_directory,
- .zig_lib_directory = comp.zig_lib_directory,
- .target = comp.getTarget(),
- .root_name = lib.name,
- .root_pkg = null,
- .output_mode = .Lib,
- .link_mode = .Dynamic,
- .rand = comp.rand,
- .libc_installation = comp.bin_file.options.libc_installation,
- .emit_bin = emit_bin,
- .optimize_mode = comp.bin_file.options.optimize_mode,
- .want_sanitize_c = false,
- .want_stack_check = false,
- .want_valgrind = false,
- .emit_h = null,
- .strip = comp.bin_file.options.strip,
- .is_native_os = false,
- .self_exe_path = comp.self_exe_path,
- .verbose_cc = comp.verbose_cc,
- .verbose_link = comp.bin_file.options.verbose_link,
- .verbose_tokenize = comp.verbose_tokenize,
- .verbose_ast = comp.verbose_ast,
- .verbose_ir = comp.verbose_ir,
- .verbose_llvm_ir = comp.verbose_llvm_ir,
- .verbose_cimport = comp.verbose_cimport,
- .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
- .clang_passthrough_mode = comp.clang_passthrough_mode,
- .version = version,
- .version_script = map_file_path,
- .override_soname = override_soname,
- .c_source_files = &c_source_files,
- });
- defer sub_compilation.destroy();
-
- try sub_compilation.updateSubCompilation();
-}
diff --git a/src-self-hosted/introspect.zig b/src-self-hosted/introspect.zig
deleted file mode 100644
index 067326ebb68a6473185970546ab1870e9a551bd9..0000000000000000000000000000000000000000
--- a/src-self-hosted/introspect.zig
+++ /dev/null
@@ -1,82 +0,0 @@
-const std = @import("std");
-const mem = std.mem;
-const fs = std.fs;
-const Compilation = @import("Compilation.zig");
-
-/// Returns the sub_path that worked, or `null` if none did.
-/// The path of the returned Directory is relative to `base`.
-/// The handle of the returned Directory is open.
-fn testZigInstallPrefix(base_dir: fs.Dir) ?Compilation.Directory {
- const test_index_file = "std" ++ fs.path.sep_str ++ "std.zig";
-
- zig_dir: {
- // Try lib/zig/std/std.zig
- const lib_zig = "lib" ++ fs.path.sep_str ++ "zig";
- var test_zig_dir = base_dir.openDir(lib_zig, .{}) catch break :zig_dir;
- const file = test_zig_dir.openFile(test_index_file, .{}) catch {
- test_zig_dir.close();
- break :zig_dir;
- };
- file.close();
- return Compilation.Directory{ .handle = test_zig_dir, .path = lib_zig };
- }
-
- // Try lib/std/std.zig
- var test_zig_dir = base_dir.openDir("lib", .{}) catch return null;
- const file = test_zig_dir.openFile(test_index_file, .{}) catch {
- test_zig_dir.close();
- return null;
- };
- file.close();
- return Compilation.Directory{ .handle = test_zig_dir, .path = "lib" };
-}
-
-/// Both the directory handle and the path are newly allocated resources which the caller now owns.
-pub fn findZigLibDir(gpa: *mem.Allocator) !Compilation.Directory {
- const self_exe_path = try fs.selfExePathAlloc(gpa);
- defer gpa.free(self_exe_path);
-
- return findZigLibDirFromSelfExe(gpa, self_exe_path);
-}
-
-/// Both the directory handle and the path are newly allocated resources which the caller now owns.
-pub fn findZigLibDirFromSelfExe(
- allocator: *mem.Allocator,
- self_exe_path: []const u8,
-) error{ OutOfMemory, FileNotFound }!Compilation.Directory {
- const cwd = fs.cwd();
- var cur_path: []const u8 = self_exe_path;
- while (fs.path.dirname(cur_path)) |dirname| : (cur_path = dirname) {
- var base_dir = cwd.openDir(dirname, .{}) catch continue;
- defer base_dir.close();
-
- const sub_directory = testZigInstallPrefix(base_dir) orelse continue;
- return Compilation.Directory{
- .handle = sub_directory.handle,
- .path = try fs.path.join(allocator, &[_][]const u8{ dirname, sub_directory.path.? }),
- };
- }
- return error.FileNotFound;
-}
-
-/// Caller owns returned memory.
-pub fn resolveGlobalCacheDir(allocator: *mem.Allocator) ![]u8 {
- const appname = "zig";
-
- if (std.Target.current.os.tag != .windows) {
- if (std.os.getenv("XDG_CACHE_HOME")) |cache_root| {
- return fs.path.join(allocator, &[_][]const u8{ cache_root, appname });
- } else if (std.os.getenv("HOME")) |home| {
- return fs.path.join(allocator, &[_][]const u8{ home, ".cache", appname });
- }
- }
-
- return fs.getAppDataDir(allocator, appname);
-}
-
-pub fn openGlobalCacheDir() !fs.Dir {
- var buf: [fs.MAX_PATH_BYTES]u8 = undefined;
- var fba = std.heap.FixedBufferAllocator.init(&buf);
- const path_name = try resolveGlobalCacheDir(&fba.allocator);
- return fs.cwd().makeOpenPath(path_name, .{});
-}
diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig
deleted file mode 100644
index 26afa52929e38592d1fd381cea25acc16115c315..0000000000000000000000000000000000000000
--- a/src-self-hosted/ir.zig
+++ /dev/null
@@ -1,465 +0,0 @@
-const std = @import("std");
-const Value = @import("value.zig").Value;
-const Type = @import("type.zig").Type;
-const Module = @import("Module.zig");
-const assert = std.debug.assert;
-const codegen = @import("codegen.zig");
-const ast = std.zig.ast;
-
-/// These are in-memory, analyzed instructions. See `zir.Inst` for the representation
-/// of instructions that correspond to the ZIR text format.
-/// This struct owns the `Value` and `Type` memory. When the struct is deallocated,
-/// so are the `Value` and `Type`. The value of a constant must be copied into
-/// a memory location for the value to survive after a const instruction.
-pub const Inst = struct {
- tag: Tag,
- /// Each bit represents the index of an `Inst` parameter in the `args` field.
- /// If a bit is set, it marks the end of the lifetime of the corresponding
- /// instruction parameter. For example, 0b101 means that the first and
- /// third `Inst` parameters' lifetimes end after this instruction, and will
- /// not have any more following references.
- /// The most significant bit being set means that the instruction itself is
- /// never referenced, in other words its lifetime ends as soon as it finishes.
- /// If bit 15 (0b1xxx_xxxx_xxxx_xxxx) is set, it means this instruction itself is unreferenced.
- /// If bit 14 (0bx1xx_xxxx_xxxx_xxxx) is set, it means this is a special case and the
- /// lifetimes of operands are encoded elsewhere.
- deaths: DeathsInt = undefined,
- ty: Type,
- /// Byte offset into the source.
- src: usize,
-
- pub const DeathsInt = u16;
- pub const DeathsBitIndex = std.math.Log2Int(DeathsInt);
- pub const unreferenced_bit_index = @typeInfo(DeathsInt).Int.bits - 1;
- pub const deaths_bits = unreferenced_bit_index - 1;
-
- pub fn isUnused(self: Inst) bool {
- return (self.deaths & (1 << unreferenced_bit_index)) != 0;
- }
-
- pub fn operandDies(self: Inst, index: DeathsBitIndex) bool {
- assert(index < deaths_bits);
- return @truncate(u1, self.deaths >> index) != 0;
- }
-
- pub fn clearOperandDeath(self: *Inst, index: DeathsBitIndex) void {
- assert(index < deaths_bits);
- self.deaths &= ~(@as(DeathsInt, 1) << index);
- }
-
- pub fn specialOperandDeaths(self: Inst) bool {
- return (self.deaths & (1 << deaths_bits)) != 0;
- }
-
- pub const Tag = enum {
- add,
- alloc,
- arg,
- assembly,
- bitcast,
- block,
- br,
- breakpoint,
- brvoid,
- call,
- cmp_lt,
- cmp_lte,
- cmp_eq,
- cmp_gte,
- cmp_gt,
- cmp_neq,
- condbr,
- constant,
- dbg_stmt,
- isnonnull,
- isnull,
- iserr,
- /// Read a value from a pointer.
- load,
- loop,
- ptrtoint,
- ref,
- ret,
- retvoid,
- varptr,
- /// Write a value to a pointer. LHS is pointer, RHS is value.
- store,
- sub,
- unreach,
- not,
- floatcast,
- intcast,
- unwrap_optional,
- wrap_optional,
-
- pub fn Type(tag: Tag) type {
- return switch (tag) {
- .alloc,
- .retvoid,
- .unreach,
- .breakpoint,
- .dbg_stmt,
- => NoOp,
-
- .ref,
- .ret,
- .bitcast,
- .not,
- .isnonnull,
- .isnull,
- .iserr,
- .ptrtoint,
- .floatcast,
- .intcast,
- .load,
- .unwrap_optional,
- .wrap_optional,
- => UnOp,
-
- .add,
- .sub,
- .cmp_lt,
- .cmp_lte,
- .cmp_eq,
- .cmp_gte,
- .cmp_gt,
- .cmp_neq,
- .store,
- => BinOp,
-
- .arg => Arg,
- .assembly => Assembly,
- .block => Block,
- .br => Br,
- .brvoid => BrVoid,
- .call => Call,
- .condbr => CondBr,
- .constant => Constant,
- .loop => Loop,
- .varptr => VarPtr,
- };
- }
-
- pub fn fromCmpOp(op: std.math.CompareOperator) Tag {
- return switch (op) {
- .lt => .cmp_lt,
- .lte => .cmp_lte,
- .eq => .cmp_eq,
- .gte => .cmp_gte,
- .gt => .cmp_gt,
- .neq => .cmp_neq,
- };
- }
- };
-
- /// Prefer `castTag` to this.
- pub fn cast(base: *Inst, comptime T: type) ?*T {
- if (@hasField(T, "base_tag")) {
- return base.castTag(T.base_tag);
- }
- inline for (@typeInfo(Tag).Enum.fields) |field| {
- const tag = @intToEnum(Tag, field.value);
- if (base.tag == tag) {
- if (T == tag.Type()) {
- return @fieldParentPtr(T, "base", base);
- }
- return null;
- }
- }
- unreachable;
- }
-
- pub fn castTag(base: *Inst, comptime tag: Tag) ?*tag.Type() {
- if (base.tag == tag) {
- return @fieldParentPtr(tag.Type(), "base", base);
- }
- return null;
- }
-
- pub fn Args(comptime T: type) type {
- return std.meta.fieldInfo(T, "args").field_type;
- }
-
- /// Returns `null` if runtime-known.
- pub fn value(base: *Inst) ?Value {
- if (base.ty.onePossibleValue()) |opv| return opv;
-
- const inst = base.cast(Constant) orelse return null;
- return inst.val;
- }
-
- pub fn cmpOperator(base: *Inst) ?std.math.CompareOperator {
- return switch (base.tag) {
- .cmp_lt => .lt,
- .cmp_lte => .lte,
- .cmp_eq => .eq,
- .cmp_gte => .gte,
- .cmp_gt => .gt,
- .cmp_neq => .neq,
- else => null,
- };
- }
-
- pub fn operandCount(base: *Inst) usize {
- inline for (@typeInfo(Tag).Enum.fields) |field| {
- const tag = @intToEnum(Tag, field.value);
- if (tag == base.tag) {
- return @fieldParentPtr(tag.Type(), "base", base).operandCount();
- }
- }
- unreachable;
- }
-
- pub fn getOperand(base: *Inst, index: usize) ?*Inst {
- inline for (@typeInfo(Tag).Enum.fields) |field| {
- const tag = @intToEnum(Tag, field.value);
- if (tag == base.tag) {
- return @fieldParentPtr(tag.Type(), "base", base).getOperand(index);
- }
- }
- unreachable;
- }
-
- pub fn breakBlock(base: *Inst) ?*Block {
- return switch (base.tag) {
- .br => base.castTag(.br).?.block,
- .brvoid => base.castTag(.brvoid).?.block,
- else => null,
- };
- }
-
- pub const NoOp = struct {
- base: Inst,
-
- pub fn operandCount(self: *const NoOp) usize {
- return 0;
- }
- pub fn getOperand(self: *const NoOp, index: usize) ?*Inst {
- return null;
- }
- };
-
- pub const UnOp = struct {
- base: Inst,
- operand: *Inst,
-
- pub fn operandCount(self: *const UnOp) usize {
- return 1;
- }
- pub fn getOperand(self: *const UnOp, index: usize) ?*Inst {
- if (index == 0)
- return self.operand;
- return null;
- }
- };
-
- pub const BinOp = struct {
- base: Inst,
- lhs: *Inst,
- rhs: *Inst,
-
- pub fn operandCount(self: *const BinOp) usize {
- return 2;
- }
- pub fn getOperand(self: *const BinOp, index: usize) ?*Inst {
- var i = index;
-
- if (i < 1)
- return self.lhs;
- i -= 1;
-
- if (i < 1)
- return self.rhs;
- i -= 1;
-
- return null;
- }
- };
-
- pub const Arg = struct {
- pub const base_tag = Tag.arg;
-
- base: Inst,
- name: [*:0]const u8,
-
- pub fn operandCount(self: *const Arg) usize {
- return 0;
- }
- pub fn getOperand(self: *const Arg, index: usize) ?*Inst {
- return null;
- }
- };
-
- pub const Assembly = struct {
- pub const base_tag = Tag.assembly;
-
- base: Inst,
- asm_source: []const u8,
- is_volatile: bool,
- output: ?[]const u8,
- inputs: []const []const u8,
- clobbers: []const []const u8,
- args: []const *Inst,
-
- pub fn operandCount(self: *const Assembly) usize {
- return self.args.len;
- }
- pub fn getOperand(self: *const Assembly, index: usize) ?*Inst {
- if (index < self.args.len)
- return self.args[index];
- return null;
- }
- };
-
- pub const Block = struct {
- pub const base_tag = Tag.block;
-
- base: Inst,
- body: Body,
- /// This memory is reserved for codegen code to do whatever it needs to here.
- codegen: codegen.BlockData = .{},
-
- pub fn operandCount(self: *const Block) usize {
- return 0;
- }
- pub fn getOperand(self: *const Block, index: usize) ?*Inst {
- return null;
- }
- };
-
- pub const Br = struct {
- pub const base_tag = Tag.br;
-
- base: Inst,
- block: *Block,
- operand: *Inst,
-
- pub fn operandCount(self: *const Br) usize {
- return 0;
- }
- pub fn getOperand(self: *const Br, index: usize) ?*Inst {
- if (index == 0)
- return self.operand;
- return null;
- }
- };
-
- pub const BrVoid = struct {
- pub const base_tag = Tag.brvoid;
-
- base: Inst,
- block: *Block,
-
- pub fn operandCount(self: *const BrVoid) usize {
- return 0;
- }
- pub fn getOperand(self: *const BrVoid, index: usize) ?*Inst {
- return null;
- }
- };
-
- pub const Call = struct {
- pub const base_tag = Tag.call;
-
- base: Inst,
- func: *Inst,
- args: []const *Inst,
-
- pub fn operandCount(self: *const Call) usize {
- return self.args.len + 1;
- }
- pub fn getOperand(self: *const Call, index: usize) ?*Inst {
- var i = index;
-
- if (i < 1)
- return self.func;
- i -= 1;
-
- if (i < self.args.len)
- return self.args[i];
- i -= self.args.len;
-
- return null;
- }
- };
-
- pub const CondBr = struct {
- pub const base_tag = Tag.condbr;
-
- base: Inst,
- condition: *Inst,
- then_body: Body,
- else_body: Body,
- /// Set of instructions whose lifetimes end at the start of one of the branches.
- /// The `then` branch is first: `deaths[0..then_death_count]`.
- /// The `else` branch is next: `(deaths + then_death_count)[0..else_death_count]`.
- deaths: [*]*Inst = undefined,
- then_death_count: u32 = 0,
- else_death_count: u32 = 0,
-
- pub fn operandCount(self: *const CondBr) usize {
- return 1;
- }
- pub fn getOperand(self: *const CondBr, index: usize) ?*Inst {
- var i = index;
-
- if (i < 1)
- return self.condition;
- i -= 1;
-
- return null;
- }
- pub fn thenDeaths(self: *const CondBr) []*Inst {
- return self.deaths[0..self.then_death_count];
- }
- pub fn elseDeaths(self: *const CondBr) []*Inst {
- return (self.deaths + self.then_death_count)[0..self.else_death_count];
- }
- };
-
- pub const Constant = struct {
- pub const base_tag = Tag.constant;
-
- base: Inst,
- val: Value,
-
- pub fn operandCount(self: *const Constant) usize {
- return 0;
- }
- pub fn getOperand(self: *const Constant, index: usize) ?*Inst {
- return null;
- }
- };
-
- pub const Loop = struct {
- pub const base_tag = Tag.loop;
-
- base: Inst,
- body: Body,
-
- pub fn operandCount(self: *const Loop) usize {
- return 0;
- }
- pub fn getOperand(self: *const Loop, index: usize) ?*Inst {
- return null;
- }
- };
-
- pub const VarPtr = struct {
- pub const base_tag = Tag.varptr;
-
- base: Inst,
- variable: *Module.Var,
-
- pub fn operandCount(self: *const VarPtr) usize {
- return 0;
- }
- pub fn getOperand(self: *const VarPtr, index: usize) ?*Inst {
- return null;
- }
- };
-};
-
-pub const Body = struct {
- instructions: []*Inst,
-};
diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig
deleted file mode 100644
index 535892ce745b52b4632ace9683ded03d48544545..0000000000000000000000000000000000000000
--- a/src-self-hosted/libc_installation.zig
+++ /dev/null
@@ -1,629 +0,0 @@
-const std = @import("std");
-const builtin = @import("builtin");
-const Target = std.Target;
-const fs = std.fs;
-const Allocator = std.mem.Allocator;
-const Batch = std.event.Batch;
-const build_options = @import("build_options");
-
-const is_darwin = Target.current.isDarwin();
-const is_windows = Target.current.os.tag == .windows;
-const is_gnu = Target.current.isGnu();
-
-const log = std.log.scoped(.libc_installation);
-
-usingnamespace @import("windows_sdk.zig");
-
-// TODO https://github.com/ziglang/zig/issues/6345
-
-/// See the render function implementation for documentation of the fields.
-pub const LibCInstallation = struct {
- include_dir: ?[]const u8 = null,
- sys_include_dir: ?[]const u8 = null,
- crt_dir: ?[]const u8 = null,
- msvc_lib_dir: ?[]const u8 = null,
- kernel32_lib_dir: ?[]const u8 = null,
-
- pub const FindError = error{
- OutOfMemory,
- FileSystem,
- UnableToSpawnCCompiler,
- CCompilerExitCode,
- CCompilerCrashed,
- CCompilerCannotFindHeaders,
- LibCRuntimeNotFound,
- LibCStdLibHeaderNotFound,
- LibCKernel32LibNotFound,
- UnsupportedArchitecture,
- WindowsSdkNotFound,
- ZigIsTheCCompiler,
- };
-
- pub fn parse(
- allocator: *Allocator,
- libc_file: []const u8,
- ) !LibCInstallation {
- var self: LibCInstallation = .{};
-
- const fields = std.meta.fields(LibCInstallation);
- const FoundKey = struct {
- found: bool,
- allocated: ?[:0]u8,
- };
- var found_keys = [1]FoundKey{FoundKey{ .found = false, .allocated = null }} ** fields.len;
- errdefer {
- self = .{};
- for (found_keys) |found_key| {
- if (found_key.allocated) |s| allocator.free(s);
- }
- }
-
- const contents = try std.fs.cwd().readFileAlloc(allocator, libc_file, std.math.maxInt(usize));
- defer allocator.free(contents);
-
- var it = std.mem.tokenize(contents, "\n");
- while (it.next()) |line| {
- if (line.len == 0 or line[0] == '#') continue;
- var line_it = std.mem.split(line, "=");
- const name = line_it.next() orelse {
- log.err("missing equal sign after field name\n", .{});
- return error.ParseError;
- };
- const value = line_it.rest();
- inline for (fields) |field, i| {
- if (std.mem.eql(u8, name, field.name)) {
- found_keys[i].found = true;
- if (value.len == 0) {
- @field(self, field.name) = null;
- } else {
- found_keys[i].allocated = try std.mem.dupeZ(allocator, u8, value);
- @field(self, field.name) = found_keys[i].allocated;
- }
- break;
- }
- }
- }
- inline for (fields) |field, i| {
- if (!found_keys[i].found) {
- log.err("missing field: {}\n", .{field.name});
- return error.ParseError;
- }
- }
- if (self.include_dir == null) {
- log.err("include_dir may not be empty\n", .{});
- return error.ParseError;
- }
- if (self.sys_include_dir == null) {
- log.err("sys_include_dir may not be empty\n", .{});
- return error.ParseError;
- }
- if (self.crt_dir == null and !is_darwin) {
- log.err("crt_dir may not be empty for {}\n", .{@tagName(Target.current.os.tag)});
- return error.ParseError;
- }
- if (self.msvc_lib_dir == null and is_windows and !is_gnu) {
- log.err("msvc_lib_dir may not be empty for {}-{}\n", .{
- @tagName(Target.current.os.tag),
- @tagName(Target.current.abi),
- });
- return error.ParseError;
- }
- if (self.kernel32_lib_dir == null and is_windows and !is_gnu) {
- log.err("kernel32_lib_dir may not be empty for {}-{}\n", .{
- @tagName(Target.current.os.tag),
- @tagName(Target.current.abi),
- });
- return error.ParseError;
- }
-
- return self;
- }
-
- pub fn render(self: LibCInstallation, out: anytype) !void {
- @setEvalBranchQuota(4000);
- const include_dir = self.include_dir orelse "";
- const sys_include_dir = self.sys_include_dir orelse "";
- const crt_dir = self.crt_dir orelse "";
- const msvc_lib_dir = self.msvc_lib_dir orelse "";
- const kernel32_lib_dir = self.kernel32_lib_dir orelse "";
-
- try out.print(
- \\# The directory that contains `stdlib.h`.
- \\# On POSIX-like systems, include directories be found with: `cc -E -Wp,-v -xc /dev/null`
- \\include_dir={}
- \\
- \\# The system-specific include directory. May be the same as `include_dir`.
- \\# On Windows it's the directory that includes `vcruntime.h`.
- \\# On POSIX it's the directory that includes `sys/errno.h`.
- \\sys_include_dir={}
- \\
- \\# The directory that contains `crt1.o` or `crt2.o`.
- \\# On POSIX, can be found with `cc -print-file-name=crt1.o`.
- \\# Not needed when targeting MacOS.
- \\crt_dir={}
- \\
- \\# The directory that contains `vcruntime.lib`.
- \\# Only needed when targeting MSVC on Windows.
- \\msvc_lib_dir={}
- \\
- \\# The directory that contains `kernel32.lib`.
- \\# Only needed when targeting MSVC on Windows.
- \\kernel32_lib_dir={}
- \\
- , .{
- include_dir,
- sys_include_dir,
- crt_dir,
- msvc_lib_dir,
- kernel32_lib_dir,
- });
- }
-
- pub const FindNativeOptions = struct {
- allocator: *Allocator,
-
- /// If enabled, will print human-friendly errors to stderr.
- verbose: bool = false,
- };
-
- /// Finds the default, native libc.
- pub fn findNative(args: FindNativeOptions) FindError!LibCInstallation {
- var self: LibCInstallation = .{};
-
- if (is_windows) {
- if (!build_options.have_llvm)
- return error.WindowsSdkNotFound;
- var sdk: *ZigWindowsSDK = undefined;
- switch (zig_find_windows_sdk(&sdk)) {
- .None => {
- defer zig_free_windows_sdk(sdk);
-
- var batch = Batch(FindError!void, 5, .auto_async).init();
- batch.add(&async self.findNativeMsvcIncludeDir(args, sdk));
- batch.add(&async self.findNativeMsvcLibDir(args, sdk));
- batch.add(&async self.findNativeKernel32LibDir(args, sdk));
- batch.add(&async self.findNativeIncludeDirWindows(args, sdk));
- batch.add(&async self.findNativeCrtDirWindows(args, sdk));
- try batch.wait();
- },
- .OutOfMemory => return error.OutOfMemory,
- .NotFound => return error.WindowsSdkNotFound,
- .PathTooLong => return error.WindowsSdkNotFound,
- }
- } else {
- try blk: {
- var batch = Batch(FindError!void, 2, .auto_async).init();
- errdefer batch.wait() catch {};
- batch.add(&async self.findNativeIncludeDirPosix(args));
- switch (Target.current.os.tag) {
- .freebsd, .netbsd => self.crt_dir = try std.mem.dupeZ(args.allocator, u8, "/usr/lib"),
- .linux, .dragonfly => batch.add(&async self.findNativeCrtDirPosix(args)),
- else => {},
- }
- break :blk batch.wait();
- };
- }
- return self;
- }
-
- /// Must be the same allocator passed to `parse` or `findNative`.
- pub fn deinit(self: *LibCInstallation, allocator: *Allocator) void {
- const fields = std.meta.fields(LibCInstallation);
- inline for (fields) |field| {
- if (@field(self, field.name)) |payload| {
- allocator.free(payload);
- }
- }
- self.* = undefined;
- }
-
- fn findNativeIncludeDirPosix(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
- const allocator = args.allocator;
- const dev_null = if (is_windows) "nul" else "/dev/null";
- const cc_exe = std.os.getenvZ("CC") orelse default_cc_exe;
- const argv = [_][]const u8{
- cc_exe,
- "-E",
- "-Wp,-v",
- "-xc",
- dev_null,
- };
- var env_map = try std.process.getEnvMap(allocator);
- defer env_map.deinit();
-
- // Detect infinite loops.
- const inf_loop_env_key = "ZIG_IS_DETECTING_LIBC_PATHS";
- if (env_map.get(inf_loop_env_key) != null) return error.ZigIsTheCCompiler;
- try env_map.set(inf_loop_env_key, "1");
-
- const exec_res = std.ChildProcess.exec(.{
- .allocator = allocator,
- .argv = &argv,
- .max_output_bytes = 1024 * 1024,
- .env_map = &env_map,
- // Some C compilers, such as Clang, are known to rely on argv[0] to find the path
- // to their own executable, without even bothering to resolve PATH. This results in the message:
- // error: unable to execute command: Executable "" doesn't exist!
- // So we use the expandArg0 variant of ChildProcess to give them a helping hand.
- .expand_arg0 = .expand,
- }) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- else => {
- printVerboseInvocation(&argv, null, args.verbose, null);
- return error.UnableToSpawnCCompiler;
- },
- };
- defer {
- allocator.free(exec_res.stdout);
- allocator.free(exec_res.stderr);
- }
- switch (exec_res.term) {
- .Exited => |code| if (code != 0) {
- printVerboseInvocation(&argv, null, args.verbose, exec_res.stderr);
- return error.CCompilerExitCode;
- },
- else => {
- printVerboseInvocation(&argv, null, args.verbose, exec_res.stderr);
- return error.CCompilerCrashed;
- },
- }
-
- var it = std.mem.tokenize(exec_res.stderr, "\n\r");
- var search_paths = std.ArrayList([]const u8).init(allocator);
- defer search_paths.deinit();
- while (it.next()) |line| {
- if (line.len != 0 and line[0] == ' ') {
- try search_paths.append(line);
- }
- }
- if (search_paths.items.len == 0) {
- return error.CCompilerCannotFindHeaders;
- }
-
- const include_dir_example_file = "stdlib.h";
- const sys_include_dir_example_file = if (is_windows) "sys\\types.h" else "sys/errno.h";
-
- var path_i: usize = 0;
- while (path_i < search_paths.items.len) : (path_i += 1) {
- // search in reverse order
- const search_path_untrimmed = search_paths.items[search_paths.items.len - path_i - 1];
- const search_path = std.mem.trimLeft(u8, search_path_untrimmed, " ");
- var search_dir = fs.cwd().openDir(search_path, .{}) catch |err| switch (err) {
- error.FileNotFound,
- error.NotDir,
- error.NoDevice,
- => continue,
-
- else => return error.FileSystem,
- };
- defer search_dir.close();
-
- if (self.include_dir == null) {
- if (search_dir.accessZ(include_dir_example_file, .{})) |_| {
- self.include_dir = try std.mem.dupeZ(allocator, u8, search_path);
- } else |err| switch (err) {
- error.FileNotFound => {},
- else => return error.FileSystem,
- }
- }
-
- if (self.sys_include_dir == null) {
- if (search_dir.accessZ(sys_include_dir_example_file, .{})) |_| {
- self.sys_include_dir = try std.mem.dupeZ(allocator, u8, search_path);
- } else |err| switch (err) {
- error.FileNotFound => {},
- else => return error.FileSystem,
- }
- }
-
- if (self.include_dir != null and self.sys_include_dir != null) {
- // Success.
- return;
- }
- }
-
- return error.LibCStdLibHeaderNotFound;
- }
-
- fn findNativeIncludeDirWindows(
- self: *LibCInstallation,
- args: FindNativeOptions,
- sdk: *ZigWindowsSDK,
- ) FindError!void {
- const allocator = args.allocator;
-
- var search_buf: [2]Search = undefined;
- const searches = fillSearch(&search_buf, sdk);
-
- var result_buf = std.ArrayList(u8).init(allocator);
- defer result_buf.deinit();
-
- for (searches) |search| {
- result_buf.shrink(0);
- try result_buf.outStream().print("{}\\Include\\{}\\ucrt", .{ search.path, search.version });
-
- var dir = fs.cwd().openDir(result_buf.span(), .{}) catch |err| switch (err) {
- error.FileNotFound,
- error.NotDir,
- error.NoDevice,
- => continue,
-
- else => return error.FileSystem,
- };
- defer dir.close();
-
- dir.accessZ("stdlib.h", .{}) catch |err| switch (err) {
- error.FileNotFound => continue,
- else => return error.FileSystem,
- };
-
- self.include_dir = result_buf.toOwnedSlice();
- return;
- }
-
- return error.LibCStdLibHeaderNotFound;
- }
-
- fn findNativeCrtDirWindows(
- self: *LibCInstallation,
- args: FindNativeOptions,
- sdk: *ZigWindowsSDK,
- ) FindError!void {
- const allocator = args.allocator;
-
- var search_buf: [2]Search = undefined;
- const searches = fillSearch(&search_buf, sdk);
-
- var result_buf = std.ArrayList(u8).init(allocator);
- defer result_buf.deinit();
-
- const arch_sub_dir = switch (builtin.arch) {
- .i386 => "x86",
- .x86_64 => "x64",
- .arm, .armeb => "arm",
- else => return error.UnsupportedArchitecture,
- };
-
- for (searches) |search| {
- result_buf.shrink(0);
- try result_buf.outStream().print("{}\\Lib\\{}\\ucrt\\{}", .{ search.path, search.version, arch_sub_dir });
-
- var dir = fs.cwd().openDir(result_buf.span(), .{}) catch |err| switch (err) {
- error.FileNotFound,
- error.NotDir,
- error.NoDevice,
- => continue,
-
- else => return error.FileSystem,
- };
- defer dir.close();
-
- dir.accessZ("ucrt.lib", .{}) catch |err| switch (err) {
- error.FileNotFound => continue,
- else => return error.FileSystem,
- };
-
- self.crt_dir = result_buf.toOwnedSlice();
- return;
- }
- return error.LibCRuntimeNotFound;
- }
-
- fn findNativeCrtDirPosix(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
- self.crt_dir = try ccPrintFileName(.{
- .allocator = args.allocator,
- .search_basename = "crt1.o",
- .want_dirname = .only_dir,
- .verbose = args.verbose,
- });
- }
-
- fn findNativeKernel32LibDir(
- self: *LibCInstallation,
- args: FindNativeOptions,
- sdk: *ZigWindowsSDK,
- ) FindError!void {
- const allocator = args.allocator;
-
- var search_buf: [2]Search = undefined;
- const searches = fillSearch(&search_buf, sdk);
-
- var result_buf = std.ArrayList(u8).init(allocator);
- defer result_buf.deinit();
-
- const arch_sub_dir = switch (builtin.arch) {
- .i386 => "x86",
- .x86_64 => "x64",
- .arm, .armeb => "arm",
- else => return error.UnsupportedArchitecture,
- };
-
- for (searches) |search| {
- result_buf.shrink(0);
- const stream = result_buf.outStream();
- try stream.print("{}\\Lib\\{}\\um\\{}", .{ search.path, search.version, arch_sub_dir });
-
- var dir = fs.cwd().openDir(result_buf.span(), .{}) catch |err| switch (err) {
- error.FileNotFound,
- error.NotDir,
- error.NoDevice,
- => continue,
-
- else => return error.FileSystem,
- };
- defer dir.close();
-
- dir.accessZ("kernel32.lib", .{}) catch |err| switch (err) {
- error.FileNotFound => continue,
- else => return error.FileSystem,
- };
-
- self.kernel32_lib_dir = result_buf.toOwnedSlice();
- return;
- }
- return error.LibCKernel32LibNotFound;
- }
-
- fn findNativeMsvcIncludeDir(
- self: *LibCInstallation,
- args: FindNativeOptions,
- sdk: *ZigWindowsSDK,
- ) FindError!void {
- const allocator = args.allocator;
-
- const msvc_lib_dir_ptr = sdk.msvc_lib_dir_ptr orelse return error.LibCStdLibHeaderNotFound;
- const msvc_lib_dir = msvc_lib_dir_ptr[0..sdk.msvc_lib_dir_len];
- const up1 = fs.path.dirname(msvc_lib_dir) orelse return error.LibCStdLibHeaderNotFound;
- const up2 = fs.path.dirname(up1) orelse return error.LibCStdLibHeaderNotFound;
-
- const dir_path = try fs.path.join(allocator, &[_][]const u8{ up2, "include" });
- errdefer allocator.free(dir_path);
-
- var dir = fs.cwd().openDir(dir_path, .{}) catch |err| switch (err) {
- error.FileNotFound,
- error.NotDir,
- error.NoDevice,
- => return error.LibCStdLibHeaderNotFound,
-
- else => return error.FileSystem,
- };
- defer dir.close();
-
- dir.accessZ("vcruntime.h", .{}) catch |err| switch (err) {
- error.FileNotFound => return error.LibCStdLibHeaderNotFound,
- else => return error.FileSystem,
- };
-
- self.sys_include_dir = dir_path;
- }
-
- fn findNativeMsvcLibDir(
- self: *LibCInstallation,
- args: FindNativeOptions,
- sdk: *ZigWindowsSDK,
- ) FindError!void {
- const allocator = args.allocator;
- const msvc_lib_dir_ptr = sdk.msvc_lib_dir_ptr orelse return error.LibCRuntimeNotFound;
- self.msvc_lib_dir = try std.mem.dupeZ(allocator, u8, msvc_lib_dir_ptr[0..sdk.msvc_lib_dir_len]);
- }
-};
-
-const default_cc_exe = if (is_windows) "cc.exe" else "cc";
-
-pub const CCPrintFileNameOptions = struct {
- allocator: *Allocator,
- search_basename: []const u8,
- want_dirname: enum { full_path, only_dir },
- verbose: bool = false,
-};
-
-/// caller owns returned memory
-fn ccPrintFileName(args: CCPrintFileNameOptions) ![:0]u8 {
- const allocator = args.allocator;
-
- const cc_exe = std.os.getenvZ("CC") orelse default_cc_exe;
- const arg1 = try std.fmt.allocPrint(allocator, "-print-file-name={}", .{args.search_basename});
- defer allocator.free(arg1);
- const argv = [_][]const u8{ cc_exe, arg1 };
-
- var env_map = try std.process.getEnvMap(allocator);
- defer env_map.deinit();
-
- // Detect infinite loops.
- const inf_loop_env_key = "ZIG_IS_DETECTING_LIBC_PATHS";
- if (env_map.get(inf_loop_env_key) != null) return error.ZigIsTheCCompiler;
- try env_map.set(inf_loop_env_key, "1");
-
- const exec_res = std.ChildProcess.exec(.{
- .allocator = allocator,
- .argv = &argv,
- .max_output_bytes = 1024 * 1024,
- .env_map = &env_map,
- // Some C compilers, such as Clang, are known to rely on argv[0] to find the path
- // to their own executable, without even bothering to resolve PATH. This results in the message:
- // error: unable to execute command: Executable "" doesn't exist!
- // So we use the expandArg0 variant of ChildProcess to give them a helping hand.
- .expand_arg0 = .expand,
- }) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- else => return error.UnableToSpawnCCompiler,
- };
- defer {
- allocator.free(exec_res.stdout);
- allocator.free(exec_res.stderr);
- }
- switch (exec_res.term) {
- .Exited => |code| if (code != 0) {
- printVerboseInvocation(&argv, args.search_basename, args.verbose, exec_res.stderr);
- return error.CCompilerExitCode;
- },
- else => {
- printVerboseInvocation(&argv, args.search_basename, args.verbose, exec_res.stderr);
- return error.CCompilerCrashed;
- },
- }
-
- var it = std.mem.tokenize(exec_res.stdout, "\n\r");
- const line = it.next() orelse return error.LibCRuntimeNotFound;
- // When this command fails, it returns exit code 0 and duplicates the input file name.
- // So we detect failure by checking if the output matches exactly the input.
- if (std.mem.eql(u8, line, args.search_basename)) return error.LibCRuntimeNotFound;
- switch (args.want_dirname) {
- .full_path => return std.mem.dupeZ(allocator, u8, line),
- .only_dir => {
- const dirname = fs.path.dirname(line) orelse return error.LibCRuntimeNotFound;
- return std.mem.dupeZ(allocator, u8, dirname);
- },
- }
-}
-
-fn printVerboseInvocation(
- argv: []const []const u8,
- search_basename: ?[]const u8,
- verbose: bool,
- stderr: ?[]const u8,
-) void {
- if (!verbose) return;
-
- if (search_basename) |s| {
- std.debug.warn("Zig attempted to find the file '{}' by executing this command:\n", .{s});
- } else {
- std.debug.warn("Zig attempted to find the path to native system libc headers by executing this command:\n", .{});
- }
- for (argv) |arg, i| {
- if (i != 0) std.debug.warn(" ", .{});
- std.debug.warn("{}", .{arg});
- }
- std.debug.warn("\n", .{});
- if (stderr) |s| {
- std.debug.warn("Output:\n==========\n{}\n==========\n", .{s});
- }
-}
-
-const Search = struct {
- path: []const u8,
- version: []const u8,
-};
-
-fn fillSearch(search_buf: *[2]Search, sdk: *ZigWindowsSDK) []Search {
- var search_end: usize = 0;
- if (sdk.path10_ptr) |path10_ptr| {
- if (sdk.version10_ptr) |version10_ptr| {
- search_buf[search_end] = Search{
- .path = path10_ptr[0..sdk.path10_len],
- .version = version10_ptr[0..sdk.version10_len],
- };
- search_end += 1;
- }
- }
- if (sdk.path81_ptr) |path81_ptr| {
- if (sdk.version81_ptr) |version81_ptr| {
- search_buf[search_end] = Search{
- .path = path81_ptr[0..sdk.path81_len],
- .version = version81_ptr[0..sdk.version81_len],
- };
- search_end += 1;
- }
- }
- return search_buf[0..search_end];
-}
diff --git a/src-self-hosted/libcxx.zig b/src-self-hosted/libcxx.zig
deleted file mode 100644
index c7dc24ae9fd0e09a89ffc1c1765776a02817dd5a..0000000000000000000000000000000000000000
--- a/src-self-hosted/libcxx.zig
+++ /dev/null
@@ -1,66 +0,0 @@
-//! TODO build libcxx and libcxxabi from source
-
-pub const libcxxabi_files = [_][]const u8{
- "src/abort_message.cpp",
- "src/cxa_aux_runtime.cpp",
- "src/cxa_default_handlers.cpp",
- "src/cxa_demangle.cpp",
- "src/cxa_exception.cpp",
- "src/cxa_exception_storage.cpp",
- "src/cxa_guard.cpp",
- "src/cxa_handlers.cpp",
- "src/cxa_noexception.cpp",
- "src/cxa_personality.cpp",
- "src/cxa_thread_atexit.cpp",
- "src/cxa_unexpected.cpp",
- "src/cxa_vector.cpp",
- "src/cxa_virtual.cpp",
- "src/fallback_malloc.cpp",
- "src/private_typeinfo.cpp",
- "src/stdlib_exception.cpp",
- "src/stdlib_stdexcept.cpp",
- "src/stdlib_typeinfo.cpp",
-};
-
-pub const libcxx_files = [_][]const u8{
- "src/algorithm.cpp",
- "src/any.cpp",
- "src/bind.cpp",
- "src/charconv.cpp",
- "src/chrono.cpp",
- "src/condition_variable.cpp",
- "src/condition_variable_destructor.cpp",
- "src/debug.cpp",
- "src/exception.cpp",
- "src/experimental/memory_resource.cpp",
- "src/filesystem/directory_iterator.cpp",
- "src/filesystem/operations.cpp",
- "src/functional.cpp",
- "src/future.cpp",
- "src/hash.cpp",
- "src/ios.cpp",
- "src/iostream.cpp",
- "src/locale.cpp",
- "src/memory.cpp",
- "src/mutex.cpp",
- "src/mutex_destructor.cpp",
- "src/new.cpp",
- "src/optional.cpp",
- "src/random.cpp",
- "src/regex.cpp",
- "src/shared_mutex.cpp",
- "src/stdexcept.cpp",
- "src/string.cpp",
- "src/strstream.cpp",
- "src/support/solaris/xlocale.cpp",
- "src/support/win32/locale_win32.cpp",
- "src/support/win32/support.cpp",
- "src/support/win32/thread_win32.cpp",
- "src/system_error.cpp",
- "src/thread.cpp",
- "src/typeinfo.cpp",
- "src/utility.cpp",
- "src/valarray.cpp",
- "src/variant.cpp",
- "src/vector.cpp",
-};
diff --git a/src-self-hosted/libunwind.zig b/src-self-hosted/libunwind.zig
deleted file mode 100644
index 0bb44808ff572763374a57735e99c8f566b0b133..0000000000000000000000000000000000000000
--- a/src-self-hosted/libunwind.zig
+++ /dev/null
@@ -1,130 +0,0 @@
-const std = @import("std");
-const path = std.fs.path;
-const assert = std.debug.assert;
-
-const target_util = @import("target.zig");
-const Compilation = @import("Compilation.zig");
-const build_options = @import("build_options");
-const trace = @import("tracy.zig").trace;
-
-pub fn buildStaticLib(comp: *Compilation) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- if (!build_options.have_llvm) {
- return error.ZigCompilerNotBuiltWithLLVMExtensions;
- }
-
- var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
- defer arena_allocator.deinit();
- const arena = &arena_allocator.allocator;
-
- const root_name = "unwind";
- const output_mode = .Lib;
- const link_mode = .Static;
- const target = comp.getTarget();
- const basename = try std.zig.binNameAlloc(arena, root_name, target, output_mode, link_mode, null);
-
- const emit_bin = Compilation.EmitLoc{
- .directory = null, // Put it in the cache directory.
- .basename = basename,
- };
-
- const unwind_src_list = [_][]const u8{
- "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "libunwind.cpp",
- "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "Unwind-EHABI.cpp",
- "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "Unwind-seh.cpp",
- "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindLevel1.c",
- "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindLevel1-gcc-ext.c",
- "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "Unwind-sjlj.c",
- "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindRegistersRestore.S",
- "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindRegistersSave.S",
- };
-
- var c_source_files: [unwind_src_list.len]Compilation.CSourceFile = undefined;
- for (unwind_src_list) |unwind_src, i| {
- var cflags = std.ArrayList([]const u8).init(arena);
-
- switch (Compilation.classifyFileExt(unwind_src)) {
- .c => {
- try cflags.append("-std=c99");
- },
- .cpp => {
- try cflags.appendSlice(&[_][]const u8{
- "-fno-rtti",
- "-I",
- try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", "include" }),
- });
- },
- .assembly => {},
- else => unreachable, // You can see the entire list of files just above.
- }
- try cflags.append("-I");
- try cflags.append(try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libunwind", "include" }));
- if (target_util.supports_fpic(target)) {
- try cflags.append("-fPIC");
- }
- try cflags.append("-D_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS");
- try cflags.append("-Wa,--noexecstack");
-
- // This is intentionally always defined because the macro definition means, should it only
- // build for the target specified by compiler defines. Since we pass -target the compiler
- // defines will be correct.
- try cflags.append("-D_LIBUNWIND_IS_NATIVE_ONLY");
-
- if (comp.bin_file.options.optimize_mode == .Debug) {
- try cflags.append("-D_DEBUG");
- }
- if (comp.bin_file.options.single_threaded) {
- try cflags.append("-D_LIBUNWIND_HAS_NO_THREADS");
- }
- try cflags.append("-Wno-bitwise-conditional-parentheses");
-
- c_source_files[i] = .{
- .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{unwind_src}),
- .extra_flags = cflags.items,
- };
- }
- const sub_compilation = try Compilation.create(comp.gpa, .{
- // TODO use the global cache directory here
- .zig_cache_directory = comp.zig_cache_directory,
- .zig_lib_directory = comp.zig_lib_directory,
- .target = target,
- .root_name = root_name,
- .root_pkg = null,
- .output_mode = output_mode,
- .rand = comp.rand,
- .libc_installation = comp.bin_file.options.libc_installation,
- .emit_bin = emit_bin,
- .optimize_mode = comp.bin_file.options.optimize_mode,
- .link_mode = link_mode,
- .want_sanitize_c = false,
- .want_stack_check = false,
- .want_valgrind = false,
- .want_pic = comp.bin_file.options.pic,
- .emit_h = null,
- .strip = comp.bin_file.options.strip,
- .is_native_os = comp.bin_file.options.is_native_os,
- .self_exe_path = comp.self_exe_path,
- .c_source_files = &c_source_files,
- .verbose_cc = comp.verbose_cc,
- .verbose_link = comp.bin_file.options.verbose_link,
- .verbose_tokenize = comp.verbose_tokenize,
- .verbose_ast = comp.verbose_ast,
- .verbose_ir = comp.verbose_ir,
- .verbose_llvm_ir = comp.verbose_llvm_ir,
- .verbose_cimport = comp.verbose_cimport,
- .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
- .clang_passthrough_mode = comp.clang_passthrough_mode,
- .link_libc = true,
- });
- defer sub_compilation.destroy();
-
- try sub_compilation.updateSubCompilation();
-
- assert(comp.libunwind_static_lib == null);
- comp.libunwind_static_lib = Compilation.CRTFile{
- .full_object_path = try sub_compilation.bin_file.options.directory.join(comp.gpa, &[_][]const u8{basename}),
- .lock = sub_compilation.bin_file.toOwnedLock(),
- };
-}
diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig
deleted file mode 100644
index 3ec81715e403c010567b55789adf4d5541f7dbb7..0000000000000000000000000000000000000000
--- a/src-self-hosted/link.zig
+++ /dev/null
@@ -1,517 +0,0 @@
-const std = @import("std");
-const mem = std.mem;
-const Allocator = std.mem.Allocator;
-const Compilation = @import("Compilation.zig");
-const Module = @import("Module.zig");
-const fs = std.fs;
-const trace = @import("tracy.zig").trace;
-const Package = @import("Package.zig");
-const Type = @import("type.zig").Type;
-const Cache = @import("Cache.zig");
-const build_options = @import("build_options");
-const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
-const log = std.log.scoped(.link);
-
-pub const producer_string = if (std.builtin.is_test) "zig test" else "zig " ++ build_options.version;
-
-pub const Options = struct {
- /// Where the output will go.
- directory: Compilation.Directory,
- /// Path to the output file, relative to `directory`.
- sub_path: []const u8,
- target: std.Target,
- output_mode: std.builtin.OutputMode,
- link_mode: std.builtin.LinkMode,
- object_format: std.builtin.ObjectFormat,
- optimize_mode: std.builtin.Mode,
- machine_code_model: std.builtin.CodeModel,
- root_name: []const u8,
- /// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`.
- module: ?*Module,
- dynamic_linker: ?[]const u8 = null,
- /// Used for calculating how much space to reserve for symbols in case the binary file
- /// does not already have a symbol table.
- symbol_count_hint: u64 = 32,
- /// Used for calculating how much space to reserve for executable program code in case
- /// the binary file does not already have such a section.
- program_code_size_hint: u64 = 256 * 1024,
- entry_addr: ?u64 = null,
- stack_size_override: ?u64 = null,
- /// Set to `true` to omit debug info.
- strip: bool = false,
- /// If this is true then this link code is responsible for outputting an object
- /// file and then using LLD to link it together with the link options and other objects.
- /// Otherwise (depending on `use_llvm`) this link code directly outputs and updates the final binary.
- use_lld: bool = false,
- /// If this is true then this link code is responsible for making an LLVM IR Module,
- /// outputting it to an object file, and then linking that together with link options and
- /// other objects.
- /// Otherwise (depending on `use_lld`) this link code directly outputs and updates the final binary.
- use_llvm: bool = false,
- link_libc: bool = false,
- link_libcpp: bool = false,
- function_sections: bool = false,
- eh_frame_hdr: bool = false,
- rdynamic: bool = false,
- z_nodelete: bool = false,
- z_defs: bool = false,
- bind_global_refs_locally: bool,
- is_native_os: bool,
- pic: bool,
- valgrind: bool,
- stack_check: bool,
- single_threaded: bool,
- verbose_link: bool = false,
- dll_export_fns: bool,
- error_return_tracing: bool,
- gc_sections: ?bool = null,
- allow_shlib_undefined: ?bool = null,
- linker_script: ?[]const u8 = null,
- version_script: ?[]const u8 = null,
- override_soname: ?[]const u8 = null,
- llvm_cpu_features: ?[*:0]const u8 = null,
- /// Extra args passed directly to LLD. Ignored when not linking with LLD.
- extra_lld_args: []const []const u8 = &[0][]const u8,
-
- objects: []const []const u8 = &[0][]const u8{},
- framework_dirs: []const []const u8 = &[0][]const u8{},
- frameworks: []const []const u8 = &[0][]const u8{},
- system_libs: []const []const u8 = &[0][]const u8{},
- lib_dirs: []const []const u8 = &[0][]const u8{},
- rpath_list: []const []const u8 = &[0][]const u8{},
-
- version: ?std.builtin.Version,
- libc_installation: ?*const LibCInstallation,
-
- pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode {
- return if (options.use_lld) .Obj else options.output_mode;
- }
-};
-
-pub const File = struct {
- tag: Tag,
- options: Options,
- file: ?fs.File,
- allocator: *Allocator,
- /// When linking with LLD, this linker code will output an object file only at
- /// this location, and then this path can be placed on the LLD linker line.
- intermediary_basename: ?[]const u8 = null,
-
- /// Prevents other processes from clobbering files in the output directory
- /// of this linking operation.
- lock: ?Cache.Lock = null,
-
- pub const LinkBlock = union {
- elf: Elf.TextBlock,
- coff: Coff.TextBlock,
- macho: MachO.TextBlock,
- c: void,
- wasm: void,
- };
-
- pub const LinkFn = union {
- elf: Elf.SrcFn,
- coff: Coff.SrcFn,
- macho: MachO.SrcFn,
- c: void,
- wasm: ?Wasm.FnData,
- };
-
- /// For DWARF .debug_info.
- pub const DbgInfoTypeRelocsTable = std.HashMapUnmanaged(Type, DbgInfoTypeReloc, Type.hash, Type.eql, std.hash_map.DefaultMaxLoadPercentage);
-
- /// For DWARF .debug_info.
- pub const DbgInfoTypeReloc = struct {
- /// Offset from `TextBlock.dbg_info_off` (the buffer that is local to a Decl).
- /// This is where the .debug_info tag for the type is.
- off: u32,
- /// Offset from `TextBlock.dbg_info_off` (the buffer that is local to a Decl).
- /// List of DW.AT_type / DW.FORM_ref4 that points to the type.
- relocs: std.ArrayListUnmanaged(u32),
- };
-
- /// Attempts incremental linking, if the file already exists. If
- /// incremental linking fails, falls back to truncating the file and
- /// rewriting it. A malicious file is detected as incremental link failure
- /// and does not cause Illegal Behavior. This operation is not atomic.
- pub fn openPath(allocator: *Allocator, options: Options) !*File {
- const use_stage1 = build_options.is_stage1 and options.use_llvm;
- if (use_stage1) {
- return switch (options.object_format) {
- .coff, .pe => &(try Coff.createEmpty(allocator, options)).base,
- .elf => &(try Elf.createEmpty(allocator, options)).base,
- .macho => &(try MachO.createEmpty(allocator, options)).base,
- .wasm => &(try Wasm.createEmpty(allocator, options)).base,
- .c => unreachable, // Reported error earlier.
- .hex => return error.HexObjectFormatUnimplemented,
- .raw => return error.RawObjectFormatUnimplemented,
- };
- }
- const use_lld = build_options.have_llvm and options.use_lld; // comptime known false when !have_llvm
- const sub_path = if (use_lld) blk: {
- if (options.module == null) {
- // No point in opening a file, we would not write anything to it. Initialize with empty.
- return switch (options.object_format) {
- .coff, .pe => &(try Coff.createEmpty(allocator, options)).base,
- .elf => &(try Elf.createEmpty(allocator, options)).base,
- .macho => &(try MachO.createEmpty(allocator, options)).base,
- .wasm => &(try Wasm.createEmpty(allocator, options)).base,
- .c => unreachable, // Reported error earlier.
- .hex => return error.HexObjectFormatUnimplemented,
- .raw => return error.RawObjectFormatUnimplemented,
- };
- }
- // Open a temporary object file, not the final output file because we want to link with LLD.
- break :blk try std.fmt.allocPrint(allocator, "{}{}", .{ options.sub_path, options.target.oFileExt() });
- } else options.sub_path;
- errdefer if (use_lld) allocator.free(sub_path);
-
- const file: *File = switch (options.object_format) {
- .coff, .pe => &(try Coff.openPath(allocator, sub_path, options)).base,
- .elf => &(try Elf.openPath(allocator, sub_path, options)).base,
- .macho => &(try MachO.openPath(allocator, sub_path, options)).base,
- .wasm => &(try Wasm.openPath(allocator, sub_path, options)).base,
- .c => &(try C.openPath(allocator, sub_path, options)).base,
- .hex => return error.HexObjectFormatUnimplemented,
- .raw => return error.RawObjectFormatUnimplemented,
- };
-
- if (use_lld) {
- file.intermediary_basename = sub_path;
- }
-
- return file;
- }
-
- pub fn cast(base: *File, comptime T: type) ?*T {
- if (base.tag != T.base_tag)
- return null;
-
- return @fieldParentPtr(T, "base", base);
- }
-
- pub fn makeWritable(base: *File) !void {
- switch (base.tag) {
- .coff, .elf, .macho => {
- if (base.file != null) return;
- base.file = try base.options.directory.handle.createFile(base.options.sub_path, .{
- .truncate = false,
- .read = true,
- .mode = determineMode(base.options),
- });
- },
- .c, .wasm => {},
- }
- }
-
- pub fn makeExecutable(base: *File) !void {
- switch (base.tag) {
- .coff, .elf, .macho => if (base.file) |f| {
- if (base.intermediary_basename != null) {
- // The file we have open is not the final file that we want to
- // make executable, so we don't have to close it.
- return;
- }
- f.close();
- base.file = null;
- },
- .c, .wasm => {},
- }
- }
-
- /// May be called before or after updateDeclExports but must be called
- /// after allocateDeclIndexes for any given Decl.
- pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void {
- switch (base.tag) {
- .coff => return @fieldParentPtr(Coff, "base", base).updateDecl(module, decl),
- .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),
- .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl),
- .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),
- .wasm => return @fieldParentPtr(Wasm, "base", base).updateDecl(module, decl),
- }
- }
-
- pub fn updateDeclLineNumber(base: *File, module: *Module, decl: *Module.Decl) !void {
- switch (base.tag) {
- .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl),
- .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),
- .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl),
- .c, .wasm => {},
- }
- }
-
- /// Must be called before any call to updateDecl or updateDeclExports for
- /// any given Decl.
- pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {
- switch (base.tag) {
- .coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl),
- .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
- .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl),
- .c, .wasm => {},
- }
- }
-
- pub fn releaseLock(self: *File) void {
- if (self.lock) |*lock| {
- lock.release();
- self.lock = null;
- }
- }
-
- pub fn toOwnedLock(self: *File) Cache.Lock {
- const lock = self.lock.?;
- self.lock = null;
- return lock;
- }
-
- pub fn destroy(base: *File) void {
- base.releaseLock();
- if (base.file) |f| f.close();
- if (base.intermediary_basename) |sub_path| base.allocator.free(sub_path);
- switch (base.tag) {
- .coff => {
- const parent = @fieldParentPtr(Coff, "base", base);
- parent.deinit();
- base.allocator.destroy(parent);
- },
- .elf => {
- const parent = @fieldParentPtr(Elf, "base", base);
- parent.deinit();
- base.allocator.destroy(parent);
- },
- .macho => {
- const parent = @fieldParentPtr(MachO, "base", base);
- parent.deinit();
- base.allocator.destroy(parent);
- },
- .c => {
- const parent = @fieldParentPtr(C, "base", base);
- parent.deinit();
- base.allocator.destroy(parent);
- },
- .wasm => {
- const parent = @fieldParentPtr(Wasm, "base", base);
- parent.deinit();
- base.allocator.destroy(parent);
- },
- }
- }
-
- /// Commit pending changes and write headers. Takes into account final output mode
- /// and `use_lld`, not only `effectiveOutputMode`.
- pub fn flush(base: *File, comp: *Compilation) !void {
- const use_lld = build_options.have_llvm and base.options.use_lld;
- if (use_lld and base.options.output_mode == .Lib and base.options.link_mode == .Static and
- !base.options.target.isWasm())
- {
- return base.linkAsArchive(comp);
- }
- switch (base.tag) {
- .coff => return @fieldParentPtr(Coff, "base", base).flush(comp),
- .elf => return @fieldParentPtr(Elf, "base", base).flush(comp),
- .macho => return @fieldParentPtr(MachO, "base", base).flush(comp),
- .c => return @fieldParentPtr(C, "base", base).flush(comp),
- .wasm => return @fieldParentPtr(Wasm, "base", base).flush(comp),
- }
- }
-
- /// Commit pending changes and write headers. Works based on `effectiveOutputMode`
- /// rather than final output mode.
- pub fn flushModule(base: *File, comp: *Compilation) !void {
- switch (base.tag) {
- .coff => return @fieldParentPtr(Coff, "base", base).flushModule(comp),
- .elf => return @fieldParentPtr(Elf, "base", base).flushModule(comp),
- .macho => return @fieldParentPtr(MachO, "base", base).flushModule(comp),
- .c => return @fieldParentPtr(C, "base", base).flushModule(comp),
- .wasm => return @fieldParentPtr(Wasm, "base", base).flushModule(comp),
- }
- }
-
- pub fn freeDecl(base: *File, decl: *Module.Decl) void {
- switch (base.tag) {
- .coff => @fieldParentPtr(Coff, "base", base).freeDecl(decl),
- .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),
- .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl),
- .c => unreachable,
- .wasm => @fieldParentPtr(Wasm, "base", base).freeDecl(decl),
- }
- }
-
- pub fn errorFlags(base: *File) ErrorFlags {
- switch (base.tag) {
- .coff => return @fieldParentPtr(Coff, "base", base).error_flags,
- .elf => return @fieldParentPtr(Elf, "base", base).error_flags,
- .macho => return @fieldParentPtr(MachO, "base", base).error_flags,
- .c => return .{ .no_entry_point_found = false },
- .wasm => return ErrorFlags{},
- }
- }
-
- /// May be called before or after updateDecl, but must be called after
- /// allocateDeclIndexes for any given Decl.
- pub fn updateDeclExports(
- base: *File,
- module: *Module,
- decl: *const Module.Decl,
- exports: []const *Module.Export,
- ) !void {
- switch (base.tag) {
- .coff => return @fieldParentPtr(Coff, "base", base).updateDeclExports(module, decl, exports),
- .elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports),
- .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl, exports),
- .c => return {},
- .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclExports(module, decl, exports),
- }
- }
-
- pub fn getDeclVAddr(base: *File, decl: *const Module.Decl) u64 {
- switch (base.tag) {
- .coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl),
- .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl),
- .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl),
- .c => unreachable,
- .wasm => unreachable,
- }
- }
-
- fn linkAsArchive(base: *File, comp: *Compilation) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- var arena_allocator = std.heap.ArenaAllocator.init(base.allocator);
- defer arena_allocator.deinit();
- const arena = &arena_allocator.allocator;
-
- const directory = base.options.directory; // Just an alias to make it shorter to type.
-
- // If there is no Zig code to compile, then we should skip flushing the output file because it
- // will not be part of the linker line anyway.
- const module_obj_path: ?[]const u8 = if (base.options.module) |module| blk: {
- const use_stage1 = build_options.is_stage1 and base.options.use_llvm;
- if (use_stage1) {
- const obj_basename = try std.fmt.allocPrint(arena, "{}.o", .{base.options.root_name});
- const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename});
- break :blk full_obj_path;
- }
- try base.flushModule(comp);
- const obj_basename = base.intermediary_basename.?;
- const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename});
- break :blk full_obj_path;
- } else null;
-
- // This function follows the same pattern as link.Elf.linkWithLLD so if you want some
- // insight as to what's going on here you can read that function body which is more
- // well-commented.
-
- const id_symlink_basename = "llvm-ar.id";
-
- base.releaseLock();
-
- var ch = comp.cache_parent.obtain();
- defer ch.deinit();
-
- try ch.addListOfFiles(base.options.objects);
- for (comp.c_object_table.items()) |entry| {
- _ = try ch.addFile(entry.key.status.success.object_path, null);
- }
- try ch.addOptionalFile(module_obj_path);
-
- // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
- _ = try ch.hit();
- const digest = ch.final();
-
- var prev_digest_buf: [digest.len]u8 = undefined;
- const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| b: {
- log.debug("archive new_digest={} readlink error: {}", .{ digest, @errorName(err) });
- break :b prev_digest_buf[0..0];
- };
- if (mem.eql(u8, prev_digest, &digest)) {
- log.debug("archive digest={} match - skipping invocation", .{digest});
- base.lock = ch.toOwnedLock();
- return;
- }
-
- // We are about to change the output file to be different, so we invalidate the build hash now.
- directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
- error.FileNotFound => {},
- else => |e| return e,
- };
-
- var object_files = std.ArrayList([*:0]const u8).init(base.allocator);
- defer object_files.deinit();
-
- try object_files.ensureCapacity(base.options.objects.len + comp.c_object_table.items().len + 1);
- for (base.options.objects) |obj_path| {
- object_files.appendAssumeCapacity(try arena.dupeZ(u8, obj_path));
- }
- for (comp.c_object_table.items()) |entry| {
- object_files.appendAssumeCapacity(try arena.dupeZ(u8, entry.key.status.success.object_path));
- }
- if (module_obj_path) |p| {
- object_files.appendAssumeCapacity(try arena.dupeZ(u8, p));
- }
-
- const full_out_path = if (directory.path) |dir_path|
- try std.fs.path.join(arena, &[_][]const u8{ dir_path, base.options.sub_path })
- else
- base.options.sub_path;
- const full_out_path_z = try arena.dupeZ(u8, full_out_path);
-
- if (base.options.verbose_link) {
- std.debug.print("ar rcs {}", .{full_out_path_z});
- for (object_files.items) |arg| {
- std.debug.print(" {}", .{arg});
- }
- std.debug.print("\n", .{});
- }
-
- const llvm = @import("llvm.zig");
- const os_type = @import("target.zig").osToLLVM(base.options.target.os.tag);
- const bad = llvm.WriteArchive(full_out_path_z, object_files.items.ptr, object_files.items.len, os_type);
- if (bad) return error.UnableToWriteArchive;
-
- directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| {
- std.log.warn("failed to save archive hash digest symlink: {}", .{@errorName(err)});
- };
-
- ch.writeManifest() catch |err| {
- std.log.warn("failed to write cache manifest when archiving: {}", .{@errorName(err)});
- };
-
- base.lock = ch.toOwnedLock();
- }
-
- pub const Tag = enum {
- coff,
- elf,
- macho,
- c,
- wasm,
- };
-
- pub const ErrorFlags = struct {
- no_entry_point_found: bool = false,
- };
-
- pub const C = @import("link/C.zig");
- pub const Coff = @import("link/Coff.zig");
- pub const Elf = @import("link/Elf.zig");
- pub const MachO = @import("link/MachO.zig");
- pub const Wasm = @import("link/Wasm.zig");
-};
-
-pub fn determineMode(options: Options) fs.File.Mode {
- // On common systems with a 0o022 umask, 0o777 will still result in a file created
- // with 0o755 permissions, but it works appropriately if the system is configured
- // more leniently. As another data point, C's fopen seems to open files with the
- // 666 mode.
- const executable_mode = if (std.Target.current.os.tag == .windows) 0 else 0o777;
- switch (options.effectiveOutputMode()) {
- .Lib => return switch (options.link_mode) {
- .Dynamic => executable_mode,
- .Static => fs.File.default_mode,
- },
- .Exe => return executable_mode,
- .Obj => return fs.File.default_mode,
- }
-}
diff --git a/src-self-hosted/link/C.zig b/src-self-hosted/link/C.zig
deleted file mode 100644
index d5d12492447358e15f70fd2e6007c362185f9e59..0000000000000000000000000000000000000000
--- a/src-self-hosted/link/C.zig
+++ /dev/null
@@ -1,113 +0,0 @@
-const std = @import("std");
-const mem = std.mem;
-const assert = std.debug.assert;
-const Allocator = std.mem.Allocator;
-const Module = @import("../Module.zig");
-const Compilation = @import("../Compilation.zig");
-const fs = std.fs;
-const codegen = @import("../codegen/c.zig");
-const link = @import("../link.zig");
-const trace = @import("../tracy.zig").trace;
-const File = link.File;
-const C = @This();
-
-pub const base_tag: File.Tag = .c;
-
-base: File,
-
-header: std.ArrayList(u8),
-constants: std.ArrayList(u8),
-main: std.ArrayList(u8),
-
-called: std.StringHashMap(void),
-need_stddef: bool = false,
-need_stdint: bool = false,
-error_msg: *Compilation.ErrorMsg = undefined,
-
-pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*C {
- assert(options.object_format == .c);
-
- if (options.use_llvm) return error.LLVMHasNoCBackend;
- if (options.use_lld) return error.LLDHasNoCBackend;
-
- const file = try options.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true, .mode = link.determineMode(options) });
- errdefer file.close();
-
- var c_file = try allocator.create(C);
- errdefer allocator.destroy(c_file);
-
- c_file.* = C{
- .base = .{
- .tag = .c,
- .options = options,
- .file = file,
- .allocator = allocator,
- },
- .main = std.ArrayList(u8).init(allocator),
- .header = std.ArrayList(u8).init(allocator),
- .constants = std.ArrayList(u8).init(allocator),
- .called = std.StringHashMap(void).init(allocator),
- };
-
- return c_file;
-}
-
-pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
- self.error_msg = try Compilation.ErrorMsg.create(self.base.allocator, src, format, args);
- return error.AnalysisFail;
-}
-
-pub fn deinit(self: *C) void {
- self.main.deinit();
- self.header.deinit();
- self.constants.deinit();
- self.called.deinit();
-}
-
-pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
- codegen.generate(self, decl) catch |err| {
- if (err == error.AnalysisFail) {
- try module.failed_decls.put(module.gpa, decl, self.error_msg);
- }
- return err;
- };
-}
-
-pub fn flush(self: *C, comp: *Compilation) !void {
- return self.flushModule(comp);
-}
-
-pub fn flushModule(self: *C, comp: *Compilation) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- const writer = self.base.file.?.writer();
- try writer.writeAll(@embedFile("cbe.h"));
- var includes = false;
- if (self.need_stddef) {
- try writer.writeAll("#include \n");
- includes = true;
- }
- if (self.need_stdint) {
- try writer.writeAll("#include \n");
- includes = true;
- }
- if (includes) {
- try writer.writeByte('\n');
- }
- if (self.header.items.len > 0) {
- try writer.print("{}\n", .{self.header.items});
- }
- if (self.constants.items.len > 0) {
- try writer.print("{}\n", .{self.constants.items});
- }
- if (self.main.items.len > 1) {
- const last_two = self.main.items[self.main.items.len - 2 ..];
- if (std.mem.eql(u8, last_two, "\n\n")) {
- self.main.items.len -= 1;
- }
- }
- try writer.writeAll(self.main.items);
- self.base.file.?.close();
- self.base.file = null;
-}
diff --git a/src-self-hosted/link/Coff.zig b/src-self-hosted/link/Coff.zig
deleted file mode 100644
index 31726a5712a4e8dfbb3a329fa3e39cac06869894..0000000000000000000000000000000000000000
--- a/src-self-hosted/link/Coff.zig
+++ /dev/null
@@ -1,784 +0,0 @@
-const Coff = @This();
-
-const std = @import("std");
-const log = std.log.scoped(.link);
-const Allocator = std.mem.Allocator;
-const assert = std.debug.assert;
-const fs = std.fs;
-
-const trace = @import("../tracy.zig").trace;
-const Module = @import("../Module.zig");
-const Compilation = @import("../Compilation.zig");
-const codegen = @import("../codegen.zig");
-const link = @import("../link.zig");
-const build_options = @import("build_options");
-
-const allocation_padding = 4 / 3;
-const minimum_text_block_size = 64 * allocation_padding;
-
-const section_alignment = 4096;
-const file_alignment = 512;
-const image_base = 0x400_000;
-const section_table_size = 2 * 40;
-comptime {
- assert(std.mem.isAligned(image_base, section_alignment));
-}
-
-pub const base_tag: link.File.Tag = .coff;
-
-const msdos_stub = @embedFile("msdos-stub.bin");
-
-base: link.File,
-ptr_width: PtrWidth,
-error_flags: link.File.ErrorFlags = .{},
-
-text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},
-last_text_block: ?*TextBlock = null,
-
-/// Section table file pointer.
-section_table_offset: u32 = 0,
-/// Section data file pointer.
-section_data_offset: u32 = 0,
-/// Optiona header file pointer.
-optional_header_offset: u32 = 0,
-
-/// Absolute virtual address of the offset table when the executable is loaded in memory.
-offset_table_virtual_address: u32 = 0,
-/// Current size of the offset table on disk, must be a multiple of `file_alignment`
-offset_table_size: u32 = 0,
-/// Contains absolute virtual addresses
-offset_table: std.ArrayListUnmanaged(u64) = .{},
-/// Free list of offset table indices
-offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
-
-/// Virtual address of the entry point procedure relative to `image_base`
-entry_addr: ?u32 = null,
-
-/// Absolute virtual address of the text section when the executable is loaded in memory.
-text_section_virtual_address: u32 = 0,
-/// Current size of the `.text` section on disk, must be a multiple of `file_alignment`
-text_section_size: u32 = 0,
-
-offset_table_size_dirty: bool = false,
-text_section_size_dirty: bool = false,
-/// This flag is set when the virtual size of the whole image file when loaded in memory has changed
-/// and needs to be updated in the optional header.
-size_of_image_dirty: bool = false,
-
-pub const PtrWidth = enum { p32, p64 };
-
-pub const TextBlock = struct {
- /// Offset of the code relative to the start of the text section
- text_offset: u32,
- /// Used size of the text block
- size: u32,
- /// This field is undefined for symbols with size = 0.
- offset_table_index: u32,
- /// Points to the previous and next neighbors, based on the `text_offset`.
- /// This can be used to find, for example, the capacity of this `TextBlock`.
- prev: ?*TextBlock,
- next: ?*TextBlock,
-
- pub const empty = TextBlock{
- .text_offset = 0,
- .size = 0,
- .offset_table_index = undefined,
- .prev = null,
- .next = null,
- };
-
- /// Returns how much room there is to grow in virtual address space.
- fn capacity(self: TextBlock) u64 {
- if (self.next) |next| {
- return next.text_offset - self.text_offset;
- }
- // This is the last block, the capacity is only limited by the address space.
- return std.math.maxInt(u32) - self.text_offset;
- }
-
- fn freeListEligible(self: TextBlock) bool {
- // No need to keep a free list node for the last block.
- const next = self.next orelse return false;
- const cap = next.text_offset - self.text_offset;
- const ideal_cap = self.size * allocation_padding;
- if (cap <= ideal_cap) return false;
- const surplus = cap - ideal_cap;
- return surplus >= minimum_text_block_size;
- }
-
- /// Absolute virtual address of the text block when the file is loaded in memory.
- fn getVAddr(self: TextBlock, coff: Coff) u32 {
- return coff.text_section_virtual_address + self.text_offset;
- }
-};
-
-pub const SrcFn = void;
-
-pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Coff {
- assert(options.object_format == .coff);
-
- if (options.use_llvm) return error.LLVM_BackendIsTODO_ForCoff; // TODO
- if (options.use_lld) return error.LLD_LinkingIsTODO_ForCoff; // TODO
-
- const file = try options.directory.handle.createFile(sub_path, .{
- .truncate = false,
- .read = true,
- .mode = link.determineMode(options),
- });
- errdefer file.close();
-
- const self = try createEmpty(allocator, options);
- errdefer self.base.destroy();
-
- self.base.file = file;
-
- // TODO Write object specific relocations, COFF symbol table, then enable object file output.
- switch (options.output_mode) {
- .Exe => {},
- .Obj => return error.TODOImplementWritingObjFiles,
- .Lib => return error.TODOImplementWritingLibFiles,
- }
-
- var coff_file_header_offset: u32 = 0;
- if (options.output_mode == .Exe) {
- // Write the MS-DOS stub and the PE signature
- try self.base.file.?.pwriteAll(msdos_stub ++ "PE\x00\x00", 0);
- coff_file_header_offset = msdos_stub.len + 4;
- }
-
- // COFF file header
- const data_directory_count = 0;
- var hdr_data: [112 + data_directory_count * 8 + section_table_size]u8 = undefined;
- var index: usize = 0;
-
- const machine = self.base.options.target.cpu.arch.toCoffMachine();
- if (machine == .Unknown) {
- return error.UnsupportedCOFFArchitecture;
- }
- std.mem.writeIntLittle(u16, hdr_data[0..2], @enumToInt(machine));
- index += 2;
-
- // Number of sections (we only use .got, .text)
- std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 2);
- index += 2;
- // TimeDateStamp (u32), PointerToSymbolTable (u32), NumberOfSymbols (u32)
- std.mem.set(u8, hdr_data[index..][0..12], 0);
- index += 12;
-
- const optional_header_size = switch (options.output_mode) {
- .Exe => data_directory_count * 8 + switch (self.ptr_width) {
- .p32 => @as(u16, 96),
- .p64 => 112,
- },
- else => 0,
- };
-
- const section_table_offset = coff_file_header_offset + 20 + optional_header_size;
- const default_offset_table_size = file_alignment;
- const default_size_of_code = 0;
-
- self.section_data_offset = std.mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, file_alignment);
- const section_data_relative_virtual_address = std.mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, section_alignment);
- self.offset_table_virtual_address = image_base + section_data_relative_virtual_address;
- self.offset_table_size = default_offset_table_size;
- self.section_table_offset = section_table_offset;
- self.text_section_virtual_address = image_base + section_data_relative_virtual_address + section_alignment;
- self.text_section_size = default_size_of_code;
-
- // Size of file when loaded in memory
- const size_of_image = std.mem.alignForwardGeneric(u32, self.text_section_virtual_address - image_base + default_size_of_code, section_alignment);
-
- std.mem.writeIntLittle(u16, hdr_data[index..][0..2], optional_header_size);
- index += 2;
-
- // Characteristics
- var characteristics: u16 = std.coff.IMAGE_FILE_DEBUG_STRIPPED | std.coff.IMAGE_FILE_RELOCS_STRIPPED; // TODO Remove debug info stripped flag when necessary
- if (options.output_mode == .Exe) {
- characteristics |= std.coff.IMAGE_FILE_EXECUTABLE_IMAGE;
- }
- switch (self.ptr_width) {
- .p32 => characteristics |= std.coff.IMAGE_FILE_32BIT_MACHINE,
- .p64 => characteristics |= std.coff.IMAGE_FILE_LARGE_ADDRESS_AWARE,
- }
- std.mem.writeIntLittle(u16, hdr_data[index..][0..2], characteristics);
- index += 2;
-
- assert(index == 20);
- try self.base.file.?.pwriteAll(hdr_data[0..index], coff_file_header_offset);
-
- if (options.output_mode == .Exe) {
- self.optional_header_offset = coff_file_header_offset + 20;
- // Optional header
- index = 0;
- std.mem.writeIntLittle(u16, hdr_data[0..2], switch (self.ptr_width) {
- .p32 => @as(u16, 0x10b),
- .p64 => 0x20b,
- });
- index += 2;
-
- // Linker version (u8 + u8)
- std.mem.set(u8, hdr_data[index..][0..2], 0);
- index += 2;
-
- // SizeOfCode (UNUSED, u32), SizeOfInitializedData (u32), SizeOfUninitializedData (u32), AddressOfEntryPoint (u32), BaseOfCode (UNUSED, u32)
- std.mem.set(u8, hdr_data[index..][0..20], 0);
- index += 20;
-
- if (self.ptr_width == .p32) {
- // Base of data relative to the image base (UNUSED)
- std.mem.set(u8, hdr_data[index..][0..4], 0);
- index += 4;
-
- // Image base address
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], image_base);
- index += 4;
- } else {
- // Image base address
- std.mem.writeIntLittle(u64, hdr_data[index..][0..8], image_base);
- index += 8;
- }
-
- // Section alignment
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], section_alignment);
- index += 4;
- // File alignment
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], file_alignment);
- index += 4;
- // Required OS version, 6.0 is vista
- std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 6);
- index += 2;
- std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 0);
- index += 2;
- // Image version
- std.mem.set(u8, hdr_data[index..][0..4], 0);
- index += 4;
- // Required subsystem version, same as OS version
- std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 6);
- index += 2;
- std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 0);
- index += 2;
- // Reserved zeroes (u32)
- std.mem.set(u8, hdr_data[index..][0..4], 0);
- index += 4;
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], size_of_image);
- index += 4;
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset);
- index += 4;
- // CheckSum (u32)
- std.mem.set(u8, hdr_data[index..][0..4], 0);
- index += 4;
- // Subsystem, TODO: Let users specify the subsystem, always CUI for now
- std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 3);
- index += 2;
- // DLL characteristics
- std.mem.writeIntLittle(u16, hdr_data[index..][0..2], 0x0);
- index += 2;
-
- switch (self.ptr_width) {
- .p32 => {
- // Size of stack reserve + commit
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000_000);
- index += 4;
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000);
- index += 4;
- // Size of heap reserve + commit
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x100_000);
- index += 4;
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000);
- index += 4;
- },
- .p64 => {
- // Size of stack reserve + commit
- std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000_000);
- index += 8;
- std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000);
- index += 8;
- // Size of heap reserve + commit
- std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x100_000);
- index += 8;
- std.mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000);
- index += 8;
- },
- }
-
- // Reserved zeroes
- std.mem.set(u8, hdr_data[index..][0..4], 0);
- index += 4;
-
- // Number of data directories
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], data_directory_count);
- index += 4;
- // Initialize data directories to zero
- std.mem.set(u8, hdr_data[index..][0 .. data_directory_count * 8], 0);
- index += data_directory_count * 8;
-
- assert(index == optional_header_size);
- }
-
- // Write section table.
- // First, the .got section
- hdr_data[index..][0..8].* = ".got\x00\x00\x00\x00".*;
- index += 8;
- if (options.output_mode == .Exe) {
- // Virtual size (u32)
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size);
- index += 4;
- // Virtual address (u32)
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.offset_table_virtual_address - image_base);
- index += 4;
- } else {
- std.mem.set(u8, hdr_data[index..][0..8], 0);
- index += 8;
- }
- // Size of raw data (u32)
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size);
- index += 4;
- // File pointer to the start of the section
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset);
- index += 4;
- // Pointer to relocations (u32), PointerToLinenumbers (u32), NumberOfRelocations (u16), NumberOfLinenumbers (u16)
- std.mem.set(u8, hdr_data[index..][0..12], 0);
- index += 12;
- // Section flags
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], std.coff.IMAGE_SCN_CNT_INITIALIZED_DATA | std.coff.IMAGE_SCN_MEM_READ);
- index += 4;
- // Then, the .text section
- hdr_data[index..][0..8].* = ".text\x00\x00\x00".*;
- index += 8;
- if (options.output_mode == .Exe) {
- // Virtual size (u32)
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code);
- index += 4;
- // Virtual address (u32)
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.text_section_virtual_address - image_base);
- index += 4;
- } else {
- std.mem.set(u8, hdr_data[index..][0..8], 0);
- index += 8;
- }
- // Size of raw data (u32)
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code);
- index += 4;
- // File pointer to the start of the section
- std.mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset + default_offset_table_size);
- index += 4;
- // Pointer to relocations (u32), PointerToLinenumbers (u32), NumberOfRelocations (u16), NumberOfLinenumbers (u16)
- std.mem.set(u8, hdr_data[index..][0..12], 0);
- index += 12;
- // Section flags
- std.mem.writeIntLittle(
- u32,
- hdr_data[index..][0..4],
- std.coff.IMAGE_SCN_CNT_CODE | std.coff.IMAGE_SCN_MEM_EXECUTE | std.coff.IMAGE_SCN_MEM_READ | std.coff.IMAGE_SCN_MEM_WRITE,
- );
- index += 4;
-
- assert(index == optional_header_size + section_table_size);
- try self.base.file.?.pwriteAll(hdr_data[0..index], self.optional_header_offset);
- try self.base.file.?.setEndPos(self.section_data_offset + default_offset_table_size + default_size_of_code);
-
- return self;
-}
-
-pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Coff {
- const ptr_width: PtrWidth = switch (options.target.cpu.arch.ptrBitWidth()) {
- 0...32 => .p32,
- 33...64 => .p64,
- else => return error.UnsupportedCOFFArchitecture,
- };
- const self = try gpa.create(Coff);
- self.* = .{
- .base = .{
- .tag = .coff,
- .options = options,
- .allocator = gpa,
- .file = null,
- },
- .ptr_width = ptr_width,
- };
- return self;
-}
-
-pub fn allocateDeclIndexes(self: *Coff, decl: *Module.Decl) !void {
- try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);
-
- if (self.offset_table_free_list.popOrNull()) |i| {
- decl.link.coff.offset_table_index = i;
- } else {
- decl.link.coff.offset_table_index = @intCast(u32, self.offset_table.items.len);
- _ = self.offset_table.addOneAssumeCapacity();
-
- const entry_size = self.base.options.target.cpu.arch.ptrBitWidth() / 8;
- if (self.offset_table.items.len > self.offset_table_size / entry_size) {
- self.offset_table_size_dirty = true;
- }
- }
-
- self.offset_table.items[decl.link.coff.offset_table_index] = 0;
-}
-
-fn allocateTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
- const new_block_min_capacity = new_block_size * allocation_padding;
-
- // We use these to indicate our intention to update metadata, placing the new block,
- // and possibly removing a free list node.
- // It would be simpler to do it inside the for loop below, but that would cause a
- // problem if an error was returned later in the function. So this action
- // is actually carried out at the end of the function, when errors are no longer possible.
- var block_placement: ?*TextBlock = null;
- var free_list_removal: ?usize = null;
-
- const vaddr = blk: {
- var i: usize = 0;
- while (i < self.text_block_free_list.items.len) {
- const free_block = self.text_block_free_list.items[i];
-
- const next_block_text_offset = free_block.text_offset + free_block.capacity();
- const new_block_text_offset = std.mem.alignForwardGeneric(u64, free_block.getVAddr(self.*) + free_block.size, alignment) - self.text_section_virtual_address;
- if (new_block_text_offset < next_block_text_offset and next_block_text_offset - new_block_text_offset >= new_block_min_capacity) {
- block_placement = free_block;
-
- const remaining_capacity = next_block_text_offset - new_block_text_offset - new_block_min_capacity;
- if (remaining_capacity < minimum_text_block_size) {
- free_list_removal = i;
- }
-
- break :blk new_block_text_offset + self.text_section_virtual_address;
- } else {
- if (!free_block.freeListEligible()) {
- _ = self.text_block_free_list.swapRemove(i);
- } else {
- i += 1;
- }
- continue;
- }
- } else if (self.last_text_block) |last| {
- const new_block_vaddr = std.mem.alignForwardGeneric(u64, last.getVAddr(self.*) + last.size, alignment);
- block_placement = last;
- break :blk new_block_vaddr;
- } else {
- break :blk self.text_section_virtual_address;
- }
- };
-
- const expand_text_section = block_placement == null or block_placement.?.next == null;
- if (expand_text_section) {
- const needed_size = @intCast(u32, std.mem.alignForwardGeneric(u64, vaddr + new_block_size - self.text_section_virtual_address, file_alignment));
- if (needed_size > self.text_section_size) {
- const current_text_section_virtual_size = std.mem.alignForwardGeneric(u32, self.text_section_size, section_alignment);
- const new_text_section_virtual_size = std.mem.alignForwardGeneric(u32, needed_size, section_alignment);
- if (current_text_section_virtual_size != new_text_section_virtual_size) {
- self.size_of_image_dirty = true;
- // Write new virtual size
- var buf: [4]u8 = undefined;
- std.mem.writeIntLittle(u32, &buf, new_text_section_virtual_size);
- try self.base.file.?.pwriteAll(&buf, self.section_table_offset + 40 + 8);
- }
-
- self.text_section_size = needed_size;
- self.text_section_size_dirty = true;
- }
- self.last_text_block = text_block;
- }
- text_block.text_offset = @intCast(u32, vaddr - self.text_section_virtual_address);
- text_block.size = @intCast(u32, new_block_size);
-
- // This function can also reallocate a text block.
- // In this case we need to "unplug" it from its previous location before
- // plugging it in to its new location.
- if (text_block.prev) |prev| {
- prev.next = text_block.next;
- }
- if (text_block.next) |next| {
- next.prev = text_block.prev;
- }
-
- if (block_placement) |big_block| {
- text_block.prev = big_block;
- text_block.next = big_block.next;
- big_block.next = text_block;
- } else {
- text_block.prev = null;
- text_block.next = null;
- }
- if (free_list_removal) |i| {
- _ = self.text_block_free_list.swapRemove(i);
- }
- return vaddr;
-}
-
-fn growTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
- const block_vaddr = text_block.getVAddr(self.*);
- const align_ok = std.mem.alignBackwardGeneric(u64, block_vaddr, alignment) == block_vaddr;
- const need_realloc = !align_ok or new_block_size > text_block.capacity();
- if (!need_realloc) return @as(u64, block_vaddr);
- return self.allocateTextBlock(text_block, new_block_size, alignment);
-}
-
-fn shrinkTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64) void {
- text_block.size = @intCast(u32, new_block_size);
- if (text_block.capacity() - text_block.size >= minimum_text_block_size) {
- self.text_block_free_list.append(self.base.allocator, text_block) catch {};
- }
-}
-
-fn freeTextBlock(self: *Coff, text_block: *TextBlock) void {
- var already_have_free_list_node = false;
- {
- var i: usize = 0;
- // TODO turn text_block_free_list into a hash map
- while (i < self.text_block_free_list.items.len) {
- if (self.text_block_free_list.items[i] == text_block) {
- _ = self.text_block_free_list.swapRemove(i);
- continue;
- }
- if (self.text_block_free_list.items[i] == text_block.prev) {
- already_have_free_list_node = true;
- }
- i += 1;
- }
- }
- if (self.last_text_block == text_block) {
- self.last_text_block = text_block.prev;
- }
- if (text_block.prev) |prev| {
- prev.next = text_block.next;
-
- if (!already_have_free_list_node and prev.freeListEligible()) {
- // The free list is heuristics, it doesn't have to be perfect, so we can
- // ignore the OOM here.
- self.text_block_free_list.append(self.base.allocator, prev) catch {};
- }
- }
-
- if (text_block.next) |next| {
- next.prev = text_block.prev;
- }
-}
-
-fn writeOffsetTableEntry(self: *Coff, index: usize) !void {
- const entry_size = self.base.options.target.cpu.arch.ptrBitWidth() / 8;
- const endian = self.base.options.target.cpu.arch.endian();
-
- const offset_table_start = self.section_data_offset;
- if (self.offset_table_size_dirty) {
- const current_raw_size = self.offset_table_size;
- const new_raw_size = self.offset_table_size * 2;
- log.debug("growing offset table from raw size {} to {}\n", .{ current_raw_size, new_raw_size });
-
- // Move the text section to a new place in the executable
- const current_text_section_start = self.section_data_offset + current_raw_size;
- const new_text_section_start = self.section_data_offset + new_raw_size;
-
- const amt = try self.base.file.?.copyRangeAll(current_text_section_start, self.base.file.?, new_text_section_start, self.text_section_size);
- if (amt != self.text_section_size) return error.InputOutput;
-
- // Write the new raw size in the .got header
- var buf: [8]u8 = undefined;
- std.mem.writeIntLittle(u32, buf[0..4], new_raw_size);
- try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 16);
- // Write the new .text section file offset in the .text section header
- std.mem.writeIntLittle(u32, buf[0..4], new_text_section_start);
- try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 20);
-
- const current_virtual_size = std.mem.alignForwardGeneric(u32, self.offset_table_size, section_alignment);
- const new_virtual_size = std.mem.alignForwardGeneric(u32, new_raw_size, section_alignment);
- // If we had to move in the virtual address space, we need to fix the VAs in the offset table, as well as the virtual address of the `.text` section
- // and the virutal size of the `.got` section
-
- if (new_virtual_size != current_virtual_size) {
- log.debug("growing offset table from virtual size {} to {}\n", .{ current_virtual_size, new_virtual_size });
- self.size_of_image_dirty = true;
- const va_offset = new_virtual_size - current_virtual_size;
-
- // Write .got virtual size
- std.mem.writeIntLittle(u32, buf[0..4], new_virtual_size);
- try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 8);
-
- // Write .text new virtual address
- self.text_section_virtual_address = self.text_section_virtual_address + va_offset;
- std.mem.writeIntLittle(u32, buf[0..4], self.text_section_virtual_address - image_base);
- try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 12);
-
- // Fix the VAs in the offset table
- for (self.offset_table.items) |*va, idx| {
- if (va.* != 0) {
- va.* += va_offset;
-
- switch (entry_size) {
- 4 => {
- std.mem.writeInt(u32, buf[0..4], @intCast(u32, va.*), endian);
- try self.base.file.?.pwriteAll(buf[0..4], offset_table_start + idx * entry_size);
- },
- 8 => {
- std.mem.writeInt(u64, &buf, va.*, endian);
- try self.base.file.?.pwriteAll(&buf, offset_table_start + idx * entry_size);
- },
- else => unreachable,
- }
- }
- }
- }
- self.offset_table_size = new_raw_size;
- self.offset_table_size_dirty = false;
- }
- // Write the new entry
- switch (entry_size) {
- 4 => {
- var buf: [4]u8 = undefined;
- std.mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);
- try self.base.file.?.pwriteAll(&buf, offset_table_start + index * entry_size);
- },
- 8 => {
- var buf: [8]u8 = undefined;
- std.mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
- try self.base.file.?.pwriteAll(&buf, offset_table_start + index * entry_size);
- },
- else => unreachable,
- }
-}
-
-pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void {
- // TODO COFF/PE debug information
- // TODO Implement exports
- const tracy = trace(@src());
- defer tracy.end();
-
- var code_buffer = std.ArrayList(u8).init(self.base.allocator);
- defer code_buffer.deinit();
-
- const typed_value = decl.typed_value.most_recent.typed_value;
- const res = try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, .none);
- const code = switch (res) {
- .externally_managed => |x| x,
- .appended => code_buffer.items,
- .fail => |em| {
- decl.analysis = .codegen_failure;
- try module.failed_decls.put(module.gpa, decl, em);
- return;
- },
- };
-
- const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
- const curr_size = decl.link.coff.size;
- if (curr_size != 0) {
- const capacity = decl.link.coff.capacity();
- const need_realloc = code.len > capacity or
- !std.mem.isAlignedGeneric(u32, decl.link.coff.text_offset, required_alignment);
- if (need_realloc) {
- const curr_vaddr = self.getDeclVAddr(decl);
- const vaddr = try self.growTextBlock(&decl.link.coff, code.len, required_alignment);
- log.debug("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, curr_vaddr, vaddr });
- if (vaddr != curr_vaddr) {
- log.debug(" (writing new offset table entry)\n", .{});
- self.offset_table.items[decl.link.coff.offset_table_index] = vaddr;
- try self.writeOffsetTableEntry(decl.link.coff.offset_table_index);
- }
- } else if (code.len < curr_size) {
- self.shrinkTextBlock(&decl.link.coff, code.len);
- }
- } else {
- const vaddr = try self.allocateTextBlock(&decl.link.coff, code.len, required_alignment);
- log.debug("allocated text block for {} at 0x{x} (size: {Bi})\n", .{ std.mem.spanZ(decl.name), vaddr, code.len });
- errdefer self.freeTextBlock(&decl.link.coff);
- self.offset_table.items[decl.link.coff.offset_table_index] = vaddr;
- try self.writeOffsetTableEntry(decl.link.coff.offset_table_index);
- }
-
- // Write the code into the file
- try self.base.file.?.pwriteAll(code, self.section_data_offset + self.offset_table_size + decl.link.coff.text_offset);
-
- // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
- const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
- return self.updateDeclExports(module, decl, decl_exports);
-}
-
-pub fn freeDecl(self: *Coff, decl: *Module.Decl) void {
- // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
- self.freeTextBlock(&decl.link.coff);
- self.offset_table_free_list.append(self.base.allocator, decl.link.coff.offset_table_index) catch {};
-}
-
-pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl, exports: []const *Module.Export) !void {
- for (exports) |exp| {
- if (exp.options.section) |section_name| {
- if (!std.mem.eql(u8, section_name, ".text")) {
- try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
- module.failed_exports.putAssumeCapacityNoClobber(
- exp,
- try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
- );
- continue;
- }
- }
- if (std.mem.eql(u8, exp.options.name, "_start")) {
- self.entry_addr = decl.link.coff.getVAddr(self.*) - image_base;
- } else {
- try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
- module.failed_exports.putAssumeCapacityNoClobber(
- exp,
- try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: Exports other than '_start'", .{}),
- );
- continue;
- }
- }
-}
-
-pub fn flush(self: *Coff, comp: *Compilation) !void {
- if (build_options.have_llvm and self.base.options.use_lld) {
- return error.CoffLinkingWithLLDUnimplemented;
- } else {
- return self.flushModule(comp);
- }
-}
-
-pub fn flushModule(self: *Coff, comp: *Compilation) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- if (self.text_section_size_dirty) {
- // Write the new raw size in the .text header
- var buf: [4]u8 = undefined;
- std.mem.writeIntLittle(u32, &buf, self.text_section_size);
- try self.base.file.?.pwriteAll(&buf, self.section_table_offset + 40 + 16);
- try self.base.file.?.setEndPos(self.section_data_offset + self.offset_table_size + self.text_section_size);
- self.text_section_size_dirty = false;
- }
-
- if (self.base.options.output_mode == .Exe and self.size_of_image_dirty) {
- const new_size_of_image = std.mem.alignForwardGeneric(u32, self.text_section_virtual_address - image_base + self.text_section_size, section_alignment);
- var buf: [4]u8 = undefined;
- std.mem.writeIntLittle(u32, &buf, new_size_of_image);
- try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 56);
- self.size_of_image_dirty = false;
- }
-
- if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
- log.debug("flushing. no_entry_point_found = true\n", .{});
- self.error_flags.no_entry_point_found = true;
- } else {
- log.debug("flushing. no_entry_point_found = false\n", .{});
- self.error_flags.no_entry_point_found = false;
-
- if (self.base.options.output_mode == .Exe) {
- // Write AddressOfEntryPoint
- var buf: [4]u8 = undefined;
- std.mem.writeIntLittle(u32, &buf, self.entry_addr.?);
- try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 16);
- }
- }
-}
-
-pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 {
- return self.text_section_virtual_address + decl.link.coff.text_offset;
-}
-
-pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void {
- // TODO Implement this
-}
-
-pub fn deinit(self: *Coff) void {
- self.text_block_free_list.deinit(self.base.allocator);
- self.offset_table.deinit(self.base.allocator);
- self.offset_table_free_list.deinit(self.base.allocator);
-}
diff --git a/src-self-hosted/link/Elf.zig b/src-self-hosted/link/Elf.zig
deleted file mode 100644
index 98deefb3bf5b7eed734b3bfbe858b9f1e6b5a87b..0000000000000000000000000000000000000000
--- a/src-self-hosted/link/Elf.zig
+++ /dev/null
@@ -1,3059 +0,0 @@
-const Elf = @This();
-
-const std = @import("std");
-const mem = std.mem;
-const assert = std.debug.assert;
-const Allocator = std.mem.Allocator;
-const fs = std.fs;
-const elf = std.elf;
-const log = std.log.scoped(.link);
-const DW = std.dwarf;
-const leb128 = std.debug.leb;
-
-const ir = @import("../ir.zig");
-const Module = @import("../Module.zig");
-const Compilation = @import("../Compilation.zig");
-const codegen = @import("../codegen.zig");
-const trace = @import("../tracy.zig").trace;
-const Package = @import("../Package.zig");
-const Value = @import("../value.zig").Value;
-const Type = @import("../type.zig").Type;
-const link = @import("../link.zig");
-const File = link.File;
-const build_options = @import("build_options");
-const target_util = @import("../target.zig");
-const glibc = @import("../glibc.zig");
-
-const default_entry_addr = 0x8000000;
-
-// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented.
-// zig fmt: off
-
-pub const base_tag: File.Tag = .elf;
-
-base: File,
-
-ptr_width: PtrWidth,
-
-/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
-/// Same order as in the file.
-sections: std.ArrayListUnmanaged(elf.Elf64_Shdr) = std.ArrayListUnmanaged(elf.Elf64_Shdr){},
-shdr_table_offset: ?u64 = null,
-
-/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
-/// Same order as in the file.
-program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = std.ArrayListUnmanaged(elf.Elf64_Phdr){},
-phdr_table_offset: ?u64 = null,
-/// The index into the program headers of a PT_LOAD program header with Read and Execute flags
-phdr_load_re_index: ?u16 = null,
-/// The index into the program headers of the global offset table.
-/// It needs PT_LOAD and Read flags.
-phdr_got_index: ?u16 = null,
-entry_addr: ?u64 = null,
-
-debug_strtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
-shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
-shstrtab_index: ?u16 = null,
-
-text_section_index: ?u16 = null,
-symtab_section_index: ?u16 = null,
-got_section_index: ?u16 = null,
-debug_info_section_index: ?u16 = null,
-debug_abbrev_section_index: ?u16 = null,
-debug_str_section_index: ?u16 = null,
-debug_aranges_section_index: ?u16 = null,
-debug_line_section_index: ?u16 = null,
-
-debug_abbrev_table_offset: ?u64 = null,
-
-/// The same order as in the file. ELF requires global symbols to all be after the
-/// local symbols, they cannot be mixed. So we must buffer all the global symbols and
-/// write them at the end. These are only the local symbols. The length of this array
-/// is the value used for sh_info in the .symtab section.
-local_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
-global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
-
-local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
-global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
-offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
-
-/// Same order as in the file. The value is the absolute vaddr value.
-/// If the vaddr of the executable program header changes, the entire
-/// offset table needs to be rewritten.
-offset_table: std.ArrayListUnmanaged(u64) = .{},
-
-phdr_table_dirty: bool = false,
-shdr_table_dirty: bool = false,
-shstrtab_dirty: bool = false,
-debug_strtab_dirty: bool = false,
-offset_table_count_dirty: bool = false,
-debug_abbrev_section_dirty: bool = false,
-debug_aranges_section_dirty: bool = false,
-
-debug_info_header_dirty: bool = false,
-debug_line_header_dirty: bool = false,
-
-error_flags: File.ErrorFlags = File.ErrorFlags{},
-
-/// A list of text blocks that have surplus capacity. This list can have false
-/// positives, as functions grow and shrink over time, only sometimes being added
-/// or removed from the freelist.
-///
-/// A text block has surplus capacity when its overcapacity value is greater than
-/// minimum_text_block_size * alloc_num / alloc_den. That is, when it has so
-/// much extra capacity, that we could fit a small new symbol in it, itself with
-/// ideal_capacity or more.
-///
-/// Ideal capacity is defined by size * alloc_num / alloc_den.
-///
-/// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
-/// overcapacity can be negative. A simple way to have negative overcapacity is to
-/// allocate a fresh text block, which will have ideal capacity, and then grow it
-/// by 1 byte. It will then have -1 overcapacity.
-text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},
-last_text_block: ?*TextBlock = null,
-
-/// A list of `SrcFn` whose Line Number Programs have surplus capacity.
-/// This is the same concept as `text_block_free_list`; see those doc comments.
-dbg_line_fn_free_list: std.AutoHashMapUnmanaged(*SrcFn, void) = .{},
-dbg_line_fn_first: ?*SrcFn = null,
-dbg_line_fn_last: ?*SrcFn = null,
-
-/// A list of `TextBlock` whose corresponding .debug_info tags have surplus capacity.
-/// This is the same concept as `text_block_free_list`; see those doc comments.
-dbg_info_decl_free_list: std.AutoHashMapUnmanaged(*TextBlock, void) = .{},
-dbg_info_decl_first: ?*TextBlock = null,
-dbg_info_decl_last: ?*TextBlock = null,
-
-/// `alloc_num / alloc_den` is the factor of padding when allocating.
-const alloc_num = 4;
-const alloc_den = 3;
-
-/// In order for a slice of bytes to be considered eligible to keep metadata pointing at
-/// it as a possible place to put new symbols, it must have enough room for this many bytes
-/// (plus extra for reserved capacity).
-const minimum_text_block_size = 64;
-const min_text_capacity = minimum_text_block_size * alloc_num / alloc_den;
-
-pub const PtrWidth = enum { p32, p64 };
-
-pub const TextBlock = struct {
- /// Each decl always gets a local symbol with the fully qualified name.
- /// The vaddr and size are found here directly.
- /// The file offset is found by computing the vaddr offset from the section vaddr
- /// the symbol references, and adding that to the file offset of the section.
- /// If this field is 0, it means the codegen size = 0 and there is no symbol or
- /// offset table entry.
- local_sym_index: u32,
- /// This field is undefined for symbols with size = 0.
- offset_table_index: u32,
- /// Points to the previous and next neighbors, based on the `text_offset`.
- /// This can be used to find, for example, the capacity of this `TextBlock`.
- prev: ?*TextBlock,
- next: ?*TextBlock,
-
- /// Previous/next linked list pointers. This value is `next ^ prev`.
- /// This is the linked list node for this Decl's corresponding .debug_info tag.
- dbg_info_prev: ?*TextBlock,
- dbg_info_next: ?*TextBlock,
- /// Offset into .debug_info pointing to the tag for this Decl.
- dbg_info_off: u32,
- /// Size of the .debug_info tag for this Decl, not including padding.
- dbg_info_len: u32,
-
- pub const empty = TextBlock{
- .local_sym_index = 0,
- .offset_table_index = undefined,
- .prev = null,
- .next = null,
- .dbg_info_prev = null,
- .dbg_info_next = null,
- .dbg_info_off = undefined,
- .dbg_info_len = undefined,
- };
-
- /// Returns how much room there is to grow in virtual address space.
- /// File offset relocation happens transparently, so it is not included in
- /// this calculation.
- fn capacity(self: TextBlock, elf_file: Elf) u64 {
- const self_sym = elf_file.local_symbols.items[self.local_sym_index];
- if (self.next) |next| {
- const next_sym = elf_file.local_symbols.items[next.local_sym_index];
- return next_sym.st_value - self_sym.st_value;
- } else {
- // We are the last block. The capacity is limited only by virtual address space.
- return std.math.maxInt(u32) - self_sym.st_value;
- }
- }
-
- fn freeListEligible(self: TextBlock, elf_file: Elf) bool {
- // No need to keep a free list node for the last block.
- const next = self.next orelse return false;
- const self_sym = elf_file.local_symbols.items[self.local_sym_index];
- const next_sym = elf_file.local_symbols.items[next.local_sym_index];
- const cap = next_sym.st_value - self_sym.st_value;
- const ideal_cap = self_sym.st_size * alloc_num / alloc_den;
- if (cap <= ideal_cap) return false;
- const surplus = cap - ideal_cap;
- return surplus >= min_text_capacity;
- }
-};
-
-pub const Export = struct {
- sym_index: ?u32 = null,
-};
-
-pub const SrcFn = struct {
- /// Offset from the beginning of the Debug Line Program header that contains this function.
- off: u32,
- /// Size of the line number program component belonging to this function, not
- /// including padding.
- len: u32,
-
- /// Points to the previous and next neighbors, based on the offset from .debug_line.
- /// This can be used to find, for example, the capacity of this `SrcFn`.
- prev: ?*SrcFn,
- next: ?*SrcFn,
-
- pub const empty: SrcFn = .{
- .off = 0,
- .len = 0,
- .prev = null,
- .next = null,
- };
-};
-
-pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Elf {
- assert(options.object_format == .elf);
-
- if (options.use_llvm) return error.LLVMBackendUnimplementedForELF; // TODO
-
- const file = try options.directory.handle.createFile(sub_path, .{
- .truncate = false,
- .read = true,
- .mode = link.determineMode(options),
- });
- errdefer file.close();
-
- const self = try createEmpty(allocator, options);
- errdefer self.base.destroy();
-
- self.base.file = file;
- self.shdr_table_dirty = true;
-
- // Index 0 is always a null symbol.
- try self.local_symbols.append(allocator, .{
- .st_name = 0,
- .st_info = 0,
- .st_other = 0,
- .st_shndx = 0,
- .st_value = 0,
- .st_size = 0,
- });
-
- // There must always be a null section in index 0
- try self.sections.append(allocator, .{
- .sh_name = 0,
- .sh_type = elf.SHT_NULL,
- .sh_flags = 0,
- .sh_addr = 0,
- .sh_offset = 0,
- .sh_size = 0,
- .sh_link = 0,
- .sh_info = 0,
- .sh_addralign = 0,
- .sh_entsize = 0,
- });
-
- try self.populateMissingMetadata();
-
- return self;
-}
-
-pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Elf {
- const ptr_width: PtrWidth = switch (options.target.cpu.arch.ptrBitWidth()) {
- 0 ... 32 => .p32,
- 33 ... 64 => .p64,
- else => return error.UnsupportedELFArchitecture,
- };
- const self = try gpa.create(Elf);
- self.* = .{
- .base = .{
- .tag = .elf,
- .options = options,
- .allocator = gpa,
- .file = null,
- },
- .ptr_width = ptr_width,
- };
- return self;
-}
-
-pub fn deinit(self: *Elf) void {
- self.sections.deinit(self.base.allocator);
- self.program_headers.deinit(self.base.allocator);
- self.shstrtab.deinit(self.base.allocator);
- self.debug_strtab.deinit(self.base.allocator);
- self.local_symbols.deinit(self.base.allocator);
- self.global_symbols.deinit(self.base.allocator);
- self.global_symbol_free_list.deinit(self.base.allocator);
- self.local_symbol_free_list.deinit(self.base.allocator);
- self.offset_table_free_list.deinit(self.base.allocator);
- self.text_block_free_list.deinit(self.base.allocator);
- self.dbg_line_fn_free_list.deinit(self.base.allocator);
- self.dbg_info_decl_free_list.deinit(self.base.allocator);
- self.offset_table.deinit(self.base.allocator);
-}
-
-pub fn getDeclVAddr(self: *Elf, decl: *const Module.Decl) u64 {
- assert(decl.link.elf.local_sym_index != 0);
- return self.local_symbols.items[decl.link.elf.local_sym_index].st_value;
-}
-
-fn getDebugLineProgramOff(self: Elf) u32 {
- return self.dbg_line_fn_first.?.off;
-}
-
-fn getDebugLineProgramEnd(self: Elf) u32 {
- return self.dbg_line_fn_last.?.off + self.dbg_line_fn_last.?.len;
-}
-
-/// Returns end pos of collision, if any.
-fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
- const small_ptr = self.ptr_width == .p32;
- const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr);
- if (start < ehdr_size)
- return ehdr_size;
-
- const end = start + satMul(size, alloc_num) / alloc_den;
-
- if (self.shdr_table_offset) |off| {
- const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr);
- const tight_size = self.sections.items.len * shdr_size;
- const increased_size = satMul(tight_size, alloc_num) / alloc_den;
- const test_end = off + increased_size;
- if (end > off and start < test_end) {
- return test_end;
- }
- }
-
- if (self.phdr_table_offset) |off| {
- const phdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Phdr) else @sizeOf(elf.Elf64_Phdr);
- const tight_size = self.sections.items.len * phdr_size;
- const increased_size = satMul(tight_size, alloc_num) / alloc_den;
- const test_end = off + increased_size;
- if (end > off and start < test_end) {
- return test_end;
- }
- }
-
- for (self.sections.items) |section| {
- const increased_size = satMul(section.sh_size, alloc_num) / alloc_den;
- const test_end = section.sh_offset + increased_size;
- if (end > section.sh_offset and start < test_end) {
- return test_end;
- }
- }
- for (self.program_headers.items) |program_header| {
- const increased_size = satMul(program_header.p_filesz, alloc_num) / alloc_den;
- const test_end = program_header.p_offset + increased_size;
- if (end > program_header.p_offset and start < test_end) {
- return test_end;
- }
- }
- return null;
-}
-
-fn allocatedSize(self: *Elf, start: u64) u64 {
- if (start == 0)
- return 0;
- var min_pos: u64 = std.math.maxInt(u64);
- if (self.shdr_table_offset) |off| {
- if (off > start and off < min_pos) min_pos = off;
- }
- if (self.phdr_table_offset) |off| {
- if (off > start and off < min_pos) min_pos = off;
- }
- for (self.sections.items) |section| {
- if (section.sh_offset <= start) continue;
- if (section.sh_offset < min_pos) min_pos = section.sh_offset;
- }
- for (self.program_headers.items) |program_header| {
- if (program_header.p_offset <= start) continue;
- if (program_header.p_offset < min_pos) min_pos = program_header.p_offset;
- }
- return min_pos - start;
-}
-
-fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u16) u64 {
- var start: u64 = 0;
- while (self.detectAllocCollision(start, object_size)) |item_end| {
- start = mem.alignForwardGeneric(u64, item_end, min_alignment);
- }
- return start;
-}
-
-/// TODO Improve this to use a table.
-fn makeString(self: *Elf, bytes: []const u8) !u32 {
- try self.shstrtab.ensureCapacity(self.base.allocator, self.shstrtab.items.len + bytes.len + 1);
- const result = self.shstrtab.items.len;
- self.shstrtab.appendSliceAssumeCapacity(bytes);
- self.shstrtab.appendAssumeCapacity(0);
- return @intCast(u32, result);
-}
-
-/// TODO Improve this to use a table.
-fn makeDebugString(self: *Elf, bytes: []const u8) !u32 {
- try self.debug_strtab.ensureCapacity(self.base.allocator, self.debug_strtab.items.len + bytes.len + 1);
- const result = self.debug_strtab.items.len;
- self.debug_strtab.appendSliceAssumeCapacity(bytes);
- self.debug_strtab.appendAssumeCapacity(0);
- return @intCast(u32, result);
-}
-
-fn getString(self: *Elf, str_off: u32) []const u8 {
- assert(str_off < self.shstrtab.items.len);
- return mem.spanZ(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off));
-}
-
-fn updateString(self: *Elf, old_str_off: u32, new_name: []const u8) !u32 {
- const existing_name = self.getString(old_str_off);
- if (mem.eql(u8, existing_name, new_name)) {
- return old_str_off;
- }
- return self.makeString(new_name);
-}
-
-pub fn populateMissingMetadata(self: *Elf) !void {
- const small_ptr = switch (self.ptr_width) {
- .p32 => true,
- .p64 => false,
- };
- const ptr_size: u8 = self.ptrWidthBytes();
- if (self.phdr_load_re_index == null) {
- self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);
- const file_size = self.base.options.program_code_size_hint;
- const p_align = 0x1000;
- const off = self.findFreeSpace(file_size, p_align);
- log.debug("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
- const entry_addr: u64 = self.entry_addr orelse if (self.base.options.target.cpu.arch == .spu_2) @as(u64, 0) else default_entry_addr;
- try self.program_headers.append(self.base.allocator, .{
- .p_type = elf.PT_LOAD,
- .p_offset = off,
- .p_filesz = file_size,
- .p_vaddr = entry_addr,
- .p_paddr = entry_addr,
- .p_memsz = file_size,
- .p_align = p_align,
- .p_flags = elf.PF_X | elf.PF_R,
- });
- self.entry_addr = null;
- self.phdr_table_dirty = true;
- }
- if (self.phdr_got_index == null) {
- self.phdr_got_index = @intCast(u16, self.program_headers.items.len);
- const file_size = @as(u64, ptr_size) * self.base.options.symbol_count_hint;
- // We really only need ptr alignment but since we are using PROGBITS, linux requires
- // page align.
- const p_align = if (self.base.options.target.os.tag == .linux) 0x1000 else @as(u16, ptr_size);
- const off = self.findFreeSpace(file_size, p_align);
- log.debug("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
- // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.
- // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
- // else in virtual memory.
- const got_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0x4000000 else 0x8000;
- try self.program_headers.append(self.base.allocator, .{
- .p_type = elf.PT_LOAD,
- .p_offset = off,
- .p_filesz = file_size,
- .p_vaddr = got_addr,
- .p_paddr = got_addr,
- .p_memsz = file_size,
- .p_align = p_align,
- .p_flags = elf.PF_R,
- });
- self.phdr_table_dirty = true;
- }
- if (self.shstrtab_index == null) {
- self.shstrtab_index = @intCast(u16, self.sections.items.len);
- assert(self.shstrtab.items.len == 0);
- try self.shstrtab.append(self.base.allocator, 0); // need a 0 at position 0
- const off = self.findFreeSpace(self.shstrtab.items.len, 1);
- log.debug("found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len });
- try self.sections.append(self.base.allocator, .{
- .sh_name = try self.makeString(".shstrtab"),
- .sh_type = elf.SHT_STRTAB,
- .sh_flags = 0,
- .sh_addr = 0,
- .sh_offset = off,
- .sh_size = self.shstrtab.items.len,
- .sh_link = 0,
- .sh_info = 0,
- .sh_addralign = 1,
- .sh_entsize = 0,
- });
- self.shstrtab_dirty = true;
- self.shdr_table_dirty = true;
- }
- if (self.text_section_index == null) {
- self.text_section_index = @intCast(u16, self.sections.items.len);
- const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
-
- try self.sections.append(self.base.allocator, .{
- .sh_name = try self.makeString(".text"),
- .sh_type = elf.SHT_PROGBITS,
- .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
- .sh_addr = phdr.p_vaddr,
- .sh_offset = phdr.p_offset,
- .sh_size = phdr.p_filesz,
- .sh_link = 0,
- .sh_info = 0,
- .sh_addralign = phdr.p_align,
- .sh_entsize = 0,
- });
- self.shdr_table_dirty = true;
- }
- if (self.got_section_index == null) {
- self.got_section_index = @intCast(u16, self.sections.items.len);
- const phdr = &self.program_headers.items[self.phdr_got_index.?];
-
- try self.sections.append(self.base.allocator, .{
- .sh_name = try self.makeString(".got"),
- .sh_type = elf.SHT_PROGBITS,
- .sh_flags = elf.SHF_ALLOC,
- .sh_addr = phdr.p_vaddr,
- .sh_offset = phdr.p_offset,
- .sh_size = phdr.p_filesz,
- .sh_link = 0,
- .sh_info = 0,
- .sh_addralign = phdr.p_align,
- .sh_entsize = 0,
- });
- self.shdr_table_dirty = true;
- }
- if (self.symtab_section_index == null) {
- self.symtab_section_index = @intCast(u16, self.sections.items.len);
- const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
- const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);
- const file_size = self.base.options.symbol_count_hint * each_size;
- const off = self.findFreeSpace(file_size, min_align);
- log.debug("found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
-
- try self.sections.append(self.base.allocator, .{
- .sh_name = try self.makeString(".symtab"),
- .sh_type = elf.SHT_SYMTAB,
- .sh_flags = 0,
- .sh_addr = 0,
- .sh_offset = off,
- .sh_size = file_size,
- // The section header index of the associated string table.
- .sh_link = self.shstrtab_index.?,
- .sh_info = @intCast(u32, self.local_symbols.items.len),
- .sh_addralign = min_align,
- .sh_entsize = each_size,
- });
- self.shdr_table_dirty = true;
- try self.writeSymbol(0);
- }
- if (self.debug_str_section_index == null) {
- self.debug_str_section_index = @intCast(u16, self.sections.items.len);
- assert(self.debug_strtab.items.len == 0);
- try self.sections.append(self.base.allocator, .{
- .sh_name = try self.makeString(".debug_str"),
- .sh_type = elf.SHT_PROGBITS,
- .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS,
- .sh_addr = 0,
- .sh_offset = 0,
- .sh_size = self.debug_strtab.items.len,
- .sh_link = 0,
- .sh_info = 0,
- .sh_addralign = 1,
- .sh_entsize = 1,
- });
- self.debug_strtab_dirty = true;
- self.shdr_table_dirty = true;
- }
- if (self.debug_info_section_index == null) {
- self.debug_info_section_index = @intCast(u16, self.sections.items.len);
-
- const file_size_hint = 200;
- const p_align = 1;
- const off = self.findFreeSpace(file_size_hint, p_align);
- log.debug("found .debug_info free space 0x{x} to 0x{x}\n", .{
- off,
- off + file_size_hint,
- });
- try self.sections.append(self.base.allocator, .{
- .sh_name = try self.makeString(".debug_info"),
- .sh_type = elf.SHT_PROGBITS,
- .sh_flags = 0,
- .sh_addr = 0,
- .sh_offset = off,
- .sh_size = file_size_hint,
- .sh_link = 0,
- .sh_info = 0,
- .sh_addralign = p_align,
- .sh_entsize = 0,
- });
- self.shdr_table_dirty = true;
- self.debug_info_header_dirty = true;
- }
- if (self.debug_abbrev_section_index == null) {
- self.debug_abbrev_section_index = @intCast(u16, self.sections.items.len);
-
- const file_size_hint = 128;
- const p_align = 1;
- const off = self.findFreeSpace(file_size_hint, p_align);
- log.debug("found .debug_abbrev free space 0x{x} to 0x{x}\n", .{
- off,
- off + file_size_hint,
- });
- try self.sections.append(self.base.allocator, .{
- .sh_name = try self.makeString(".debug_abbrev"),
- .sh_type = elf.SHT_PROGBITS,
- .sh_flags = 0,
- .sh_addr = 0,
- .sh_offset = off,
- .sh_size = file_size_hint,
- .sh_link = 0,
- .sh_info = 0,
- .sh_addralign = p_align,
- .sh_entsize = 0,
- });
- self.shdr_table_dirty = true;
- self.debug_abbrev_section_dirty = true;
- }
- if (self.debug_aranges_section_index == null) {
- self.debug_aranges_section_index = @intCast(u16, self.sections.items.len);
-
- const file_size_hint = 160;
- const p_align = 16;
- const off = self.findFreeSpace(file_size_hint, p_align);
- log.debug("found .debug_aranges free space 0x{x} to 0x{x}\n", .{
- off,
- off + file_size_hint,
- });
- try self.sections.append(self.base.allocator, .{
- .sh_name = try self.makeString(".debug_aranges"),
- .sh_type = elf.SHT_PROGBITS,
- .sh_flags = 0,
- .sh_addr = 0,
- .sh_offset = off,
- .sh_size = file_size_hint,
- .sh_link = 0,
- .sh_info = 0,
- .sh_addralign = p_align,
- .sh_entsize = 0,
- });
- self.shdr_table_dirty = true;
- self.debug_aranges_section_dirty = true;
- }
- if (self.debug_line_section_index == null) {
- self.debug_line_section_index = @intCast(u16, self.sections.items.len);
-
- const file_size_hint = 250;
- const p_align = 1;
- const off = self.findFreeSpace(file_size_hint, p_align);
- log.debug("found .debug_line free space 0x{x} to 0x{x}\n", .{
- off,
- off + file_size_hint,
- });
- try self.sections.append(self.base.allocator, .{
- .sh_name = try self.makeString(".debug_line"),
- .sh_type = elf.SHT_PROGBITS,
- .sh_flags = 0,
- .sh_addr = 0,
- .sh_offset = off,
- .sh_size = file_size_hint,
- .sh_link = 0,
- .sh_info = 0,
- .sh_addralign = p_align,
- .sh_entsize = 0,
- });
- self.shdr_table_dirty = true;
- self.debug_line_header_dirty = true;
- }
- const shsize: u64 = switch (self.ptr_width) {
- .p32 => @sizeOf(elf.Elf32_Shdr),
- .p64 => @sizeOf(elf.Elf64_Shdr),
- };
- const shalign: u16 = switch (self.ptr_width) {
- .p32 => @alignOf(elf.Elf32_Shdr),
- .p64 => @alignOf(elf.Elf64_Shdr),
- };
- if (self.shdr_table_offset == null) {
- self.shdr_table_offset = self.findFreeSpace(self.sections.items.len * shsize, shalign);
- self.shdr_table_dirty = true;
- }
- const phsize: u64 = switch (self.ptr_width) {
- .p32 => @sizeOf(elf.Elf32_Phdr),
- .p64 => @sizeOf(elf.Elf64_Phdr),
- };
- const phalign: u16 = switch (self.ptr_width) {
- .p32 => @alignOf(elf.Elf32_Phdr),
- .p64 => @alignOf(elf.Elf64_Phdr),
- };
- if (self.phdr_table_offset == null) {
- self.phdr_table_offset = self.findFreeSpace(self.program_headers.items.len * phsize, phalign);
- self.phdr_table_dirty = true;
- }
- {
- // Iterate over symbols, populating free_list and last_text_block.
- if (self.local_symbols.items.len != 1) {
- @panic("TODO implement setting up free_list and last_text_block from existing ELF file");
- }
- // We are starting with an empty file. The default values are correct, null and empty list.
- }
-}
-
-pub const abbrev_compile_unit = 1;
-pub const abbrev_subprogram = 2;
-pub const abbrev_subprogram_retvoid = 3;
-pub const abbrev_base_type = 4;
-pub const abbrev_pad1 = 5;
-pub const abbrev_parameter = 6;
-
-pub fn flush(self: *Elf, comp: *Compilation) !void {
- if (build_options.have_llvm and self.base.options.use_lld) {
- return self.linkWithLLD(comp);
- } else {
- switch (self.base.options.effectiveOutputMode()) {
- .Exe, .Obj => {},
- .Lib => return error.TODOImplementWritingLibFiles,
- }
- return self.flushModule(comp);
- }
-}
-
-pub fn flushModule(self: *Elf, comp: *Compilation) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- // TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the
- // Zig source code.
- const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
-
- const target_endian = self.base.options.target.cpu.arch.endian();
- const foreign_endian = target_endian != std.Target.current.cpu.arch.endian();
- const ptr_width_bytes: u8 = self.ptrWidthBytes();
- const init_len_size: usize = switch (self.ptr_width) {
- .p32 => 4,
- .p64 => 12,
- };
-
- // Unfortunately these have to be buffered and done at the end because ELF does not allow
- // mixing local and global symbols within a symbol table.
- try self.writeAllGlobalSymbols();
-
- if (self.debug_abbrev_section_dirty) {
- const debug_abbrev_sect = &self.sections.items[self.debug_abbrev_section_index.?];
-
- // These are LEB encoded but since the values are all less than 127
- // we can simply append these bytes.
- const abbrev_buf = [_]u8{
- abbrev_compile_unit, DW.TAG_compile_unit, DW.CHILDREN_yes, // header
- DW.AT_stmt_list, DW.FORM_sec_offset, DW.AT_low_pc,
- DW.FORM_addr, DW.AT_high_pc, DW.FORM_addr,
- DW.AT_name, DW.FORM_strp, DW.AT_comp_dir,
- DW.FORM_strp, DW.AT_producer, DW.FORM_strp,
- DW.AT_language, DW.FORM_data2, 0,
- 0, // table sentinel
- abbrev_subprogram, DW.TAG_subprogram,
- DW.CHILDREN_yes, // header
- DW.AT_low_pc, DW.FORM_addr,
- DW.AT_high_pc, DW.FORM_data4, DW.AT_type,
- DW.FORM_ref4, DW.AT_name, DW.FORM_string,
- 0, 0, // table sentinel
- abbrev_subprogram_retvoid,
- DW.TAG_subprogram, DW.CHILDREN_yes, // header
- DW.AT_low_pc,
- DW.FORM_addr, DW.AT_high_pc, DW.FORM_data4,
- DW.AT_name, DW.FORM_string, 0,
- 0, // table sentinel
- abbrev_base_type, DW.TAG_base_type,
- DW.CHILDREN_no, // header
- DW.AT_encoding, DW.FORM_data1,
- DW.AT_byte_size, DW.FORM_data1, DW.AT_name,
- DW.FORM_string, 0, 0, // table sentinel
-
- abbrev_pad1, DW.TAG_unspecified_type, DW.CHILDREN_no, // header
- 0, 0, // table sentinel
- abbrev_parameter,
- DW.TAG_formal_parameter, DW.CHILDREN_no, // header
- DW.AT_location,
- DW.FORM_exprloc, DW.AT_type, DW.FORM_ref4,
- DW.AT_name, DW.FORM_string, 0,
- 0, // table sentinel
- 0, 0,
- 0, // section sentinel
- };
-
- const needed_size = abbrev_buf.len;
- const allocated_size = self.allocatedSize(debug_abbrev_sect.sh_offset);
- if (needed_size > allocated_size) {
- debug_abbrev_sect.sh_size = 0; // free the space
- debug_abbrev_sect.sh_offset = self.findFreeSpace(needed_size, 1);
- }
- debug_abbrev_sect.sh_size = needed_size;
- log.debug(".debug_abbrev start=0x{x} end=0x{x}\n", .{
- debug_abbrev_sect.sh_offset,
- debug_abbrev_sect.sh_offset + needed_size,
- });
-
- const abbrev_offset = 0;
- self.debug_abbrev_table_offset = abbrev_offset;
- try self.base.file.?.pwriteAll(&abbrev_buf, debug_abbrev_sect.sh_offset + abbrev_offset);
- if (!self.shdr_table_dirty) {
- // Then it won't get written with the others and we need to do it.
- try self.writeSectHeader(self.debug_abbrev_section_index.?);
- }
-
- self.debug_abbrev_section_dirty = false;
- }
-
- if (self.debug_info_header_dirty) debug_info: {
- // If this value is null it means there is an error in the module;
- // leave debug_info_header_dirty=true.
- const first_dbg_info_decl = self.dbg_info_decl_first orelse break :debug_info;
- const last_dbg_info_decl = self.dbg_info_decl_last.?;
- const debug_info_sect = &self.sections.items[self.debug_info_section_index.?];
-
- var di_buf = std.ArrayList(u8).init(self.base.allocator);
- defer di_buf.deinit();
-
- // We have a function to compute the upper bound size, because it's needed
- // for determining where to put the offset of the first `LinkBlock`.
- try di_buf.ensureCapacity(self.dbgInfoNeededHeaderBytes());
-
- // initial length - length of the .debug_info contribution for this compilation unit,
- // not including the initial length itself.
- // We have to come back and write it later after we know the size.
- const after_init_len = di_buf.items.len + init_len_size;
- // +1 for the final 0 that ends the compilation unit children.
- const dbg_info_end = last_dbg_info_decl.dbg_info_off + last_dbg_info_decl.dbg_info_len + 1;
- const init_len = dbg_info_end - after_init_len;
- switch (self.ptr_width) {
- .p32 => {
- mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len), target_endian);
- },
- .p64 => {
- di_buf.appendNTimesAssumeCapacity(0xff, 4);
- mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), init_len, target_endian);
- },
- }
- mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // DWARF version
- const abbrev_offset = self.debug_abbrev_table_offset.?;
- switch (self.ptr_width) {
- .p32 => {
- mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, abbrev_offset), target_endian);
- di_buf.appendAssumeCapacity(4); // address size
- },
- .p64 => {
- mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), abbrev_offset, target_endian);
- di_buf.appendAssumeCapacity(8); // address size
- },
- }
- // Write the form for the compile unit, which must match the abbrev table above.
- const name_strp = try self.makeDebugString(module.root_pkg.root_src_path);
- const comp_dir_strp = try self.makeDebugString(module.root_pkg.root_src_directory.path.?);
- const producer_strp = try self.makeDebugString(link.producer_string);
- // Currently only one compilation unit is supported, so the address range is simply
- // identical to the main program header virtual address and memory size.
- const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];
- const low_pc = text_phdr.p_vaddr;
- const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz;
-
- di_buf.appendAssumeCapacity(abbrev_compile_unit);
- self.writeDwarfAddrAssumeCapacity(&di_buf, 0); // DW.AT_stmt_list, DW.FORM_sec_offset
- self.writeDwarfAddrAssumeCapacity(&di_buf, low_pc);
- self.writeDwarfAddrAssumeCapacity(&di_buf, high_pc);
- self.writeDwarfAddrAssumeCapacity(&di_buf, name_strp);
- self.writeDwarfAddrAssumeCapacity(&di_buf, comp_dir_strp);
- self.writeDwarfAddrAssumeCapacity(&di_buf, producer_strp);
- // We are still waiting on dwarf-std.org to assign DW_LANG_Zig a number:
- // http://dwarfstd.org/ShowIssue.php?issue=171115.1
- // Until then we say it is C99.
- mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), DW.LANG_C99, target_endian);
-
- if (di_buf.items.len > first_dbg_info_decl.dbg_info_off) {
- // Move the first N decls to the end to make more padding for the header.
- @panic("TODO: handle .debug_info header exceeding its padding");
- }
- const jmp_amt = first_dbg_info_decl.dbg_info_off - di_buf.items.len;
- try self.pwriteDbgInfoNops(0, di_buf.items, jmp_amt, false, debug_info_sect.sh_offset);
- self.debug_info_header_dirty = false;
- }
-
- if (self.debug_aranges_section_dirty) {
- const debug_aranges_sect = &self.sections.items[self.debug_aranges_section_index.?];
-
- var di_buf = std.ArrayList(u8).init(self.base.allocator);
- defer di_buf.deinit();
-
- // Enough for all the data without resizing. When support for more compilation units
- // is added, the size of this section will become more variable.
- try di_buf.ensureCapacity(100);
-
- // initial length - length of the .debug_aranges contribution for this compilation unit,
- // not including the initial length itself.
- // We have to come back and write it later after we know the size.
- const init_len_index = di_buf.items.len;
- di_buf.items.len += init_len_size;
- const after_init_len = di_buf.items.len;
- mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 2, target_endian); // version
- // When more than one compilation unit is supported, this will be the offset to it.
- // For now it is always at offset 0 in .debug_info.
- self.writeDwarfAddrAssumeCapacity(&di_buf, 0); // .debug_info offset
- di_buf.appendAssumeCapacity(ptr_width_bytes); // address_size
- di_buf.appendAssumeCapacity(0); // segment_selector_size
-
- const end_header_offset = di_buf.items.len;
- const begin_entries_offset = mem.alignForward(end_header_offset, ptr_width_bytes * 2);
- di_buf.appendNTimesAssumeCapacity(0, begin_entries_offset - end_header_offset);
-
- // Currently only one compilation unit is supported, so the address range is simply
- // identical to the main program header virtual address and memory size.
- const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];
- self.writeDwarfAddrAssumeCapacity(&di_buf, text_phdr.p_vaddr);
- self.writeDwarfAddrAssumeCapacity(&di_buf, text_phdr.p_memsz);
-
- // Sentinel.
- self.writeDwarfAddrAssumeCapacity(&di_buf, 0);
- self.writeDwarfAddrAssumeCapacity(&di_buf, 0);
-
- // Go back and populate the initial length.
- const init_len = di_buf.items.len - after_init_len;
- switch (self.ptr_width) {
- .p32 => {
- mem.writeInt(u32, di_buf.items[init_len_index..][0..4], @intCast(u32, init_len), target_endian);
- },
- .p64 => {
- // initial length - length of the .debug_aranges contribution for this compilation unit,
- // not including the initial length itself.
- di_buf.items[init_len_index..][0..4].* = [_]u8{ 0xff, 0xff, 0xff, 0xff };
- mem.writeInt(u64, di_buf.items[init_len_index + 4 ..][0..8], init_len, target_endian);
- },
- }
-
- const needed_size = di_buf.items.len;
- const allocated_size = self.allocatedSize(debug_aranges_sect.sh_offset);
- if (needed_size > allocated_size) {
- debug_aranges_sect.sh_size = 0; // free the space
- debug_aranges_sect.sh_offset = self.findFreeSpace(needed_size, 16);
- }
- debug_aranges_sect.sh_size = needed_size;
- log.debug(".debug_aranges start=0x{x} end=0x{x}\n", .{
- debug_aranges_sect.sh_offset,
- debug_aranges_sect.sh_offset + needed_size,
- });
-
- try self.base.file.?.pwriteAll(di_buf.items, debug_aranges_sect.sh_offset);
- if (!self.shdr_table_dirty) {
- // Then it won't get written with the others and we need to do it.
- try self.writeSectHeader(self.debug_aranges_section_index.?);
- }
-
- self.debug_aranges_section_dirty = false;
- }
- if (self.debug_line_header_dirty) debug_line: {
- if (self.dbg_line_fn_first == null) {
- break :debug_line; // Error in module; leave debug_line_header_dirty=true.
- }
- const dbg_line_prg_off = self.getDebugLineProgramOff();
- const dbg_line_prg_end = self.getDebugLineProgramEnd();
- assert(dbg_line_prg_end != 0);
-
- const debug_line_sect = &self.sections.items[self.debug_line_section_index.?];
-
- var di_buf = std.ArrayList(u8).init(self.base.allocator);
- defer di_buf.deinit();
-
- // The size of this header is variable, depending on the number of directories,
- // files, and padding. We have a function to compute the upper bound size, however,
- // because it's needed for determining where to put the offset of the first `SrcFn`.
- try di_buf.ensureCapacity(self.dbgLineNeededHeaderBytes());
-
- // initial length - length of the .debug_line contribution for this compilation unit,
- // not including the initial length itself.
- const after_init_len = di_buf.items.len + init_len_size;
- const init_len = dbg_line_prg_end - after_init_len;
- switch (self.ptr_width) {
- .p32 => {
- mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len), target_endian);
- },
- .p64 => {
- di_buf.appendNTimesAssumeCapacity(0xff, 4);
- mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), init_len, target_endian);
- },
- }
-
- mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // version
-
- // Empirically, debug info consumers do not respect this field, or otherwise
- // consider it to be an error when it does not point exactly to the end of the header.
- // Therefore we rely on the NOP jump at the beginning of the Line Number Program for
- // padding rather than this field.
- const before_header_len = di_buf.items.len;
- di_buf.items.len += ptr_width_bytes; // We will come back and write this.
- const after_header_len = di_buf.items.len;
-
- const opcode_base = DW.LNS_set_isa + 1;
- di_buf.appendSliceAssumeCapacity(&[_]u8{
- 1, // minimum_instruction_length
- 1, // maximum_operations_per_instruction
- 1, // default_is_stmt
- 1, // line_base (signed)
- 1, // line_range
- opcode_base,
-
- // Standard opcode lengths. The number of items here is based on `opcode_base`.
- // The value is the number of LEB128 operands the instruction takes.
- 0, // `DW.LNS_copy`
- 1, // `DW.LNS_advance_pc`
- 1, // `DW.LNS_advance_line`
- 1, // `DW.LNS_set_file`
- 1, // `DW.LNS_set_column`
- 0, // `DW.LNS_negate_stmt`
- 0, // `DW.LNS_set_basic_block`
- 0, // `DW.LNS_const_add_pc`
- 1, // `DW.LNS_fixed_advance_pc`
- 0, // `DW.LNS_set_prologue_end`
- 0, // `DW.LNS_set_epilogue_begin`
- 1, // `DW.LNS_set_isa`
-
- 0, // include_directories (none except the compilation unit cwd)
- });
- // file_names[0]
- di_buf.appendSliceAssumeCapacity(module.root_pkg.root_src_path); // relative path name
- di_buf.appendSliceAssumeCapacity(&[_]u8{
- 0, // null byte for the relative path name
- 0, // directory_index
- 0, // mtime (TODO supply this)
- 0, // file size bytes (TODO supply this)
- 0, // file_names sentinel
- });
-
- const header_len = di_buf.items.len - after_header_len;
- switch (self.ptr_width) {
- .p32 => {
- mem.writeInt(u32, di_buf.items[before_header_len..][0..4], @intCast(u32, header_len), target_endian);
- },
- .p64 => {
- mem.writeInt(u64, di_buf.items[before_header_len..][0..8], header_len, target_endian);
- },
- }
-
- // We use NOPs because consumers empirically do not respect the header length field.
- if (di_buf.items.len > dbg_line_prg_off) {
- // Move the first N files to the end to make more padding for the header.
- @panic("TODO: handle .debug_line header exceeding its padding");
- }
- const jmp_amt = dbg_line_prg_off - di_buf.items.len;
- try self.pwriteDbgLineNops(0, di_buf.items, jmp_amt, debug_line_sect.sh_offset);
- self.debug_line_header_dirty = false;
- }
-
- if (self.phdr_table_dirty) {
- const phsize: u64 = switch (self.ptr_width) {
- .p32 => @sizeOf(elf.Elf32_Phdr),
- .p64 => @sizeOf(elf.Elf64_Phdr),
- };
- const phalign: u16 = switch (self.ptr_width) {
- .p32 => @alignOf(elf.Elf32_Phdr),
- .p64 => @alignOf(elf.Elf64_Phdr),
- };
- const allocated_size = self.allocatedSize(self.phdr_table_offset.?);
- const needed_size = self.program_headers.items.len * phsize;
-
- if (needed_size > allocated_size) {
- self.phdr_table_offset = null; // free the space
- self.phdr_table_offset = self.findFreeSpace(needed_size, phalign);
- }
-
- switch (self.ptr_width) {
- .p32 => {
- const buf = try self.base.allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len);
- defer self.base.allocator.free(buf);
-
- for (buf) |*phdr, i| {
- phdr.* = progHeaderTo32(self.program_headers.items[i]);
- if (foreign_endian) {
- bswapAllFields(elf.Elf32_Phdr, phdr);
- }
- }
- try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
- },
- .p64 => {
- const buf = try self.base.allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len);
- defer self.base.allocator.free(buf);
-
- for (buf) |*phdr, i| {
- phdr.* = self.program_headers.items[i];
- if (foreign_endian) {
- bswapAllFields(elf.Elf64_Phdr, phdr);
- }
- }
- try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
- },
- }
- self.phdr_table_dirty = false;
- }
-
- {
- const shstrtab_sect = &self.sections.items[self.shstrtab_index.?];
- if (self.shstrtab_dirty or self.shstrtab.items.len != shstrtab_sect.sh_size) {
- const allocated_size = self.allocatedSize(shstrtab_sect.sh_offset);
- const needed_size = self.shstrtab.items.len;
-
- if (needed_size > allocated_size) {
- shstrtab_sect.sh_size = 0; // free the space
- shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
- }
- shstrtab_sect.sh_size = needed_size;
- log.debug("writing shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size });
-
- try self.base.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);
- if (!self.shdr_table_dirty) {
- // Then it won't get written with the others and we need to do it.
- try self.writeSectHeader(self.shstrtab_index.?);
- }
- self.shstrtab_dirty = false;
- }
- }
- {
- const debug_strtab_sect = &self.sections.items[self.debug_str_section_index.?];
- if (self.debug_strtab_dirty or self.debug_strtab.items.len != debug_strtab_sect.sh_size) {
- const allocated_size = self.allocatedSize(debug_strtab_sect.sh_offset);
- const needed_size = self.debug_strtab.items.len;
-
- if (needed_size > allocated_size) {
- debug_strtab_sect.sh_size = 0; // free the space
- debug_strtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
- }
- debug_strtab_sect.sh_size = needed_size;
- log.debug("debug_strtab start=0x{x} end=0x{x}\n", .{ debug_strtab_sect.sh_offset, debug_strtab_sect.sh_offset + needed_size });
-
- try self.base.file.?.pwriteAll(self.debug_strtab.items, debug_strtab_sect.sh_offset);
- if (!self.shdr_table_dirty) {
- // Then it won't get written with the others and we need to do it.
- try self.writeSectHeader(self.debug_str_section_index.?);
- }
- self.debug_strtab_dirty = false;
- }
- }
- if (self.shdr_table_dirty) {
- const shsize: u64 = switch (self.ptr_width) {
- .p32 => @sizeOf(elf.Elf32_Shdr),
- .p64 => @sizeOf(elf.Elf64_Shdr),
- };
- const shalign: u16 = switch (self.ptr_width) {
- .p32 => @alignOf(elf.Elf32_Shdr),
- .p64 => @alignOf(elf.Elf64_Shdr),
- };
- const allocated_size = self.allocatedSize(self.shdr_table_offset.?);
- const needed_size = self.sections.items.len * shsize;
-
- if (needed_size > allocated_size) {
- self.shdr_table_offset = null; // free the space
- self.shdr_table_offset = self.findFreeSpace(needed_size, shalign);
- }
-
- switch (self.ptr_width) {
- .p32 => {
- const buf = try self.base.allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);
- defer self.base.allocator.free(buf);
-
- for (buf) |*shdr, i| {
- shdr.* = sectHeaderTo32(self.sections.items[i]);
- log.debug("writing section {}\n", .{shdr.*});
- if (foreign_endian) {
- bswapAllFields(elf.Elf32_Shdr, shdr);
- }
- }
- try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
- },
- .p64 => {
- const buf = try self.base.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);
- defer self.base.allocator.free(buf);
-
- for (buf) |*shdr, i| {
- shdr.* = self.sections.items[i];
- log.debug("writing section {}\n", .{shdr.*});
- if (foreign_endian) {
- bswapAllFields(elf.Elf64_Shdr, shdr);
- }
- }
- try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
- },
- }
- self.shdr_table_dirty = false;
- }
- if (self.entry_addr == null and self.base.options.effectiveOutputMode() == .Exe) {
- log.debug("flushing. no_entry_point_found = true\n", .{});
- self.error_flags.no_entry_point_found = true;
- } else {
- log.debug("flushing. no_entry_point_found = false\n", .{});
- self.error_flags.no_entry_point_found = false;
- try self.writeElfHeader();
- }
-
- // The point of flush() is to commit changes, so in theory, nothing should
- // be dirty after this. However, it is possible for some things to remain
- // dirty because they fail to be written in the event of compile errors,
- // such as debug_line_header_dirty and debug_info_header_dirty.
- assert(!self.debug_abbrev_section_dirty);
- assert(!self.debug_aranges_section_dirty);
- assert(!self.phdr_table_dirty);
- assert(!self.shdr_table_dirty);
- assert(!self.shstrtab_dirty);
- assert(!self.debug_strtab_dirty);
-}
-
-fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
- defer arena_allocator.deinit();
- const arena = &arena_allocator.allocator;
-
- const directory = self.base.options.directory; // Just an alias to make it shorter to type.
-
- // If there is no Zig code to compile, then we should skip flushing the output file because it
- // will not be part of the linker line anyway.
- const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {
- const use_stage1 = build_options.is_stage1 and self.base.options.use_llvm;
- if (use_stage1) {
- const obj_basename = try std.fmt.allocPrint(arena, "{}.o", .{self.base.options.root_name});
- const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename});
- break :blk full_obj_path;
- }
-
- try self.flushModule(comp);
- const obj_basename = self.base.intermediary_basename.?;
- const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename});
- break :blk full_obj_path;
- } else null;
-
- // Here we want to determine whether we can save time by not invoking LLD when the
- // output is unchanged. None of the linker options or the object files that are being
- // linked are in the hash that namespaces the directory we are outputting to. Therefore,
- // we must hash those now, and the resulting digest will form the "id" of the linking
- // job we are about to perform.
- // After a successful link, we store the id in the metadata of a symlink named "id.txt" in
- // the artifact directory. So, now, we check if this symlink exists, and if it matches
- // our digest. If so, we can skip linking. Otherwise, we proceed with invoking LLD.
- const id_symlink_basename = "lld.id";
-
- // We are about to obtain this lock, so here we give other processes a chance first.
- self.base.releaseLock();
-
- var ch = comp.cache_parent.obtain();
- defer ch.deinit();
-
- const is_lib = self.base.options.output_mode == .Lib;
- const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
- const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
- const have_dynamic_linker = self.base.options.link_libc and
- self.base.options.link_mode == .Dynamic and is_exe_or_dyn_lib;
-
- try ch.addOptionalFile(self.base.options.linker_script);
- try ch.addOptionalFile(self.base.options.version_script);
- try ch.addListOfFiles(self.base.options.objects);
- for (comp.c_object_table.items()) |entry| {
- _ = try ch.addFile(entry.key.status.success.object_path, null);
- }
- try ch.addOptionalFile(module_obj_path);
- // We can skip hashing libc and libc++ components that we are in charge of building from Zig
- // installation sources because they are always a product of the compiler version + target information.
- ch.hash.addOptional(self.base.options.stack_size_override);
- ch.hash.addOptional(self.base.options.gc_sections);
- ch.hash.add(self.base.options.eh_frame_hdr);
- ch.hash.add(self.base.options.rdynamic);
- ch.hash.addListOfBytes(self.base.options.extra_lld_args);
- ch.hash.addListOfBytes(self.base.options.lib_dirs);
- ch.hash.add(self.base.options.z_nodelete);
- ch.hash.add(self.base.options.z_defs);
- if (self.base.options.link_libc) {
- ch.hash.add(self.base.options.libc_installation != null);
- if (self.base.options.libc_installation) |libc_installation| {
- ch.hash.addBytes(libc_installation.crt_dir.?);
- }
- if (have_dynamic_linker) {
- ch.hash.addOptionalBytes(self.base.options.dynamic_linker);
- }
- }
- if (is_dyn_lib) {
- ch.hash.addOptionalBytes(self.base.options.override_soname);
- ch.hash.addOptional(self.base.options.version);
- }
- ch.hash.addListOfBytes(self.base.options.system_libs);
- ch.hash.addOptional(self.base.options.allow_shlib_undefined);
- ch.hash.add(self.base.options.bind_global_refs_locally);
-
- // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
- _ = try ch.hit();
- const digest = ch.final();
-
- var prev_digest_buf: [digest.len]u8 = undefined;
- const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: {
- log.debug("ELF LLD new_digest={} readlink error: {}", .{digest, @errorName(err)});
- // Handle this as a cache miss.
- break :blk prev_digest_buf[0..0];
- };
- if (mem.eql(u8, prev_digest, &digest)) {
- log.debug("ELF LLD digest={} match - skipping invocation", .{digest});
- // Hot diggity dog! The output binary is already there.
- self.base.lock = ch.toOwnedLock();
- return;
- }
- log.debug("ELF LLD prev_digest={} new_digest={}", .{prev_digest, digest});
-
- // We are about to change the output file to be different, so we invalidate the build hash now.
- directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
- error.FileNotFound => {},
- else => |e| return e,
- };
-
- const target = self.base.options.target;
- const is_obj = self.base.options.output_mode == .Obj;
-
- // Create an LLD command line and invoke it.
- var argv = std.ArrayList([]const u8).init(self.base.allocator);
- defer argv.deinit();
- // Even though we're calling LLD as a library it thinks the first argument is its own exe name.
- try argv.append("lld");
- if (is_obj) {
- try argv.append("-r");
- }
- const link_in_crt = self.base.options.link_libc and self.base.options.output_mode == .Exe;
-
- try argv.append("-error-limit=0");
-
- if (self.base.options.output_mode == .Exe) {
- try argv.append("-z");
- const stack_size = self.base.options.stack_size_override orelse 16777216;
- const arg = try std.fmt.allocPrint(arena, "stack-size={}", .{stack_size});
- try argv.append(arg);
- }
-
- if (self.base.options.linker_script) |linker_script| {
- try argv.append("-T");
- try argv.append(linker_script);
- }
-
- const gc_sections = self.base.options.gc_sections orelse !is_obj;
- if (gc_sections) {
- try argv.append("--gc-sections");
- }
-
- if (self.base.options.eh_frame_hdr) {
- try argv.append("--eh-frame-hdr");
- }
-
- if (self.base.options.rdynamic) {
- try argv.append("--export-dynamic");
- }
-
- try argv.appendSlice(self.base.options.extra_lld_args);
-
- if (self.base.options.z_nodelete) {
- try argv.append("-z");
- try argv.append("nodelete");
- }
- if (self.base.options.z_defs) {
- try argv.append("-z");
- try argv.append("defs");
- }
-
- if (getLDMOption(target)) |ldm| {
- // Any target ELF will use the freebsd osabi if suffixed with "_fbsd".
- const arg = if (target.os.tag == .freebsd)
- try std.fmt.allocPrint(arena, "{}_fbsd", .{ldm})
- else
- ldm;
- try argv.append("-m");
- try argv.append(arg);
- }
-
- if (self.base.options.link_mode == .Static) {
- if (target.cpu.arch.isARM() or target.cpu.arch.isThumb()) {
- try argv.append("-Bstatic");
- } else {
- try argv.append("-static");
- }
- } else if (is_dyn_lib) {
- try argv.append("-shared");
- }
-
- if (target_util.requiresPIE(target) and self.base.options.output_mode == .Exe) {
- try argv.append("-pie");
- }
-
- const full_out_path = if (directory.path) |dir_path|
- try std.fs.path.join(arena, &[_][]const u8{dir_path, self.base.options.sub_path})
- else
- self.base.options.sub_path;
- try argv.append("-o");
- try argv.append(full_out_path);
-
- if (link_in_crt) {
- const crt1o: []const u8 = o: {
- if (target.os.tag == .netbsd) {
- break :o "crt0.o";
- } else if (target.isAndroid()) {
- if (self.base.options.link_mode == .Dynamic) {
- break :o "crtbegin_dynamic.o";
- } else {
- break :o "crtbegin_static.o";
- }
- } else if (self.base.options.link_mode == .Static) {
- break :o "crt1.o";
- } else {
- break :o "Scrt1.o";
- }
- };
- try argv.append(try comp.get_libc_crt_file(arena, crt1o));
- if (target_util.libc_needs_crti_crtn(target)) {
- try argv.append(try comp.get_libc_crt_file(arena, "crti.o"));
- }
- }
-
- // TODO rpaths
- // TODO add to cache hash above too
- //for (size_t i = 0; i < g->rpath_list.length; i += 1) {
- // Buf *rpath = g->rpath_list.at(i);
- // add_rpath(lj, rpath);
- //}
- //if (g->each_lib_rpath) {
- // for (size_t i = 0; i < g->lib_dirs.length; i += 1) {
- // const char *lib_dir = g->lib_dirs.at(i);
- // for (size_t i = 0; i < g->link_libs_list.length; i += 1) {
- // LinkLib *link_lib = g->link_libs_list.at(i);
- // if (buf_eql_str(link_lib->name, "c")) {
- // continue;
- // }
- // bool does_exist;
- // Buf *test_path = buf_sprintf("%s/lib%s.so", lib_dir, buf_ptr(link_lib->name));
- // if (os_file_exists(test_path, &does_exist) != ErrorNone) {
- // zig_panic("link: unable to check if file exists: %s", buf_ptr(test_path));
- // }
- // if (does_exist) {
- // add_rpath(lj, buf_create_from_str(lib_dir));
- // break;
- // }
- // }
- // }
- //}
-
- for (self.base.options.lib_dirs) |lib_dir| {
- try argv.append("-L");
- try argv.append(lib_dir);
- }
-
- if (self.base.options.link_libc) {
- if (self.base.options.libc_installation) |libc_installation| {
- try argv.append("-L");
- try argv.append(libc_installation.crt_dir.?);
- }
-
- if (have_dynamic_linker) {
- if (self.base.options.dynamic_linker) |dynamic_linker| {
- try argv.append("-dynamic-linker");
- try argv.append(dynamic_linker);
- }
- }
- }
-
- if (is_dyn_lib) {
- const soname = self.base.options.override_soname orelse if (self.base.options.version) |ver|
- try std.fmt.allocPrint(arena, "lib{}.so.{}", .{self.base.options.root_name, ver.major})
- else
- try std.fmt.allocPrint(arena, "lib{}.so", .{self.base.options.root_name});
- try argv.append("-soname");
- try argv.append(soname);
-
- if (self.base.options.version_script) |version_script| {
- try argv.append("-version-script");
- try argv.append(version_script);
- }
- }
-
- // Positional arguments to the linker such as object files.
- try argv.appendSlice(self.base.options.objects);
-
- for (comp.c_object_table.items()) |entry| {
- try argv.append(entry.key.status.success.object_path);
- }
-
- if (module_obj_path) |p| {
- try argv.append(p);
- }
-
- // compiler-rt and libc
- if (is_exe_or_dyn_lib) {
- if (!self.base.options.link_libc) {
- try argv.append(comp.libc_static_lib.?.full_object_path);
- }
- try argv.append(comp.compiler_rt_static_lib.?.full_object_path);
- }
-
- // Shared libraries.
- try argv.ensureCapacity(argv.items.len + self.base.options.system_libs.len);
- for (self.base.options.system_libs) |link_lib| {
- // By this time, we depend on these libs being dynamically linked libraries and not static libraries
- // (the check for that needs to be earlier), but they could be full paths to .so files, in which
- // case we want to avoid prepending "-l".
- const ext = Compilation.classifyFileExt(link_lib);
- const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{}", .{link_lib});
- argv.appendAssumeCapacity(arg);
- }
-
- if (!is_obj) {
- // libc++ dep
- if (self.base.options.link_libcpp) {
- try argv.append(comp.libcxxabi_static_lib.?);
- try argv.append(comp.libcxx_static_lib.?);
- }
-
- // libc dep
- if (self.base.options.link_libc) {
- if (self.base.options.libc_installation != null) {
- if (self.base.options.link_mode == .Static) {
- try argv.append("--start-group");
- try argv.append("-lc");
- try argv.append("-lm");
- try argv.append("--end-group");
- } else {
- try argv.append("-lc");
- try argv.append("-lm");
- }
-
- if (target.os.tag == .freebsd or target.os.tag == .netbsd) {
- try argv.append("-lpthread");
- }
- } else if (target.isGnuLibC()) {
- try argv.append(comp.libunwind_static_lib.?.full_object_path);
- for (glibc.libs) |lib| {
- const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{
- comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
- });
- try argv.append(lib_path);
- }
- try argv.append(try comp.get_libc_crt_file(arena, "libc_nonshared.a"));
- } else if (target.isMusl()) {
- try argv.append(comp.libunwind_static_lib.?.full_object_path);
- try argv.append(comp.libc_static_lib.?.full_object_path);
- } else if (self.base.options.link_libcpp) {
- try argv.append(comp.libunwind_static_lib.?.full_object_path);
- } else {
- unreachable; // Compiler was supposed to emit an error for not being able to provide libc.
- }
- }
- }
-
- // crt end
- if (link_in_crt) {
- if (target.isAndroid()) {
- try argv.append(try comp.get_libc_crt_file(arena, "crtend_android.o"));
- } else if (target_util.libc_needs_crti_crtn(target)) {
- try argv.append(try comp.get_libc_crt_file(arena, "crtn.o"));
- }
- }
-
- const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os;
- if (allow_shlib_undefined) {
- try argv.append("--allow-shlib-undefined");
- }
-
- if (self.base.options.bind_global_refs_locally) {
- try argv.append("-Bsymbolic");
- }
-
- if (self.base.options.verbose_link) {
- for (argv.items[0 .. argv.items.len - 1]) |arg| {
- std.debug.print("{} ", .{arg});
- }
- std.debug.print("{}\n", .{argv.items[argv.items.len - 1]});
- }
-
- // Oh, snapplesauce! We need null terminated argv.
- // TODO allocSentinel crashed stage1 so this is working around it.
- const new_argv_with_sentinel = try arena.alloc(?[*:0]const u8, argv.items.len + 1);
- new_argv_with_sentinel[argv.items.len] = null;
- const new_argv = new_argv_with_sentinel[0..argv.items.len: null];
- for (argv.items) |arg, i| {
- new_argv[i] = try arena.dupeZ(u8, arg);
- }
-
- const llvm = @import("../llvm.zig");
- const ok = llvm.Link(.ELF, new_argv.ptr, new_argv.len, append_diagnostic, 0, 0);
- if (!ok) return error.LLDReportedFailure;
-
- // Update the dangling symlink with the digest. If it fails we can continue; it only
- // means that the next invocation will have an unnecessary cache miss.
- directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| {
- std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)});
- };
- // Again failure here only means an unnecessary cache miss.
- ch.writeManifest() catch |err| {
- std.log.warn("failed to write cache manifest when linking: {}", .{ @errorName(err) });
- };
- // We hang on to this lock so that the output file path can be used without
- // other processes clobbering it.
- self.base.lock = ch.toOwnedLock();
-}
-
-fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void {
- // TODO collect diagnostics and handle cleanly
- const msg = ptr[0..len];
- std.log.err("LLD: {}", .{msg});
-}
-
-fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void {
- const target_endian = self.base.options.target.cpu.arch.endian();
- switch (self.ptr_width) {
- .p32 => mem.writeInt(u32, buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, addr), target_endian),
- .p64 => mem.writeInt(u64, buf.addManyAsArrayAssumeCapacity(8), addr, target_endian),
- }
-}
-
-fn writeElfHeader(self: *Elf) !void {
- var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
-
- var index: usize = 0;
- hdr_buf[0..4].* = "\x7fELF".*;
- index += 4;
-
- hdr_buf[index] = switch (self.ptr_width) {
- .p32 => elf.ELFCLASS32,
- .p64 => elf.ELFCLASS64,
- };
- index += 1;
-
- const endian = self.base.options.target.cpu.arch.endian();
- hdr_buf[index] = switch (endian) {
- .Little => elf.ELFDATA2LSB,
- .Big => elf.ELFDATA2MSB,
- };
- index += 1;
-
- hdr_buf[index] = 1; // ELF version
- index += 1;
-
- // OS ABI, often set to 0 regardless of target platform
- // ABI Version, possibly used by glibc but not by static executables
- // padding
- mem.set(u8, hdr_buf[index..][0..9], 0);
- index += 9;
-
- assert(index == 16);
-
- const elf_type = switch (self.base.options.effectiveOutputMode()) {
- .Exe => elf.ET.EXEC,
- .Obj => elf.ET.REL,
- .Lib => switch (self.base.options.link_mode) {
- .Static => elf.ET.REL,
- .Dynamic => elf.ET.DYN,
- },
- };
- mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf_type), endian);
- index += 2;
-
- const machine = self.base.options.target.cpu.arch.toElfMachine();
- mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(machine), endian);
- index += 2;
-
- // ELF Version, again
- mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian);
- index += 4;
-
- const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?;
-
- switch (self.ptr_width) {
- .p32 => {
- mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, e_entry), endian);
- index += 4;
-
- // e_phoff
- mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.phdr_table_offset.?), endian);
- index += 4;
-
- // e_shoff
- mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.shdr_table_offset.?), endian);
- index += 4;
- },
- .p64 => {
- // e_entry
- mem.writeInt(u64, hdr_buf[index..][0..8], e_entry, endian);
- index += 8;
-
- // e_phoff
- mem.writeInt(u64, hdr_buf[index..][0..8], self.phdr_table_offset.?, endian);
- index += 8;
-
- // e_shoff
- mem.writeInt(u64, hdr_buf[index..][0..8], self.shdr_table_offset.?, endian);
- index += 8;
- },
- }
-
- const e_flags = 0;
- mem.writeInt(u32, hdr_buf[index..][0..4], e_flags, endian);
- index += 4;
-
- const e_ehsize: u16 = switch (self.ptr_width) {
- .p32 => @sizeOf(elf.Elf32_Ehdr),
- .p64 => @sizeOf(elf.Elf64_Ehdr),
- };
- mem.writeInt(u16, hdr_buf[index..][0..2], e_ehsize, endian);
- index += 2;
-
- const e_phentsize: u16 = switch (self.ptr_width) {
- .p32 => @sizeOf(elf.Elf32_Phdr),
- .p64 => @sizeOf(elf.Elf64_Phdr),
- };
- mem.writeInt(u16, hdr_buf[index..][0..2], e_phentsize, endian);
- index += 2;
-
- const e_phnum = @intCast(u16, self.program_headers.items.len);
- mem.writeInt(u16, hdr_buf[index..][0..2], e_phnum, endian);
- index += 2;
-
- const e_shentsize: u16 = switch (self.ptr_width) {
- .p32 => @sizeOf(elf.Elf32_Shdr),
- .p64 => @sizeOf(elf.Elf64_Shdr),
- };
- mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian);
- index += 2;
-
- const e_shnum = @intCast(u16, self.sections.items.len);
- mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian);
- index += 2;
-
- mem.writeInt(u16, hdr_buf[index..][0..2], self.shstrtab_index.?, endian);
- index += 2;
-
- assert(index == e_ehsize);
-
- try self.base.file.?.pwriteAll(hdr_buf[0..index], 0);
-}
-
-fn freeTextBlock(self: *Elf, text_block: *TextBlock) void {
- var already_have_free_list_node = false;
- {
- var i: usize = 0;
- // TODO turn text_block_free_list into a hash map
- while (i < self.text_block_free_list.items.len) {
- if (self.text_block_free_list.items[i] == text_block) {
- _ = self.text_block_free_list.swapRemove(i);
- continue;
- }
- if (self.text_block_free_list.items[i] == text_block.prev) {
- already_have_free_list_node = true;
- }
- i += 1;
- }
- }
- // TODO process free list for dbg info just like we do above for vaddrs
-
- if (self.last_text_block == text_block) {
- // TODO shrink the .text section size here
- self.last_text_block = text_block.prev;
- }
- if (self.dbg_info_decl_first == text_block) {
- self.dbg_info_decl_first = text_block.dbg_info_next;
- }
- if (self.dbg_info_decl_last == text_block) {
- // TODO shrink the .debug_info section size here
- self.dbg_info_decl_last = text_block.dbg_info_prev;
- }
-
- if (text_block.prev) |prev| {
- prev.next = text_block.next;
-
- if (!already_have_free_list_node and prev.freeListEligible(self.*)) {
- // The free list is heuristics, it doesn't have to be perfect, so we can
- // ignore the OOM here.
- self.text_block_free_list.append(self.base.allocator, prev) catch {};
- }
- } else {
- text_block.prev = null;
- }
-
- if (text_block.next) |next| {
- next.prev = text_block.prev;
- } else {
- text_block.next = null;
- }
-
- if (text_block.dbg_info_prev) |prev| {
- prev.dbg_info_next = text_block.dbg_info_next;
-
- // TODO the free list logic like we do for text blocks above
- } else {
- text_block.dbg_info_prev = null;
- }
-
- if (text_block.dbg_info_next) |next| {
- next.dbg_info_prev = text_block.dbg_info_prev;
- } else {
- text_block.dbg_info_next = null;
- }
-}
-
-fn shrinkTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64) void {
- // TODO check the new capacity, and if it crosses the size threshold into a big enough
- // capacity, insert a free list node for it.
-}
-
-fn growTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
- const sym = self.local_symbols.items[text_block.local_sym_index];
- const align_ok = mem.alignBackwardGeneric(u64, sym.st_value, alignment) == sym.st_value;
- const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*);
- if (!need_realloc) return sym.st_value;
- return self.allocateTextBlock(text_block, new_block_size, alignment);
-}
-
-fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
- const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
- const shdr = &self.sections.items[self.text_section_index.?];
- const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den;
-
- // We use these to indicate our intention to update metadata, placing the new block,
- // and possibly removing a free list node.
- // It would be simpler to do it inside the for loop below, but that would cause a
- // problem if an error was returned later in the function. So this action
- // is actually carried out at the end of the function, when errors are no longer possible.
- var block_placement: ?*TextBlock = null;
- var free_list_removal: ?usize = null;
-
- // First we look for an appropriately sized free list node.
- // The list is unordered. We'll just take the first thing that works.
- const vaddr = blk: {
- var i: usize = 0;
- while (i < self.text_block_free_list.items.len) {
- const big_block = self.text_block_free_list.items[i];
- // We now have a pointer to a live text block that has too much capacity.
- // Is it enough that we could fit this new text block?
- const sym = self.local_symbols.items[big_block.local_sym_index];
- const capacity = big_block.capacity(self.*);
- const ideal_capacity = capacity * alloc_num / alloc_den;
- const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
- const capacity_end_vaddr = sym.st_value + capacity;
- const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity;
- const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment);
- if (new_start_vaddr < ideal_capacity_end_vaddr) {
- // Additional bookkeeping here to notice if this free list node
- // should be deleted because the block that it points to has grown to take up
- // more of the extra capacity.
- if (!big_block.freeListEligible(self.*)) {
- _ = self.text_block_free_list.swapRemove(i);
- } else {
- i += 1;
- }
- continue;
- }
- // At this point we know that we will place the new block here. But the
- // remaining question is whether there is still yet enough capacity left
- // over for there to still be a free list node.
- const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr;
- const keep_free_list_node = remaining_capacity >= min_text_capacity;
-
- // Set up the metadata to be updated, after errors are no longer possible.
- block_placement = big_block;
- if (!keep_free_list_node) {
- free_list_removal = i;
- }
- break :blk new_start_vaddr;
- } else if (self.last_text_block) |last| {
- const sym = self.local_symbols.items[last.local_sym_index];
- const ideal_capacity = sym.st_size * alloc_num / alloc_den;
- const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
- const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
- // Set up the metadata to be updated, after errors are no longer possible.
- block_placement = last;
- break :blk new_start_vaddr;
- } else {
- break :blk phdr.p_vaddr;
- }
- };
-
- const expand_text_section = block_placement == null or block_placement.?.next == null;
- if (expand_text_section) {
- const text_capacity = self.allocatedSize(shdr.sh_offset);
- const needed_size = (vaddr + new_block_size) - phdr.p_vaddr;
- if (needed_size > text_capacity) {
- // Must move the entire text section.
- const new_offset = self.findFreeSpace(needed_size, 0x1000);
- const text_size = if (self.last_text_block) |last| blk: {
- const sym = self.local_symbols.items[last.local_sym_index];
- break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr;
- } else 0;
- const amt = try self.base.file.?.copyRangeAll(shdr.sh_offset, self.base.file.?, new_offset, text_size);
- if (amt != text_size) return error.InputOutput;
- shdr.sh_offset = new_offset;
- phdr.p_offset = new_offset;
- }
- self.last_text_block = text_block;
-
- shdr.sh_size = needed_size;
- phdr.p_memsz = needed_size;
- phdr.p_filesz = needed_size;
-
- // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address
- // range of the compilation unit. When we expand the text section, this range changes,
- // so the DW_TAG_compile_unit tag of the .debug_info section becomes dirty.
- self.debug_info_header_dirty = true;
- // This becomes dirty for the same reason. We could potentially make this more
- // fine-grained with the addition of support for more compilation units. It is planned to
- // model each package as a different compilation unit.
- self.debug_aranges_section_dirty = true;
-
- self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
- self.shdr_table_dirty = true; // TODO look into making only the one section dirty
- }
-
- // This function can also reallocate a text block.
- // In this case we need to "unplug" it from its previous location before
- // plugging it in to its new location.
- if (text_block.prev) |prev| {
- prev.next = text_block.next;
- }
- if (text_block.next) |next| {
- next.prev = text_block.prev;
- }
-
- if (block_placement) |big_block| {
- text_block.prev = big_block;
- text_block.next = big_block.next;
- big_block.next = text_block;
- } else {
- text_block.prev = null;
- text_block.next = null;
- }
- if (free_list_removal) |i| {
- _ = self.text_block_free_list.swapRemove(i);
- }
- return vaddr;
-}
-
-pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {
- if (decl.link.elf.local_sym_index != 0) return;
-
- try self.local_symbols.ensureCapacity(self.base.allocator, self.local_symbols.items.len + 1);
- try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);
-
- if (self.local_symbol_free_list.popOrNull()) |i| {
- log.debug("reusing symbol index {} for {}\n", .{ i, decl.name });
- decl.link.elf.local_sym_index = i;
- } else {
- log.debug("allocating symbol index {} for {}\n", .{ self.local_symbols.items.len, decl.name });
- decl.link.elf.local_sym_index = @intCast(u32, self.local_symbols.items.len);
- _ = self.local_symbols.addOneAssumeCapacity();
- }
-
- if (self.offset_table_free_list.popOrNull()) |i| {
- decl.link.elf.offset_table_index = i;
- } else {
- decl.link.elf.offset_table_index = @intCast(u32, self.offset_table.items.len);
- _ = self.offset_table.addOneAssumeCapacity();
- self.offset_table_count_dirty = true;
- }
-
- const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
-
- self.local_symbols.items[decl.link.elf.local_sym_index] = .{
- .st_name = 0,
- .st_info = 0,
- .st_other = 0,
- .st_shndx = 0,
- .st_value = phdr.p_vaddr,
- .st_size = 0,
- };
- self.offset_table.items[decl.link.elf.offset_table_index] = 0;
-}
-
-pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {
- // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
- self.freeTextBlock(&decl.link.elf);
- if (decl.link.elf.local_sym_index != 0) {
- self.local_symbol_free_list.append(self.base.allocator, decl.link.elf.local_sym_index) catch {};
- self.offset_table_free_list.append(self.base.allocator, decl.link.elf.offset_table_index) catch {};
-
- self.local_symbols.items[decl.link.elf.local_sym_index].st_info = 0;
-
- decl.link.elf.local_sym_index = 0;
- }
- // TODO make this logic match freeTextBlock. Maybe abstract the logic out since the same thing
- // is desired for both.
- _ = self.dbg_line_fn_free_list.remove(&decl.fn_link.elf);
- if (decl.fn_link.elf.prev) |prev| {
- _ = self.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {};
- prev.next = decl.fn_link.elf.next;
- if (decl.fn_link.elf.next) |next| {
- next.prev = prev;
- } else {
- self.dbg_line_fn_last = prev;
- }
- } else if (decl.fn_link.elf.next) |next| {
- self.dbg_line_fn_first = next;
- next.prev = null;
- }
- if (self.dbg_line_fn_first == &decl.fn_link.elf) {
- self.dbg_line_fn_first = decl.fn_link.elf.next;
- }
- if (self.dbg_line_fn_last == &decl.fn_link.elf) {
- self.dbg_line_fn_last = decl.fn_link.elf.prev;
- }
-}
-
-pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- var code_buffer = std.ArrayList(u8).init(self.base.allocator);
- defer code_buffer.deinit();
-
- var dbg_line_buffer = std.ArrayList(u8).init(self.base.allocator);
- defer dbg_line_buffer.deinit();
-
- var dbg_info_buffer = std.ArrayList(u8).init(self.base.allocator);
- defer dbg_info_buffer.deinit();
-
- var dbg_info_type_relocs: File.DbgInfoTypeRelocsTable = .{};
- defer {
- var it = dbg_info_type_relocs.iterator();
- while (it.next()) |entry| {
- entry.value.relocs.deinit(self.base.allocator);
- }
- dbg_info_type_relocs.deinit(self.base.allocator);
- }
-
- const typed_value = decl.typed_value.most_recent.typed_value;
- const is_fn: bool = switch (typed_value.ty.zigTypeTag()) {
- .Fn => true,
- else => false,
- };
- if (is_fn) {
- const zir_dumps = if (std.builtin.is_test) &[0][]const u8{} else build_options.zir_dumps;
- if (zir_dumps.len != 0) {
- for (zir_dumps) |fn_name| {
- if (mem.eql(u8, mem.spanZ(decl.name), fn_name)) {
- std.debug.print("\n{}\n", .{decl.name});
- typed_value.val.cast(Value.Payload.Function).?.func.dump(module.*);
- }
- }
- }
-
- // For functions we need to add a prologue to the debug line program.
- try dbg_line_buffer.ensureCapacity(26);
-
- const line_off: u28 = blk: {
- if (decl.scope.cast(Module.Scope.Container)) |container_scope| {
- const tree = container_scope.file_scope.contents.tree;
- const file_ast_decls = tree.root_node.decls();
- // TODO Look into improving the performance here by adding a token-index-to-line
- // lookup table. Currently this involves scanning over the source code for newlines.
- const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
- const block = fn_proto.getBodyNode().?.castTag(.Block).?;
- const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);
- break :blk @intCast(u28, line_delta);
- } else if (decl.scope.cast(Module.Scope.ZIRModule)) |zir_module| {
- const byte_off = zir_module.contents.module.decls[decl.src_index].inst.src;
- const line_delta = std.zig.lineDelta(zir_module.source.bytes, 0, byte_off);
- break :blk @intCast(u28, line_delta);
- } else {
- unreachable;
- }
- };
-
- const ptr_width_bytes = self.ptrWidthBytes();
- dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{
- DW.LNS_extended_op,
- ptr_width_bytes + 1,
- DW.LNE_set_address,
- });
- // This is the "relocatable" vaddr, corresponding to `code_buffer` index `0`.
- assert(dbg_line_vaddr_reloc_index == dbg_line_buffer.items.len);
- dbg_line_buffer.items.len += ptr_width_bytes;
-
- dbg_line_buffer.appendAssumeCapacity(DW.LNS_advance_line);
- // This is the "relocatable" relative line offset from the previous function's end curly
- // to this function's begin curly.
- assert(self.getRelocDbgLineOff() == dbg_line_buffer.items.len);
- // Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later.
- leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line_off);
-
- dbg_line_buffer.appendAssumeCapacity(DW.LNS_set_file);
- assert(self.getRelocDbgFileIndex() == dbg_line_buffer.items.len);
- // Once we support more than one source file, this will have the ability to be more
- // than one possible value.
- const file_index = 1;
- leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), file_index);
-
- // Emit a line for the begin curly with prologue_end=false. The codegen will
- // do the work of setting prologue_end=true and epilogue_begin=true.
- dbg_line_buffer.appendAssumeCapacity(DW.LNS_copy);
-
- // .debug_info subprogram
- const decl_name_with_null = decl.name[0 .. mem.lenZ(decl.name) + 1];
- try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 25 + decl_name_with_null.len);
-
- const fn_ret_type = typed_value.ty.fnReturnType();
- const fn_ret_has_bits = fn_ret_type.hasCodeGenBits();
- if (fn_ret_has_bits) {
- dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram);
- } else {
- dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram_retvoid);
- }
- // These get overwritten after generating the machine code. These values are
- // "relocations" and have to be in this fixed place so that functions can be
- // moved in virtual address space.
- assert(dbg_info_low_pc_reloc_index == dbg_info_buffer.items.len);
- dbg_info_buffer.items.len += ptr_width_bytes; // DW.AT_low_pc, DW.FORM_addr
- assert(self.getRelocDbgInfoSubprogramHighPC() == dbg_info_buffer.items.len);
- dbg_info_buffer.items.len += 4; // DW.AT_high_pc, DW.FORM_data4
- if (fn_ret_has_bits) {
- const gop = try dbg_info_type_relocs.getOrPut(self.base.allocator, fn_ret_type);
- if (!gop.found_existing) {
- gop.entry.value = .{
- .off = undefined,
- .relocs = .{},
- };
- }
- try gop.entry.value.relocs.append(self.base.allocator, @intCast(u32, dbg_info_buffer.items.len));
- dbg_info_buffer.items.len += 4; // DW.AT_type, DW.FORM_ref4
- }
- dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT_name, DW.FORM_string
- } else {
- // TODO implement .debug_info for global variables
- }
- const res = try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, .{
- .dwarf = .{
- .dbg_line = &dbg_line_buffer,
- .dbg_info = &dbg_info_buffer,
- .dbg_info_type_relocs = &dbg_info_type_relocs,
- },
- });
- const code = switch (res) {
- .externally_managed => |x| x,
- .appended => code_buffer.items,
- .fail => |em| {
- decl.analysis = .codegen_failure;
- try module.failed_decls.put(module.gpa, decl, em);
- return;
- },
- };
-
- const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
-
- const stt_bits: u8 = if (is_fn) elf.STT_FUNC else elf.STT_OBJECT;
-
- assert(decl.link.elf.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()
- const local_sym = &self.local_symbols.items[decl.link.elf.local_sym_index];
- if (local_sym.st_size != 0) {
- const capacity = decl.link.elf.capacity(self.*);
- const need_realloc = code.len > capacity or
- !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
- if (need_realloc) {
- const vaddr = try self.growTextBlock(&decl.link.elf, code.len, required_alignment);
- log.debug("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
- if (vaddr != local_sym.st_value) {
- local_sym.st_value = vaddr;
-
- log.debug(" (writing new offset table entry)\n", .{});
- self.offset_table.items[decl.link.elf.offset_table_index] = vaddr;
- try self.writeOffsetTableEntry(decl.link.elf.offset_table_index);
- }
- } else if (code.len < local_sym.st_size) {
- self.shrinkTextBlock(&decl.link.elf, code.len);
- }
- local_sym.st_size = code.len;
- local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(decl.name));
- local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;
- local_sym.st_other = 0;
- local_sym.st_shndx = self.text_section_index.?;
- // TODO this write could be avoided if no fields of the symbol were changed.
- try self.writeSymbol(decl.link.elf.local_sym_index);
- } else {
- const decl_name = mem.spanZ(decl.name);
- const name_str_index = try self.makeString(decl_name);
- const vaddr = try self.allocateTextBlock(&decl.link.elf, code.len, required_alignment);
- log.debug("allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
- errdefer self.freeTextBlock(&decl.link.elf);
-
- local_sym.* = .{
- .st_name = name_str_index,
- .st_info = (elf.STB_LOCAL << 4) | stt_bits,
- .st_other = 0,
- .st_shndx = self.text_section_index.?,
- .st_value = vaddr,
- .st_size = code.len,
- };
- self.offset_table.items[decl.link.elf.offset_table_index] = vaddr;
-
- try self.writeSymbol(decl.link.elf.local_sym_index);
- try self.writeOffsetTableEntry(decl.link.elf.offset_table_index);
- }
-
- const section_offset = local_sym.st_value - self.program_headers.items[self.phdr_load_re_index.?].p_vaddr;
- const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset;
- try self.base.file.?.pwriteAll(code, file_offset);
-
- const target_endian = self.base.options.target.cpu.arch.endian();
-
- const text_block = &decl.link.elf;
-
- // If the Decl is a function, we need to update the .debug_line program.
- if (is_fn) {
- // Perform the relocations based on vaddr.
- switch (self.ptr_width) {
- .p32 => {
- {
- const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..4];
- mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_value), target_endian);
- }
- {
- const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..4];
- mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_value), target_endian);
- }
- },
- .p64 => {
- {
- const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..8];
- mem.writeInt(u64, ptr, local_sym.st_value, target_endian);
- }
- {
- const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..8];
- mem.writeInt(u64, ptr, local_sym.st_value, target_endian);
- }
- },
- }
- {
- const ptr = dbg_info_buffer.items[self.getRelocDbgInfoSubprogramHighPC()..][0..4];
- mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_size), target_endian);
- }
-
- try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS_extended_op, 1, DW.LNE_end_sequence });
-
- // Now we have the full contents and may allocate a region to store it.
-
- // This logic is nearly identical to the logic below in `updateDeclDebugInfo` for
- // `TextBlock` and the .debug_info. If you are editing this logic, you
- // probably need to edit that logic too.
-
- const debug_line_sect = &self.sections.items[self.debug_line_section_index.?];
- const src_fn = &decl.fn_link.elf;
- src_fn.len = @intCast(u32, dbg_line_buffer.items.len);
- if (self.dbg_line_fn_last) |last| {
- if (src_fn.next) |next| {
- // Update existing function - non-last item.
- if (src_fn.off + src_fn.len + min_nop_size > next.off) {
- // It grew too big, so we move it to a new location.
- if (src_fn.prev) |prev| {
- _ = self.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {};
- prev.next = src_fn.next;
- }
- next.prev = src_fn.prev;
- src_fn.next = null;
- // Populate where it used to be with NOPs.
- const file_pos = debug_line_sect.sh_offset + src_fn.off;
- try self.pwriteDbgLineNops(0, &[0]u8{}, src_fn.len, file_pos);
- // TODO Look at the free list before appending at the end.
- src_fn.prev = last;
- last.next = src_fn;
- self.dbg_line_fn_last = src_fn;
-
- src_fn.off = last.off + (last.len * alloc_num / alloc_den);
- }
- } else if (src_fn.prev == null) {
- // Append new function.
- // TODO Look at the free list before appending at the end.
- src_fn.prev = last;
- last.next = src_fn;
- self.dbg_line_fn_last = src_fn;
-
- src_fn.off = last.off + (last.len * alloc_num / alloc_den);
- }
- } else {
- // This is the first function of the Line Number Program.
- self.dbg_line_fn_first = src_fn;
- self.dbg_line_fn_last = src_fn;
-
- src_fn.off = self.dbgLineNeededHeaderBytes() * alloc_num / alloc_den;
- }
-
- const last_src_fn = self.dbg_line_fn_last.?;
- const needed_size = last_src_fn.off + last_src_fn.len;
- if (needed_size != debug_line_sect.sh_size) {
- if (needed_size > self.allocatedSize(debug_line_sect.sh_offset)) {
- const new_offset = self.findFreeSpace(needed_size, 1);
- const existing_size = last_src_fn.off;
- log.debug("moving .debug_line section: {} bytes from 0x{x} to 0x{x}\n", .{
- existing_size,
- debug_line_sect.sh_offset,
- new_offset,
- });
- const amt = try self.base.file.?.copyRangeAll(debug_line_sect.sh_offset, self.base.file.?, new_offset, existing_size);
- if (amt != existing_size) return error.InputOutput;
- debug_line_sect.sh_offset = new_offset;
- }
- debug_line_sect.sh_size = needed_size;
- self.shdr_table_dirty = true; // TODO look into making only the one section dirty
- self.debug_line_header_dirty = true;
- }
- const prev_padding_size: u32 = if (src_fn.prev) |prev| src_fn.off - (prev.off + prev.len) else 0;
- const next_padding_size: u32 = if (src_fn.next) |next| next.off - (src_fn.off + src_fn.len) else 0;
-
- // We only have support for one compilation unit so far, so the offsets are directly
- // from the .debug_line section.
- const file_pos = debug_line_sect.sh_offset + src_fn.off;
- try self.pwriteDbgLineNops(prev_padding_size, dbg_line_buffer.items, next_padding_size, file_pos);
-
- // .debug_info - End the TAG_subprogram children.
- try dbg_info_buffer.append(0);
- }
-
- // Now we emit the .debug_info types of the Decl. These will count towards the size of
- // the buffer, so we have to do it before computing the offset, and we can't perform the actual
- // relocations yet.
- var it = dbg_info_type_relocs.iterator();
- while (it.next()) |entry| {
- entry.value.off = @intCast(u32, dbg_info_buffer.items.len);
- try self.addDbgInfoType(entry.key, &dbg_info_buffer);
- }
-
- try self.updateDeclDebugInfoAllocation(text_block, @intCast(u32, dbg_info_buffer.items.len));
-
- // Now that we have the offset assigned we can finally perform type relocations.
- it = dbg_info_type_relocs.iterator();
- while (it.next()) |entry| {
- for (entry.value.relocs.items) |off| {
- mem.writeInt(
- u32,
- dbg_info_buffer.items[off..][0..4],
- text_block.dbg_info_off + entry.value.off,
- target_endian,
- );
- }
- }
-
- try self.writeDeclDebugInfo(text_block, dbg_info_buffer.items);
-
- // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
- const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
- return self.updateDeclExports(module, decl, decl_exports);
-}
-
-/// Asserts the type has codegen bits.
-fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !void {
- switch (ty.zigTypeTag()) {
- .Void => unreachable,
- .NoReturn => unreachable,
- .Bool => {
- try dbg_info_buffer.appendSlice(&[_]u8{
- abbrev_base_type,
- DW.ATE_boolean, // DW.AT_encoding , DW.FORM_data1
- 1, // DW.AT_byte_size, DW.FORM_data1
- 'b',
- 'o',
- 'o',
- 'l',
- 0, // DW.AT_name, DW.FORM_string
- });
- },
- .Int => {
- const info = ty.intInfo(self.base.options.target);
- try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 12);
- dbg_info_buffer.appendAssumeCapacity(abbrev_base_type);
- // DW.AT_encoding, DW.FORM_data1
- dbg_info_buffer.appendAssumeCapacity(if (info.signed) DW.ATE_signed else DW.ATE_unsigned);
- // DW.AT_byte_size, DW.FORM_data1
- dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(self.base.options.target)));
- // DW.AT_name, DW.FORM_string
- try dbg_info_buffer.writer().print("{}\x00", .{ty});
- },
- else => {
- std.log.scoped(.compiler).err("TODO implement .debug_info for type '{}'", .{ty});
- try dbg_info_buffer.append(abbrev_pad1);
- },
- }
-}
-
-fn updateDeclDebugInfoAllocation(self: *Elf, text_block: *TextBlock, len: u32) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- // This logic is nearly identical to the logic above in `updateDecl` for
- // `SrcFn` and the line number programs. If you are editing this logic, you
- // probably need to edit that logic too.
-
- const debug_info_sect = &self.sections.items[self.debug_info_section_index.?];
- text_block.dbg_info_len = len;
- if (self.dbg_info_decl_last) |last| {
- if (text_block.dbg_info_next) |next| {
- // Update existing Decl - non-last item.
- if (text_block.dbg_info_off + text_block.dbg_info_len + min_nop_size > next.dbg_info_off) {
- // It grew too big, so we move it to a new location.
- if (text_block.dbg_info_prev) |prev| {
- _ = self.dbg_info_decl_free_list.put(self.base.allocator, prev, {}) catch {};
- prev.dbg_info_next = text_block.dbg_info_next;
- }
- next.dbg_info_prev = text_block.dbg_info_prev;
- text_block.dbg_info_next = null;
- // Populate where it used to be with NOPs.
- const file_pos = debug_info_sect.sh_offset + text_block.dbg_info_off;
- try self.pwriteDbgInfoNops(0, &[0]u8{}, text_block.dbg_info_len, false, file_pos);
- // TODO Look at the free list before appending at the end.
- text_block.dbg_info_prev = last;
- last.dbg_info_next = text_block;
- self.dbg_info_decl_last = text_block;
-
- text_block.dbg_info_off = last.dbg_info_off + (last.dbg_info_len * alloc_num / alloc_den);
- }
- } else if (text_block.dbg_info_prev == null) {
- // Append new Decl.
- // TODO Look at the free list before appending at the end.
- text_block.dbg_info_prev = last;
- last.dbg_info_next = text_block;
- self.dbg_info_decl_last = text_block;
-
- text_block.dbg_info_off = last.dbg_info_off + (last.dbg_info_len * alloc_num / alloc_den);
- }
- } else {
- // This is the first Decl of the .debug_info
- self.dbg_info_decl_first = text_block;
- self.dbg_info_decl_last = text_block;
-
- text_block.dbg_info_off = self.dbgInfoNeededHeaderBytes() * alloc_num / alloc_den;
- }
-}
-
-fn writeDeclDebugInfo(self: *Elf, text_block: *TextBlock, dbg_info_buf: []const u8) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- // This logic is nearly identical to the logic above in `updateDecl` for
- // `SrcFn` and the line number programs. If you are editing this logic, you
- // probably need to edit that logic too.
-
- const debug_info_sect = &self.sections.items[self.debug_info_section_index.?];
-
- const last_decl = self.dbg_info_decl_last.?;
- // +1 for a trailing zero to end the children of the decl tag.
- const needed_size = last_decl.dbg_info_off + last_decl.dbg_info_len + 1;
- if (needed_size != debug_info_sect.sh_size) {
- if (needed_size > self.allocatedSize(debug_info_sect.sh_offset)) {
- const new_offset = self.findFreeSpace(needed_size, 1);
- const existing_size = last_decl.dbg_info_off;
- log.debug("moving .debug_info section: {} bytes from 0x{x} to 0x{x}\n", .{
- existing_size,
- debug_info_sect.sh_offset,
- new_offset,
- });
- const amt = try self.base.file.?.copyRangeAll(debug_info_sect.sh_offset, self.base.file.?, new_offset, existing_size);
- if (amt != existing_size) return error.InputOutput;
- debug_info_sect.sh_offset = new_offset;
- }
- debug_info_sect.sh_size = needed_size;
- self.shdr_table_dirty = true; // TODO look into making only the one section dirty
- self.debug_info_header_dirty = true;
- }
- const prev_padding_size: u32 = if (text_block.dbg_info_prev) |prev|
- text_block.dbg_info_off - (prev.dbg_info_off + prev.dbg_info_len)
- else
- 0;
- const next_padding_size: u32 = if (text_block.dbg_info_next) |next|
- next.dbg_info_off - (text_block.dbg_info_off + text_block.dbg_info_len)
- else
- 0;
-
- // To end the children of the decl tag.
- const trailing_zero = text_block.dbg_info_next == null;
-
- // We only have support for one compilation unit so far, so the offsets are directly
- // from the .debug_info section.
- const file_pos = debug_info_sect.sh_offset + text_block.dbg_info_off;
- try self.pwriteDbgInfoNops(prev_padding_size, dbg_info_buf, next_padding_size, trailing_zero, file_pos);
-}
-
-pub fn updateDeclExports(
- self: *Elf,
- module: *Module,
- decl: *const Module.Decl,
- exports: []const *Module.Export,
-) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- try self.global_symbols.ensureCapacity(self.base.allocator, self.global_symbols.items.len + exports.len);
- const typed_value = decl.typed_value.most_recent.typed_value;
- if (decl.link.elf.local_sym_index == 0) return;
- const decl_sym = self.local_symbols.items[decl.link.elf.local_sym_index];
-
- for (exports) |exp| {
- if (exp.options.section) |section_name| {
- if (!mem.eql(u8, section_name, ".text")) {
- try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
- module.failed_exports.putAssumeCapacityNoClobber(
- exp,
- try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
- );
- continue;
- }
- }
- const stb_bits: u8 = switch (exp.options.linkage) {
- .Internal => elf.STB_LOCAL,
- .Strong => blk: {
- if (mem.eql(u8, exp.options.name, "_start")) {
- self.entry_addr = decl_sym.st_value;
- }
- break :blk elf.STB_GLOBAL;
- },
- .Weak => elf.STB_WEAK,
- .LinkOnce => {
- try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
- module.failed_exports.putAssumeCapacityNoClobber(
- exp,
- try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}),
- );
- continue;
- },
- };
- const stt_bits: u8 = @truncate(u4, decl_sym.st_info);
- if (exp.link.sym_index) |i| {
- const sym = &self.global_symbols.items[i];
- sym.* = .{
- .st_name = try self.updateString(sym.st_name, exp.options.name),
- .st_info = (stb_bits << 4) | stt_bits,
- .st_other = 0,
- .st_shndx = self.text_section_index.?,
- .st_value = decl_sym.st_value,
- .st_size = decl_sym.st_size,
- };
- } else {
- const name = try self.makeString(exp.options.name);
- const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: {
- _ = self.global_symbols.addOneAssumeCapacity();
- break :blk self.global_symbols.items.len - 1;
- };
- self.global_symbols.items[i] = .{
- .st_name = name,
- .st_info = (stb_bits << 4) | stt_bits,
- .st_other = 0,
- .st_shndx = self.text_section_index.?,
- .st_value = decl_sym.st_value,
- .st_size = decl_sym.st_size,
- };
-
- exp.link.sym_index = @intCast(u32, i);
- }
- }
-}
-
-/// Must be called only after a successful call to `updateDecl`.
-pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Decl) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- const container_scope = decl.scope.cast(Module.Scope.Container).?;
- const tree = container_scope.file_scope.contents.tree;
- const file_ast_decls = tree.root_node.decls();
- // TODO Look into improving the performance here by adding a token-index-to-line
- // lookup table. Currently this involves scanning over the source code for newlines.
- const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
- const block = fn_proto.getBodyNode().?.castTag(.Block).?;
- const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);
- const casted_line_off = @intCast(u28, line_delta);
-
- const shdr = &self.sections.items[self.debug_line_section_index.?];
- const file_pos = shdr.sh_offset + decl.fn_link.elf.off + self.getRelocDbgLineOff();
- var data: [4]u8 = undefined;
- leb128.writeUnsignedFixed(4, &data, casted_line_off);
- try self.base.file.?.pwriteAll(&data, file_pos);
-}
-
-pub fn deleteExport(self: *Elf, exp: Export) void {
- const sym_index = exp.sym_index orelse return;
- self.global_symbol_free_list.append(self.base.allocator, sym_index) catch {};
- self.global_symbols.items[sym_index].st_info = 0;
-}
-
-fn writeProgHeader(self: *Elf, index: usize) !void {
- const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
- const offset = self.program_headers.items[index].p_offset;
- switch (self.ptr_width) {
- .p32 => {
- var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])};
- if (foreign_endian) {
- bswapAllFields(elf.Elf32_Phdr, &phdr[0]);
- }
- return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
- },
- .p64 => {
- var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]};
- if (foreign_endian) {
- bswapAllFields(elf.Elf64_Phdr, &phdr[0]);
- }
- return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
- },
- }
-}
-
-fn writeSectHeader(self: *Elf, index: usize) !void {
- const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
- switch (self.ptr_width) {
- .p32 => {
- var shdr: [1]elf.Elf32_Shdr = undefined;
- shdr[0] = sectHeaderTo32(self.sections.items[index]);
- if (foreign_endian) {
- bswapAllFields(elf.Elf32_Shdr, &shdr[0]);
- }
- const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf32_Shdr);
- return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
- },
- .p64 => {
- var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]};
- if (foreign_endian) {
- bswapAllFields(elf.Elf64_Shdr, &shdr[0]);
- }
- const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf64_Shdr);
- return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
- },
- }
-}
-
-fn writeOffsetTableEntry(self: *Elf, index: usize) !void {
- const shdr = &self.sections.items[self.got_section_index.?];
- const phdr = &self.program_headers.items[self.phdr_got_index.?];
- const entry_size: u16 = self.archPtrWidthBytes();
- if (self.offset_table_count_dirty) {
- // TODO Also detect virtual address collisions.
- const allocated_size = self.allocatedSize(shdr.sh_offset);
- const needed_size = self.local_symbols.items.len * entry_size;
- if (needed_size > allocated_size) {
- // Must move the entire got section.
- const new_offset = self.findFreeSpace(needed_size, entry_size);
- const amt = try self.base.file.?.copyRangeAll(shdr.sh_offset, self.base.file.?, new_offset, shdr.sh_size);
- if (amt != shdr.sh_size) return error.InputOutput;
- shdr.sh_offset = new_offset;
- phdr.p_offset = new_offset;
- }
- shdr.sh_size = needed_size;
- phdr.p_memsz = needed_size;
- phdr.p_filesz = needed_size;
-
- self.shdr_table_dirty = true; // TODO look into making only the one section dirty
- self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
-
- self.offset_table_count_dirty = false;
- }
- const endian = self.base.options.target.cpu.arch.endian();
- const off = shdr.sh_offset + @as(u64, entry_size) * index;
- switch (entry_size) {
- 2 => {
- var buf: [2]u8 = undefined;
- mem.writeInt(u16, &buf, @intCast(u16, self.offset_table.items[index]), endian);
- try self.base.file.?.pwriteAll(&buf, off);
- },
- 4 => {
- var buf: [4]u8 = undefined;
- mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);
- try self.base.file.?.pwriteAll(&buf, off);
- },
- 8 => {
- var buf: [8]u8 = undefined;
- mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
- try self.base.file.?.pwriteAll(&buf, off);
- },
- else => unreachable,
- }
-}
-
-fn writeSymbol(self: *Elf, index: usize) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- const syms_sect = &self.sections.items[self.symtab_section_index.?];
- // Make sure we are not pointlessly writing symbol data that will have to get relocated
- // due to running out of space.
- if (self.local_symbols.items.len != syms_sect.sh_info) {
- const sym_size: u64 = switch (self.ptr_width) {
- .p32 => @sizeOf(elf.Elf32_Sym),
- .p64 => @sizeOf(elf.Elf64_Sym),
- };
- const sym_align: u16 = switch (self.ptr_width) {
- .p32 => @alignOf(elf.Elf32_Sym),
- .p64 => @alignOf(elf.Elf64_Sym),
- };
- const needed_size = (self.local_symbols.items.len + self.global_symbols.items.len) * sym_size;
- if (needed_size > self.allocatedSize(syms_sect.sh_offset)) {
- // Move all the symbols to a new file location.
- const new_offset = self.findFreeSpace(needed_size, sym_align);
- const existing_size = @as(u64, syms_sect.sh_info) * sym_size;
- const amt = try self.base.file.?.copyRangeAll(syms_sect.sh_offset, self.base.file.?, new_offset, existing_size);
- if (amt != existing_size) return error.InputOutput;
- syms_sect.sh_offset = new_offset;
- }
- syms_sect.sh_info = @intCast(u32, self.local_symbols.items.len);
- syms_sect.sh_size = needed_size; // anticipating adding the global symbols later
- self.shdr_table_dirty = true; // TODO look into only writing one section
- }
- const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
- switch (self.ptr_width) {
- .p32 => {
- var sym = [1]elf.Elf32_Sym{
- .{
- .st_name = self.local_symbols.items[index].st_name,
- .st_value = @intCast(u32, self.local_symbols.items[index].st_value),
- .st_size = @intCast(u32, self.local_symbols.items[index].st_size),
- .st_info = self.local_symbols.items[index].st_info,
- .st_other = self.local_symbols.items[index].st_other,
- .st_shndx = self.local_symbols.items[index].st_shndx,
- },
- };
- if (foreign_endian) {
- bswapAllFields(elf.Elf32_Sym, &sym[0]);
- }
- const off = syms_sect.sh_offset + @sizeOf(elf.Elf32_Sym) * index;
- try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
- },
- .p64 => {
- var sym = [1]elf.Elf64_Sym{self.local_symbols.items[index]};
- if (foreign_endian) {
- bswapAllFields(elf.Elf64_Sym, &sym[0]);
- }
- const off = syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index;
- try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
- },
- }
-}
-
-fn writeAllGlobalSymbols(self: *Elf) !void {
- const syms_sect = &self.sections.items[self.symtab_section_index.?];
- const sym_size: u64 = switch (self.ptr_width) {
- .p32 => @sizeOf(elf.Elf32_Sym),
- .p64 => @sizeOf(elf.Elf64_Sym),
- };
- const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
- const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size;
- switch (self.ptr_width) {
- .p32 => {
- const buf = try self.base.allocator.alloc(elf.Elf32_Sym, self.global_symbols.items.len);
- defer self.base.allocator.free(buf);
-
- for (buf) |*sym, i| {
- sym.* = .{
- .st_name = self.global_symbols.items[i].st_name,
- .st_value = @intCast(u32, self.global_symbols.items[i].st_value),
- .st_size = @intCast(u32, self.global_symbols.items[i].st_size),
- .st_info = self.global_symbols.items[i].st_info,
- .st_other = self.global_symbols.items[i].st_other,
- .st_shndx = self.global_symbols.items[i].st_shndx,
- };
- if (foreign_endian) {
- bswapAllFields(elf.Elf32_Sym, sym);
- }
- }
- try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
- },
- .p64 => {
- const buf = try self.base.allocator.alloc(elf.Elf64_Sym, self.global_symbols.items.len);
- defer self.base.allocator.free(buf);
-
- for (buf) |*sym, i| {
- sym.* = .{
- .st_name = self.global_symbols.items[i].st_name,
- .st_value = self.global_symbols.items[i].st_value,
- .st_size = self.global_symbols.items[i].st_size,
- .st_info = self.global_symbols.items[i].st_info,
- .st_other = self.global_symbols.items[i].st_other,
- .st_shndx = self.global_symbols.items[i].st_shndx,
- };
- if (foreign_endian) {
- bswapAllFields(elf.Elf64_Sym, sym);
- }
- }
- try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
- },
- }
-}
-
-/// Always 4 or 8 depending on whether this is 32-bit ELF or 64-bit ELF.
-fn ptrWidthBytes(self: Elf) u8 {
- return switch (self.ptr_width) {
- .p32 => 4,
- .p64 => 8,
- };
-}
-
-/// Does not necessarily match `ptrWidthBytes` for example can be 2 bytes
-/// in a 32-bit ELF file.
-fn archPtrWidthBytes(self: Elf) u8 {
- return @intCast(u8, self.base.options.target.cpu.arch.ptrBitWidth() / 8);
-}
-
-/// The reloc offset for the virtual address of a function in its Line Number Program.
-/// Size is a virtual address integer.
-const dbg_line_vaddr_reloc_index = 3;
-/// The reloc offset for the virtual address of a function in its .debug_info TAG_subprogram.
-/// Size is a virtual address integer.
-const dbg_info_low_pc_reloc_index = 1;
-
-/// The reloc offset for the line offset of a function from the previous function's line.
-/// It's a fixed-size 4-byte ULEB128.
-fn getRelocDbgLineOff(self: Elf) usize {
- return dbg_line_vaddr_reloc_index + self.ptrWidthBytes() + 1;
-}
-
-fn getRelocDbgFileIndex(self: Elf) usize {
- return self.getRelocDbgLineOff() + 5;
-}
-
-fn getRelocDbgInfoSubprogramHighPC(self: Elf) u32 {
- return dbg_info_low_pc_reloc_index + self.ptrWidthBytes();
-}
-
-fn dbgLineNeededHeaderBytes(self: Elf) u32 {
- const directory_entry_format_count = 1;
- const file_name_entry_format_count = 1;
- const directory_count = 1;
- const file_name_count = 1;
- return @intCast(u32, 53 + directory_entry_format_count * 2 + file_name_entry_format_count * 2 +
- directory_count * 8 + file_name_count * 8 +
- // These are encoded as DW.FORM_string rather than DW.FORM_strp as we would like
- // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly.
- self.base.options.module.?.root_pkg.root_src_directory.path.?.len +
- self.base.options.module.?.root_pkg.root_src_path.len);
-}
-
-fn dbgInfoNeededHeaderBytes(self: Elf) u32 {
- return 120;
-}
-
-const min_nop_size = 2;
-
-/// Writes to the file a buffer, prefixed and suffixed by the specified number of
-/// bytes of NOPs. Asserts each padding size is at least `min_nop_size` and total padding bytes
-/// are less than 126,976 bytes (if this limit is ever reached, this function can be
-/// improved to make more than one pwritev call, or the limit can be raised by a fixed
-/// amount by increasing the length of `vecs`).
-fn pwriteDbgLineNops(
- self: *Elf,
- prev_padding_size: usize,
- buf: []const u8,
- next_padding_size: usize,
- offset: usize,
-) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- const page_of_nops = [1]u8{DW.LNS_negate_stmt} ** 4096;
- const three_byte_nop = [3]u8{ DW.LNS_advance_pc, 0b1000_0000, 0 };
- var vecs: [32]std.os.iovec_const = undefined;
- var vec_index: usize = 0;
- {
- var padding_left = prev_padding_size;
- if (padding_left % 2 != 0) {
- vecs[vec_index] = .{
- .iov_base = &three_byte_nop,
- .iov_len = three_byte_nop.len,
- };
- vec_index += 1;
- padding_left -= three_byte_nop.len;
- }
- while (padding_left > page_of_nops.len) {
- vecs[vec_index] = .{
- .iov_base = &page_of_nops,
- .iov_len = page_of_nops.len,
- };
- vec_index += 1;
- padding_left -= page_of_nops.len;
- }
- if (padding_left > 0) {
- vecs[vec_index] = .{
- .iov_base = &page_of_nops,
- .iov_len = padding_left,
- };
- vec_index += 1;
- }
- }
-
- vecs[vec_index] = .{
- .iov_base = buf.ptr,
- .iov_len = buf.len,
- };
- vec_index += 1;
-
- {
- var padding_left = next_padding_size;
- if (padding_left % 2 != 0) {
- vecs[vec_index] = .{
- .iov_base = &three_byte_nop,
- .iov_len = three_byte_nop.len,
- };
- vec_index += 1;
- padding_left -= three_byte_nop.len;
- }
- while (padding_left > page_of_nops.len) {
- vecs[vec_index] = .{
- .iov_base = &page_of_nops,
- .iov_len = page_of_nops.len,
- };
- vec_index += 1;
- padding_left -= page_of_nops.len;
- }
- if (padding_left > 0) {
- vecs[vec_index] = .{
- .iov_base = &page_of_nops,
- .iov_len = padding_left,
- };
- vec_index += 1;
- }
- }
- try self.base.file.?.pwritevAll(vecs[0..vec_index], offset - prev_padding_size);
-}
-
-/// Writes to the file a buffer, prefixed and suffixed by the specified number of
-/// bytes of padding.
-fn pwriteDbgInfoNops(
- self: *Elf,
- prev_padding_size: usize,
- buf: []const u8,
- next_padding_size: usize,
- trailing_zero: bool,
- offset: usize,
-) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- const page_of_nops = [1]u8{abbrev_pad1} ** 4096;
- var vecs: [32]std.os.iovec_const = undefined;
- var vec_index: usize = 0;
- {
- var padding_left = prev_padding_size;
- while (padding_left > page_of_nops.len) {
- vecs[vec_index] = .{
- .iov_base = &page_of_nops,
- .iov_len = page_of_nops.len,
- };
- vec_index += 1;
- padding_left -= page_of_nops.len;
- }
- if (padding_left > 0) {
- vecs[vec_index] = .{
- .iov_base = &page_of_nops,
- .iov_len = padding_left,
- };
- vec_index += 1;
- }
- }
-
- vecs[vec_index] = .{
- .iov_base = buf.ptr,
- .iov_len = buf.len,
- };
- vec_index += 1;
-
- {
- var padding_left = next_padding_size;
- while (padding_left > page_of_nops.len) {
- vecs[vec_index] = .{
- .iov_base = &page_of_nops,
- .iov_len = page_of_nops.len,
- };
- vec_index += 1;
- padding_left -= page_of_nops.len;
- }
- if (padding_left > 0) {
- vecs[vec_index] = .{
- .iov_base = &page_of_nops,
- .iov_len = padding_left,
- };
- vec_index += 1;
- }
- }
-
- if (trailing_zero) {
- var zbuf = [1]u8{0};
- vecs[vec_index] = .{
- .iov_base = &zbuf,
- .iov_len = zbuf.len,
- };
- vec_index += 1;
- }
-
- try self.base.file.?.pwritevAll(vecs[0..vec_index], offset - prev_padding_size);
-}
-
-/// Saturating multiplication
-fn satMul(a: anytype, b: anytype) @TypeOf(a, b) {
- const T = @TypeOf(a, b);
- return std.math.mul(T, a, b) catch std.math.maxInt(T);
-}
-
-fn bswapAllFields(comptime S: type, ptr: *S) void {
- @panic("TODO implement bswapAllFields");
-}
-
-fn progHeaderTo32(phdr: elf.Elf64_Phdr) elf.Elf32_Phdr {
- return .{
- .p_type = phdr.p_type,
- .p_flags = phdr.p_flags,
- .p_offset = @intCast(u32, phdr.p_offset),
- .p_vaddr = @intCast(u32, phdr.p_vaddr),
- .p_paddr = @intCast(u32, phdr.p_paddr),
- .p_filesz = @intCast(u32, phdr.p_filesz),
- .p_memsz = @intCast(u32, phdr.p_memsz),
- .p_align = @intCast(u32, phdr.p_align),
- };
-}
-
-fn sectHeaderTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr {
- return .{
- .sh_name = shdr.sh_name,
- .sh_type = shdr.sh_type,
- .sh_flags = @intCast(u32, shdr.sh_flags),
- .sh_addr = @intCast(u32, shdr.sh_addr),
- .sh_offset = @intCast(u32, shdr.sh_offset),
- .sh_size = @intCast(u32, shdr.sh_size),
- .sh_link = shdr.sh_link,
- .sh_info = shdr.sh_info,
- .sh_addralign = @intCast(u32, shdr.sh_addralign),
- .sh_entsize = @intCast(u32, shdr.sh_entsize),
- };
-}
-
-fn getLDMOption(target: std.Target) ?[]const u8 {
- switch (target.cpu.arch) {
- .i386 => return "elf_i386",
- .aarch64 => return "aarch64linux",
- .aarch64_be => return "aarch64_be_linux",
- .arm, .thumb => return "armelf_linux_eabi",
- .armeb, .thumbeb => return "armebelf_linux_eabi",
- .powerpc => return "elf32ppclinux",
- .powerpc64 => return "elf64ppc",
- .powerpc64le => return "elf64lppc",
- .sparc, .sparcel => return "elf32_sparc",
- .sparcv9 => return "elf64_sparc",
- .mips => return "elf32btsmip",
- .mipsel => return "elf32ltsmip",
- .mips64 => return "elf64btsmip",
- .mips64el => return "elf64ltsmip",
- .s390x => return "elf64_s390",
- .x86_64 => {
- if (target.abi == .gnux32) {
- return "elf32_x86_64";
- }
- // Any target elf will use the freebsd osabi if suffixed with "_fbsd".
- if (target.os.tag == .freebsd) {
- return "elf_x86_64_fbsd";
- }
- return "elf_x86_64";
- },
- .riscv32 => return "elf32lriscv",
- .riscv64 => return "elf64lriscv",
- else => return null,
- }
-}
diff --git a/src-self-hosted/link/MachO.zig b/src-self-hosted/link/MachO.zig
deleted file mode 100644
index 3b70a0d710238c3f13b00d6a5da32f737dd95e86..0000000000000000000000000000000000000000
--- a/src-self-hosted/link/MachO.zig
+++ /dev/null
@@ -1,724 +0,0 @@
-const MachO = @This();
-
-const std = @import("std");
-const Allocator = std.mem.Allocator;
-const assert = std.debug.assert;
-const fs = std.fs;
-const log = std.log.scoped(.link);
-const macho = std.macho;
-const codegen = @import("../codegen.zig");
-const math = std.math;
-const mem = std.mem;
-
-const trace = @import("../tracy.zig").trace;
-const Type = @import("../type.zig").Type;
-const build_options = @import("build_options");
-const Module = @import("../Module.zig");
-const Compilation = @import("../Compilation.zig");
-const link = @import("../link.zig");
-const File = link.File;
-
-pub const base_tag: File.Tag = File.Tag.macho;
-
-const LoadCommand = union(enum) {
- Segment: macho.segment_command_64,
- LinkeditData: macho.linkedit_data_command,
- Symtab: macho.symtab_command,
- Dysymtab: macho.dysymtab_command,
-
- pub fn cmdsize(self: LoadCommand) u32 {
- return switch (self) {
- .Segment => |x| x.cmdsize,
- .LinkeditData => |x| x.cmdsize,
- .Symtab => |x| x.cmdsize,
- .Dysymtab => |x| x.cmdsize,
- };
- }
-
- pub fn write(self: LoadCommand, file: *fs.File, offset: u64) !void {
- return switch (self) {
- .Segment => |cmd| writeGeneric(cmd, file, offset),
- .LinkeditData => |cmd| writeGeneric(cmd, file, offset),
- .Symtab => |cmd| writeGeneric(cmd, file, offset),
- .Dysymtab => |cmd| writeGeneric(cmd, file, offset),
- };
- }
-
- fn writeGeneric(cmd: anytype, file: *fs.File, offset: u64) !void {
- const slice = [1]@TypeOf(cmd){cmd};
- return file.pwriteAll(mem.sliceAsBytes(slice[0..1]), offset);
- }
-};
-
-base: File,
-
-/// Table of all load commands
-load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
-segment_cmd_index: ?u16 = null,
-symtab_cmd_index: ?u16 = null,
-dysymtab_cmd_index: ?u16 = null,
-data_in_code_cmd_index: ?u16 = null,
-
-/// Table of all sections
-sections: std.ArrayListUnmanaged(macho.section_64) = .{},
-
-/// __TEXT segment sections
-text_section_index: ?u16 = null,
-cstring_section_index: ?u16 = null,
-const_text_section_index: ?u16 = null,
-stubs_section_index: ?u16 = null,
-stub_helper_section_index: ?u16 = null,
-
-/// __DATA segment sections
-got_section_index: ?u16 = null,
-const_data_section_index: ?u16 = null,
-
-entry_addr: ?u64 = null,
-
-/// Table of all symbols used.
-/// Internally references string table for names (which are optional).
-symbol_table: std.ArrayListUnmanaged(macho.nlist_64) = .{},
-
-/// Table of symbol names aka the string table.
-string_table: std.ArrayListUnmanaged(u8) = .{},
-
-/// Table of symbol vaddr values. The values is the absolute vaddr value.
-/// If the vaddr of the executable __TEXT segment vaddr changes, the entire offset
-/// table needs to be rewritten.
-offset_table: std.ArrayListUnmanaged(u64) = .{},
-
-error_flags: File.ErrorFlags = File.ErrorFlags{},
-
-cmd_table_dirty: bool = false,
-
-/// Pointer to the last allocated text block
-last_text_block: ?*TextBlock = null,
-
-/// `alloc_num / alloc_den` is the factor of padding when allocating.
-const alloc_num = 4;
-const alloc_den = 3;
-
-/// Default path to dyld
-/// TODO instead of hardcoding it, we should probably look through some env vars and search paths
-/// instead but this will do for now.
-const DEFAULT_DYLD_PATH: [*:0]const u8 = "/usr/lib/dyld";
-
-/// Default lib search path
-/// TODO instead of hardcoding it, we should probably look through some env vars and search paths
-/// instead but this will do for now.
-const DEFAULT_LIB_SEARCH_PATH: []const u8 = "/usr/lib";
-
-const LIB_SYSTEM_NAME: [*:0]const u8 = "System";
-/// TODO we should search for libSystem and fail if it doesn't exist, instead of hardcoding it
-const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.dylib";
-
-pub const TextBlock = struct {
- /// Index into the symbol table
- symbol_table_index: ?u32,
- /// Index into offset table
- offset_table_index: ?u32,
- /// Size of this text block
- size: u64,
- /// Points to the previous and next neighbours
- prev: ?*TextBlock,
- next: ?*TextBlock,
-
- pub const empty = TextBlock{
- .symbol_table_index = null,
- .offset_table_index = null,
- .size = 0,
- .prev = null,
- .next = null,
- };
-};
-
-pub const SrcFn = struct {
- pub const empty = SrcFn{};
-};
-
-pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*MachO {
- assert(options.object_format == .macho);
-
- if (options.use_llvm) return error.LLVM_BackendIsTODO_ForMachO; // TODO
- if (options.use_lld) return error.LLD_LinkingIsTODO_ForMachO; // TODO
-
- const file = try options.directory.handle.createFile(sub_path, .{
- .truncate = false,
- .read = true,
- .mode = link.determineMode(options),
- });
- errdefer file.close();
-
- const self = try createEmpty(allocator, options);
- errdefer self.base.destroy();
-
- self.base.file = file;
-
- switch (options.output_mode) {
- .Exe => {},
- .Obj => {},
- .Lib => return error.TODOImplementWritingLibFiles,
- }
-
- try self.populateMissingMetadata();
-
- return self;
-}
-
-pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO {
- const self = try gpa.create(MachO);
- self.* = .{
- .base = .{
- .tag = .macho,
- .options = options,
- .allocator = gpa,
- .file = null,
- },
- };
- return self;
-}
-
-pub fn flush(self: *MachO, comp: *Compilation) !void {
- if (build_options.have_llvm and self.base.options.use_lld) {
- return error.MachOLLDLinkingUnimplemented;
- } else {
- return self.flushModule(comp);
- }
-}
-
-pub fn flushModule(self: *MachO, comp: *Compilation) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- switch (self.base.options.output_mode) {
- .Exe => {
- var last_cmd_offset: usize = @sizeOf(macho.mach_header_64);
- {
- // Specify path to dynamic linker dyld
- const cmdsize = commandSize(@sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH));
- const load_dylinker = [1]macho.dylinker_command{
- .{
- .cmd = macho.LC_LOAD_DYLINKER,
- .cmdsize = cmdsize,
- .name = @sizeOf(macho.dylinker_command),
- },
- };
-
- try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylinker[0..1]), last_cmd_offset);
-
- const file_offset = last_cmd_offset + @sizeOf(macho.dylinker_command);
- try self.addPadding(cmdsize - @sizeOf(macho.dylinker_command), file_offset);
-
- try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), file_offset);
- last_cmd_offset += cmdsize;
- }
-
- {
- // Link against libSystem
- const cmdsize = commandSize(@sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH));
- // TODO Find a way to work out runtime version from the OS version triple stored in std.Target.
- // In the meantime, we're gonna hardcode to the minimum compatibility version of 1.0.0.
- const min_version = 0x10000;
- const dylib = .{
- .name = @sizeOf(macho.dylib_command),
- .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files
- .current_version = min_version,
- .compatibility_version = min_version,
- };
- const load_dylib = [1]macho.dylib_command{
- .{
- .cmd = macho.LC_LOAD_DYLIB,
- .cmdsize = cmdsize,
- .dylib = dylib,
- },
- };
-
- try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylib[0..1]), last_cmd_offset);
-
- const file_offset = last_cmd_offset + @sizeOf(macho.dylib_command);
- try self.addPadding(cmdsize - @sizeOf(macho.dylib_command), file_offset);
-
- try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), file_offset);
- last_cmd_offset += cmdsize;
- }
- },
- .Obj => {
- {
- const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
- symtab.nsyms = @intCast(u32, self.symbol_table.items.len);
- const allocated_size = self.allocatedSize(symtab.stroff);
- const needed_size = self.string_table.items.len;
- log.debug("allocated_size = 0x{x}, needed_size = 0x{x}\n", .{ allocated_size, needed_size });
-
- if (needed_size > allocated_size) {
- symtab.strsize = 0;
- symtab.stroff = @intCast(u32, self.findFreeSpace(needed_size, 1));
- }
- symtab.strsize = @intCast(u32, needed_size);
-
- log.debug("writing string table from 0x{x} to 0x{x}\n", .{ symtab.stroff, symtab.stroff + symtab.strsize });
-
- try self.base.file.?.pwriteAll(self.string_table.items, symtab.stroff);
- }
-
- var last_cmd_offset: usize = @sizeOf(macho.mach_header_64);
- for (self.load_commands.items) |cmd| {
- try cmd.write(&self.base.file.?, last_cmd_offset);
- last_cmd_offset += cmd.cmdsize();
- }
- const off = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64);
- try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items), off);
- },
- .Lib => return error.TODOImplementWritingLibFiles,
- }
-
- if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
- log.debug("flushing. no_entry_point_found = true\n", .{});
- self.error_flags.no_entry_point_found = true;
- } else {
- log.debug("flushing. no_entry_point_found = false\n", .{});
- self.error_flags.no_entry_point_found = false;
- try self.writeMachOHeader();
- }
-}
-
-pub fn deinit(self: *MachO) void {
- self.offset_table.deinit(self.base.allocator);
- self.string_table.deinit(self.base.allocator);
- self.symbol_table.deinit(self.base.allocator);
- self.sections.deinit(self.base.allocator);
- self.load_commands.deinit(self.base.allocator);
-}
-
-pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
- if (decl.link.macho.symbol_table_index) |_| return;
-
- try self.symbol_table.ensureCapacity(self.base.allocator, self.symbol_table.items.len + 1);
- try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);
-
- log.debug("allocating symbol index {} for {}\n", .{ self.symbol_table.items.len, decl.name });
- decl.link.macho.symbol_table_index = @intCast(u32, self.symbol_table.items.len);
- _ = self.symbol_table.addOneAssumeCapacity();
-
- decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len);
- _ = self.offset_table.addOneAssumeCapacity();
-
- self.symbol_table.items[decl.link.macho.symbol_table_index.?] = .{
- .n_strx = 0,
- .n_type = 0,
- .n_sect = 0,
- .n_desc = 0,
- .n_value = 0,
- };
- self.offset_table.items[decl.link.macho.offset_table_index.?] = 0;
-}
-
-pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- var code_buffer = std.ArrayList(u8).init(self.base.allocator);
- defer code_buffer.deinit();
-
- const typed_value = decl.typed_value.most_recent.typed_value;
- const res = try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, .none);
-
- const code = switch (res) {
- .externally_managed => |x| x,
- .appended => code_buffer.items,
- .fail => |em| {
- decl.analysis = .codegen_failure;
- try module.failed_decls.put(module.gpa, decl, em);
- return;
- },
- };
- log.debug("generated code {}\n", .{code});
-
- const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
- const symbol = &self.symbol_table.items[decl.link.macho.symbol_table_index.?];
-
- const decl_name = mem.spanZ(decl.name);
- const name_str_index = try self.makeString(decl_name);
- const addr = try self.allocateTextBlock(&decl.link.macho, code.len, required_alignment);
- log.debug("allocated text block for {} at 0x{x}\n", .{ decl_name, addr });
- log.debug("updated text section {}\n", .{self.sections.items[self.text_section_index.?]});
-
- symbol.* = .{
- .n_strx = name_str_index,
- .n_type = macho.N_SECT,
- .n_sect = @intCast(u8, self.text_section_index.?) + 1,
- .n_desc = 0,
- .n_value = addr,
- };
-
- // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
- const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
- try self.updateDeclExports(module, decl, decl_exports);
- try self.writeSymbol(decl.link.macho.symbol_table_index.?);
-
- const text_section = self.sections.items[self.text_section_index.?];
- const section_offset = symbol.n_value - text_section.addr;
- const file_offset = text_section.offset + section_offset;
- log.debug("file_offset 0x{x}\n", .{file_offset});
-
- try self.base.file.?.pwriteAll(code, file_offset);
-}
-
-pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {}
-
-pub fn updateDeclExports(
- self: *MachO,
- module: *Module,
- decl: *const Module.Decl,
- exports: []const *Module.Export,
-) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- if (decl.link.macho.symbol_table_index == null) return;
-
- const decl_sym = &self.symbol_table.items[decl.link.macho.symbol_table_index.?];
- // TODO implement
- if (exports.len == 0) return;
-
- const exp = exports[0];
- self.entry_addr = decl_sym.n_value;
- decl_sym.n_type |= macho.N_EXT;
- exp.link.sym_index = 0;
-}
-
-pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {}
-
-pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {
- return self.symbol_table.items[decl.link.macho.symbol_table_index.?].n_value;
-}
-
-pub fn populateMissingMetadata(self: *MachO) !void {
- if (self.segment_cmd_index == null) {
- self.segment_cmd_index = @intCast(u16, self.load_commands.items.len);
- try self.load_commands.append(self.base.allocator, .{
- .Segment = .{
- .cmd = macho.LC_SEGMENT_64,
- .cmdsize = @sizeOf(macho.segment_command_64),
- .segname = makeStaticString(""),
- .vmaddr = 0,
- .vmsize = 0,
- .fileoff = 0,
- .filesize = 0,
- .maxprot = 0,
- .initprot = 0,
- .nsects = 0,
- .flags = 0,
- },
- });
- self.cmd_table_dirty = true;
- }
- if (self.symtab_cmd_index == null) {
- self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
- try self.load_commands.append(self.base.allocator, .{
- .Symtab = .{
- .cmd = macho.LC_SYMTAB,
- .cmdsize = @sizeOf(macho.symtab_command),
- .symoff = 0,
- .nsyms = 0,
- .stroff = 0,
- .strsize = 0,
- },
- });
- self.cmd_table_dirty = true;
- }
- if (self.text_section_index == null) {
- self.text_section_index = @intCast(u16, self.sections.items.len);
- const segment = &self.load_commands.items[self.segment_cmd_index.?].Segment;
- segment.cmdsize += @sizeOf(macho.section_64);
- segment.nsects += 1;
-
- const file_size = self.base.options.program_code_size_hint;
- const off = @intCast(u32, self.findFreeSpace(file_size, 1));
- const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
-
- log.debug("found __text section free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
-
- try self.sections.append(self.base.allocator, .{
- .sectname = makeStaticString("__text"),
- .segname = makeStaticString("__TEXT"),
- .addr = 0,
- .size = file_size,
- .offset = off,
- .@"align" = 0x1000,
- .reloff = 0,
- .nreloc = 0,
- .flags = flags,
- .reserved1 = 0,
- .reserved2 = 0,
- .reserved3 = 0,
- });
-
- segment.vmsize += file_size;
- segment.filesize += file_size;
- segment.fileoff = off;
-
- log.debug("initial text section {}\n", .{self.sections.items[self.text_section_index.?]});
- }
- {
- const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
- if (symtab.symoff == 0) {
- const p_align = @sizeOf(macho.nlist_64);
- const nsyms = self.base.options.symbol_count_hint;
- const file_size = p_align * nsyms;
- const off = @intCast(u32, self.findFreeSpace(file_size, p_align));
- log.debug("found symbol table free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
- symtab.symoff = off;
- symtab.nsyms = @intCast(u32, nsyms);
- }
- if (symtab.stroff == 0) {
- try self.string_table.append(self.base.allocator, 0);
- const file_size = @intCast(u32, self.string_table.items.len);
- const off = @intCast(u32, self.findFreeSpace(file_size, 1));
- log.debug("found string table free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
- symtab.stroff = off;
- symtab.strsize = file_size;
- }
- }
-}
-
-fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
- const segment = &self.load_commands.items[self.segment_cmd_index.?].Segment;
- const text_section = &self.sections.items[self.text_section_index.?];
- const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den;
-
- var block_placement: ?*TextBlock = null;
- const addr = blk: {
- if (self.last_text_block) |last| {
- const last_symbol = self.symbol_table.items[last.symbol_table_index.?];
- const end_addr = last_symbol.n_value + last.size;
- const new_start_addr = mem.alignForwardGeneric(u64, end_addr, alignment);
- block_placement = last;
- break :blk new_start_addr;
- } else {
- break :blk text_section.addr;
- }
- };
- log.debug("computed symbol address 0x{x}\n", .{addr});
-
- const expand_text_section = block_placement == null or block_placement.?.next == null;
- if (expand_text_section) {
- const text_capacity = self.allocatedSize(text_section.offset);
- const needed_size = (addr + new_block_size) - text_section.addr;
- log.debug("text capacity 0x{x}, needed size 0x{x}\n", .{ text_capacity, needed_size });
- assert(needed_size <= text_capacity); // TODO handle growth
-
- self.last_text_block = text_block;
- text_section.size = needed_size;
- segment.vmsize = needed_size;
- segment.filesize = needed_size;
- if (alignment < text_section.@"align") {
- text_section.@"align" = @intCast(u32, alignment);
- }
- }
- text_block.size = new_block_size;
-
- if (text_block.prev) |prev| {
- prev.next = text_block.next;
- }
- if (text_block.next) |next| {
- next.prev = text_block.prev;
- }
-
- if (block_placement) |big_block| {
- text_block.prev = big_block;
- text_block.next = big_block.next;
- big_block.next = text_block;
- } else {
- text_block.prev = null;
- text_block.next = null;
- }
-
- return addr;
-}
-
-fn makeStaticString(comptime bytes: []const u8) [16]u8 {
- var buf = [_]u8{0} ** 16;
- if (bytes.len > buf.len) @compileError("string too long; max 16 bytes");
- mem.copy(u8, buf[0..], bytes);
- return buf;
-}
-
-fn makeString(self: *MachO, bytes: []const u8) !u32 {
- try self.string_table.ensureCapacity(self.base.allocator, self.string_table.items.len + bytes.len + 1);
- const result = self.string_table.items.len;
- self.string_table.appendSliceAssumeCapacity(bytes);
- self.string_table.appendAssumeCapacity(0);
- return @intCast(u32, result);
-}
-
-fn alignSize(comptime Int: type, min_size: anytype, alignment: Int) Int {
- const size = @intCast(Int, min_size);
- if (size % alignment == 0) return size;
-
- const div = size / alignment;
- return (div + 1) * alignment;
-}
-
-fn commandSize(min_size: anytype) u32 {
- return alignSize(u32, min_size, @sizeOf(u64));
-}
-
-fn addPadding(self: *MachO, size: u64, file_offset: u64) !void {
- if (size == 0) return;
-
- const buf = try self.base.allocator.alloc(u8, size);
- defer self.base.allocator.free(buf);
-
- mem.set(u8, buf[0..], 0);
-
- try self.base.file.?.pwriteAll(buf, file_offset);
-}
-
-fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
- const hdr_size: u64 = @sizeOf(macho.mach_header_64);
- if (start < hdr_size)
- return hdr_size;
-
- const end = start + satMul(size, alloc_num) / alloc_den;
-
- {
- const off = @sizeOf(macho.mach_header_64);
- var tight_size: u64 = 0;
- for (self.load_commands.items) |cmd| {
- tight_size += cmd.cmdsize();
- }
- const increased_size = satMul(tight_size, alloc_num) / alloc_den;
- const test_end = off + increased_size;
- if (end > off and start < test_end) {
- return test_end;
- }
- }
-
- for (self.sections.items) |section| {
- const increased_size = satMul(section.size, alloc_num) / alloc_den;
- const test_end = section.offset + increased_size;
- if (end > section.offset and start < test_end) {
- return test_end;
- }
- }
-
- if (self.symtab_cmd_index) |symtab_index| {
- const symtab = self.load_commands.items[symtab_index].Symtab;
- {
- const tight_size = @sizeOf(macho.nlist_64) * symtab.nsyms;
- const increased_size = satMul(tight_size, alloc_num) / alloc_den;
- const test_end = symtab.symoff + increased_size;
- if (end > symtab.symoff and start < test_end) {
- return test_end;
- }
- }
- {
- const increased_size = satMul(symtab.strsize, alloc_num) / alloc_den;
- const test_end = symtab.stroff + increased_size;
- if (end > symtab.stroff and start < test_end) {
- return test_end;
- }
- }
- }
-
- return null;
-}
-
-fn allocatedSize(self: *MachO, start: u64) u64 {
- if (start == 0)
- return 0;
- var min_pos: u64 = std.math.maxInt(u64);
- {
- const off = @sizeOf(macho.mach_header_64);
- if (off > start and off < min_pos) min_pos = off;
- }
- for (self.sections.items) |section| {
- if (section.offset <= start) continue;
- if (section.offset < min_pos) min_pos = section.offset;
- }
- if (self.symtab_cmd_index) |symtab_index| {
- const symtab = self.load_commands.items[symtab_index].Symtab;
- if (symtab.symoff > start and symtab.symoff < min_pos) min_pos = symtab.symoff;
- if (symtab.stroff > start and symtab.stroff < min_pos) min_pos = symtab.stroff;
- }
- return min_pos - start;
-}
-
-fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u16) u64 {
- var start: u64 = 0;
- while (self.detectAllocCollision(start, object_size)) |item_end| {
- start = mem.alignForwardGeneric(u64, item_end, min_alignment);
- }
- return start;
-}
-
-fn writeSymbol(self: *MachO, index: usize) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
- const sym = [1]macho.nlist_64{self.symbol_table.items[index]};
- const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;
- log.debug("writing symbol {} at 0x{x}\n", .{ sym[0], off });
- try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
-}
-
-/// Writes Mach-O file header.
-/// Should be invoked last as it needs up-to-date values of ncmds and sizeof_cmds bookkeeping
-/// variables.
-fn writeMachOHeader(self: *MachO) !void {
- var hdr: macho.mach_header_64 = undefined;
- hdr.magic = macho.MH_MAGIC_64;
-
- const CpuInfo = struct {
- cpu_type: macho.cpu_type_t,
- cpu_subtype: macho.cpu_subtype_t,
- };
-
- const cpu_info: CpuInfo = switch (self.base.options.target.cpu.arch) {
- .aarch64 => .{
- .cpu_type = macho.CPU_TYPE_ARM64,
- .cpu_subtype = macho.CPU_SUBTYPE_ARM_ALL,
- },
- .x86_64 => .{
- .cpu_type = macho.CPU_TYPE_X86_64,
- .cpu_subtype = macho.CPU_SUBTYPE_X86_64_ALL,
- },
- else => return error.UnsupportedMachOArchitecture,
- };
- hdr.cputype = cpu_info.cpu_type;
- hdr.cpusubtype = cpu_info.cpu_subtype;
-
- const filetype: u32 = switch (self.base.options.output_mode) {
- .Exe => macho.MH_EXECUTE,
- .Obj => macho.MH_OBJECT,
- .Lib => switch (self.base.options.link_mode) {
- .Static => return error.TODOStaticLibMachOType,
- .Dynamic => macho.MH_DYLIB,
- },
- };
- hdr.filetype = filetype;
- hdr.ncmds = @intCast(u32, self.load_commands.items.len);
-
- var sizeofcmds: u32 = 0;
- for (self.load_commands.items) |cmd| {
- sizeofcmds += cmd.cmdsize();
- }
-
- hdr.sizeofcmds = sizeofcmds;
-
- // TODO should these be set to something else?
- hdr.flags = 0;
- hdr.reserved = 0;
-
- log.debug("writing Mach-O header {}\n", .{hdr});
-
- try self.base.file.?.pwriteAll(@ptrCast([*]const u8, &hdr)[0..@sizeOf(macho.mach_header_64)], 0);
-}
-
-/// Saturating multiplication
-fn satMul(a: anytype, b: anytype) @TypeOf(a, b) {
- const T = @TypeOf(a, b);
- return std.math.mul(T, a, b) catch std.math.maxInt(T);
-}
diff --git a/src-self-hosted/link/Wasm.zig b/src-self-hosted/link/Wasm.zig
deleted file mode 100644
index 1160e471fefe8d1db6338f8e100358bccc972d14..0000000000000000000000000000000000000000
--- a/src-self-hosted/link/Wasm.zig
+++ /dev/null
@@ -1,274 +0,0 @@
-const Wasm = @This();
-
-const std = @import("std");
-const Allocator = std.mem.Allocator;
-const assert = std.debug.assert;
-const fs = std.fs;
-const leb = std.debug.leb;
-
-const Module = @import("../Module.zig");
-const Compilation = @import("../Compilation.zig");
-const codegen = @import("../codegen/wasm.zig");
-const link = @import("../link.zig");
-const trace = @import("../tracy.zig").trace;
-const build_options = @import("build_options");
-
-/// Various magic numbers defined by the wasm spec
-const spec = struct {
- const magic = [_]u8{ 0x00, 0x61, 0x73, 0x6D }; // \0asm
- const version = [_]u8{ 0x01, 0x00, 0x00, 0x00 }; // version 1
-
- const custom_id = 0;
- const types_id = 1;
- const imports_id = 2;
- const funcs_id = 3;
- const tables_id = 4;
- const memories_id = 5;
- const globals_id = 6;
- const exports_id = 7;
- const start_id = 8;
- const elements_id = 9;
- const code_id = 10;
- const data_id = 11;
-};
-
-pub const base_tag = link.File.Tag.wasm;
-
-pub const FnData = struct {
- /// Generated code for the type of the function
- functype: std.ArrayListUnmanaged(u8) = .{},
- /// Generated code for the body of the function
- code: std.ArrayListUnmanaged(u8) = .{},
- /// Locations in the generated code where function indexes must be filled in.
- /// This must be kept ordered by offset.
- idx_refs: std.ArrayListUnmanaged(struct { offset: u32, decl: *Module.Decl }) = .{},
-};
-
-base: link.File,
-
-/// List of all function Decls to be written to the output file. The index of
-/// each Decl in this list at the time of writing the binary is used as the
-/// function index.
-/// TODO: can/should we access some data structure in Module directly?
-funcs: std.ArrayListUnmanaged(*Module.Decl) = .{},
-
-pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Wasm {
- assert(options.object_format == .wasm);
-
- if (options.use_llvm) return error.LLVM_BackendIsTODO_ForWasm; // TODO
- if (options.use_lld) return error.LLD_LinkingIsTODO_ForWasm; // TODO
-
- // TODO: read the file and keep vaild parts instead of truncating
- const file = try options.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true });
- errdefer file.close();
-
- const wasm = try createEmpty(allocator, options);
- errdefer wasm.base.destroy();
-
- wasm.base.file = file;
-
- try file.writeAll(&(spec.magic ++ spec.version));
-
- return wasm;
-}
-
-pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Wasm {
- const wasm = try gpa.create(Wasm);
- wasm.* = .{
- .base = .{
- .tag = .wasm,
- .options = options,
- .file = null,
- .allocator = gpa,
- },
- };
- return wasm;
-}
-
-pub fn deinit(self: *Wasm) void {
- for (self.funcs.items) |decl| {
- decl.fn_link.wasm.?.functype.deinit(self.base.allocator);
- decl.fn_link.wasm.?.code.deinit(self.base.allocator);
- decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator);
- }
- self.funcs.deinit(self.base.allocator);
-}
-
-// Generate code for the Decl, storing it in memory to be later written to
-// the file on flush().
-pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
- if (decl.typed_value.most_recent.typed_value.ty.zigTypeTag() != .Fn)
- return error.TODOImplementNonFnDeclsForWasm;
-
- if (decl.fn_link.wasm) |*fn_data| {
- fn_data.functype.items.len = 0;
- fn_data.code.items.len = 0;
- fn_data.idx_refs.items.len = 0;
- } else {
- decl.fn_link.wasm = .{};
- try self.funcs.append(self.base.allocator, decl);
- }
- const fn_data = &decl.fn_link.wasm.?;
-
- var managed_functype = fn_data.functype.toManaged(self.base.allocator);
- var managed_code = fn_data.code.toManaged(self.base.allocator);
- try codegen.genFunctype(&managed_functype, decl);
- try codegen.genCode(&managed_code, decl);
- fn_data.functype = managed_functype.toUnmanaged();
- fn_data.code = managed_code.toUnmanaged();
-}
-
-pub fn updateDeclExports(
- self: *Wasm,
- module: *Module,
- decl: *const Module.Decl,
- exports: []const *Module.Export,
-) !void {}
-
-pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
- // TODO: remove this assert when non-function Decls are implemented
- assert(decl.typed_value.most_recent.typed_value.ty.zigTypeTag() == .Fn);
- _ = self.funcs.swapRemove(self.getFuncidx(decl).?);
- decl.fn_link.wasm.?.functype.deinit(self.base.allocator);
- decl.fn_link.wasm.?.code.deinit(self.base.allocator);
- decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator);
- decl.fn_link.wasm = null;
-}
-
-pub fn flush(self: *Wasm, comp: *Compilation) !void {
- if (build_options.have_llvm and self.base.options.use_lld) {
- return error.WasmLinkingWithLLDUnimplemented;
- } else {
- return self.flushModule(comp);
- }
-}
-
-pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- const file = self.base.file.?;
- const header_size = 5 + 1;
-
- // No need to rewrite the magic/version header
- try file.setEndPos(@sizeOf(@TypeOf(spec.magic ++ spec.version)));
- try file.seekTo(@sizeOf(@TypeOf(spec.magic ++ spec.version)));
-
- // Type section
- {
- const header_offset = try reserveVecSectionHeader(file);
- for (self.funcs.items) |decl| {
- try file.writeAll(decl.fn_link.wasm.?.functype.items);
- }
- try writeVecSectionHeader(
- file,
- header_offset,
- spec.types_id,
- @intCast(u32, (try file.getPos()) - header_offset - header_size),
- @intCast(u32, self.funcs.items.len),
- );
- }
-
- // Function section
- {
- const header_offset = try reserveVecSectionHeader(file);
- const writer = file.writer();
- for (self.funcs.items) |_, typeidx| try leb.writeULEB128(writer, @intCast(u32, typeidx));
- try writeVecSectionHeader(
- file,
- header_offset,
- spec.funcs_id,
- @intCast(u32, (try file.getPos()) - header_offset - header_size),
- @intCast(u32, self.funcs.items.len),
- );
- }
-
- // Export section
- if (self.base.options.module) |module| {
- const header_offset = try reserveVecSectionHeader(file);
- const writer = file.writer();
- var count: u32 = 0;
- for (module.decl_exports.entries.items) |entry| {
- for (entry.value) |exprt| {
- // Export name length + name
- try leb.writeULEB128(writer, @intCast(u32, exprt.options.name.len));
- try writer.writeAll(exprt.options.name);
-
- switch (exprt.exported_decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) {
- .Fn => {
- // Type of the export
- try writer.writeByte(0x00);
- // Exported function index
- try leb.writeULEB128(writer, self.getFuncidx(exprt.exported_decl).?);
- },
- else => return error.TODOImplementNonFnDeclsForWasm,
- }
-
- count += 1;
- }
- }
- try writeVecSectionHeader(
- file,
- header_offset,
- spec.exports_id,
- @intCast(u32, (try file.getPos()) - header_offset - header_size),
- count,
- );
- }
-
- // Code section
- {
- const header_offset = try reserveVecSectionHeader(file);
- const writer = file.writer();
- for (self.funcs.items) |decl| {
- const fn_data = &decl.fn_link.wasm.?;
-
- // Write the already generated code to the file, inserting
- // function indexes where required.
- var current: u32 = 0;
- for (fn_data.idx_refs.items) |idx_ref| {
- try writer.writeAll(fn_data.code.items[current..idx_ref.offset]);
- current = idx_ref.offset;
- // Use a fixed width here to make calculating the code size
- // in codegen.wasm.genCode() simpler.
- var buf: [5]u8 = undefined;
- leb.writeUnsignedFixed(5, &buf, self.getFuncidx(idx_ref.decl).?);
- try writer.writeAll(&buf);
- }
-
- try writer.writeAll(fn_data.code.items[current..]);
- }
- try writeVecSectionHeader(
- file,
- header_offset,
- spec.code_id,
- @intCast(u32, (try file.getPos()) - header_offset - header_size),
- @intCast(u32, self.funcs.items.len),
- );
- }
-}
-
-/// Get the current index of a given Decl in the function list
-/// TODO: we could maintain a hash map to potentially make this
-fn getFuncidx(self: Wasm, decl: *Module.Decl) ?u32 {
- return for (self.funcs.items) |func, idx| {
- if (func == decl) break @intCast(u32, idx);
- } else null;
-}
-
-fn reserveVecSectionHeader(file: fs.File) !u64 {
- // section id + fixed leb contents size + fixed leb vector length
- const header_size = 1 + 5 + 5;
- // TODO: this should be a single lseek(2) call, but fs.File does not
- // currently provide a way to do this.
- try file.seekBy(header_size);
- return (try file.getPos()) - header_size;
-}
-
-fn writeVecSectionHeader(file: fs.File, offset: u64, section: u8, size: u32, items: u32) !void {
- var buf: [1 + 5 + 5]u8 = undefined;
- buf[0] = section;
- leb.writeUnsignedFixed(5, buf[1..6], size);
- leb.writeUnsignedFixed(5, buf[6..], items);
- try file.pwriteAll(&buf, offset);
-}
diff --git a/src-self-hosted/link/cbe.h b/src-self-hosted/link/cbe.h
deleted file mode 100644
index 854032227d1aed97b78f359c282efecb0f5f5e8b..0000000000000000000000000000000000000000
--- a/src-self-hosted/link/cbe.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#if __STDC_VERSION__ >= 201112L
-#define zig_noreturn _Noreturn
-#elif __GNUC__
-#define zig_noreturn __attribute__ ((noreturn))
-#elif _MSC_VER
-#define zig_noreturn __declspec(noreturn)
-#else
-#define zig_noreturn
-#endif
-
-#if __GNUC__
-#define zig_unreachable() __builtin_unreachable()
-#else
-#define zig_unreachable()
-#endif
diff --git a/src-self-hosted/link/msdos-stub.bin b/src-self-hosted/link/msdos-stub.bin
deleted file mode 100644
index 96ad91198f0de1eb25b9d9846c44706823dffa58..0000000000000000000000000000000000000000
Binary files a/src-self-hosted/link/msdos-stub.bin and /dev/null differ
diff --git a/src-self-hosted/liveness.zig b/src-self-hosted/liveness.zig
deleted file mode 100644
index d528e09ce7b85cea0ed90f7919e1e8b7cc0a4866..0000000000000000000000000000000000000000
--- a/src-self-hosted/liveness.zig
+++ /dev/null
@@ -1,166 +0,0 @@
-const std = @import("std");
-const ir = @import("ir.zig");
-const trace = @import("tracy.zig").trace;
-
-/// Perform Liveness Analysis over the `Body`. Each `Inst` will have its `deaths` field populated.
-pub fn analyze(
- /// Used for temporary storage during the analysis.
- gpa: *std.mem.Allocator,
- /// Used to tack on extra allocations in the same lifetime as the existing instructions.
- arena: *std.mem.Allocator,
- body: ir.Body,
-) error{OutOfMemory}!void {
- const tracy = trace(@src());
- defer tracy.end();
-
- var table = std.AutoHashMap(*ir.Inst, void).init(gpa);
- defer table.deinit();
- try table.ensureCapacity(@intCast(u32, body.instructions.len));
- try analyzeWithTable(arena, &table, null, body);
-}
-
-fn analyzeWithTable(
- arena: *std.mem.Allocator,
- table: *std.AutoHashMap(*ir.Inst, void),
- new_set: ?*std.AutoHashMap(*ir.Inst, void),
- body: ir.Body,
-) error{OutOfMemory}!void {
- var i: usize = body.instructions.len;
-
- if (new_set) |ns| {
- // We are only interested in doing this for instructions which are born
- // before a conditional branch, so after obtaining the new set for
- // each branch we prune the instructions which were born within.
- while (i != 0) {
- i -= 1;
- const base = body.instructions[i];
- _ = ns.remove(base);
- try analyzeInst(arena, table, new_set, base);
- }
- } else {
- while (i != 0) {
- i -= 1;
- const base = body.instructions[i];
- try analyzeInst(arena, table, new_set, base);
- }
- }
-}
-
-fn analyzeInst(
- arena: *std.mem.Allocator,
- table: *std.AutoHashMap(*ir.Inst, void),
- new_set: ?*std.AutoHashMap(*ir.Inst, void),
- base: *ir.Inst,
-) error{OutOfMemory}!void {
- if (table.contains(base)) {
- base.deaths = 0;
- } else {
- // No tombstone for this instruction means it is never referenced,
- // and its birth marks its own death. Very metal 🤘
- base.deaths = 1 << ir.Inst.unreferenced_bit_index;
- }
-
- switch (base.tag) {
- .constant => return,
- .block => {
- const inst = base.castTag(.block).?;
- try analyzeWithTable(arena, table, new_set, inst.body);
- // We let this continue so that it can possibly mark the block as
- // unreferenced below.
- },
- .loop => {
- const inst = base.castTag(.loop).?;
- try analyzeWithTable(arena, table, new_set, inst.body);
- return; // Loop has no operands and it is always unreferenced.
- },
- .condbr => {
- const inst = base.castTag(.condbr).?;
-
- // Each death that occurs inside one branch, but not the other, needs
- // to be added as a death immediately upon entering the other branch.
-
- var then_table = std.AutoHashMap(*ir.Inst, void).init(table.allocator);
- defer then_table.deinit();
- try analyzeWithTable(arena, table, &then_table, inst.then_body);
-
- // Reset the table back to its state from before the branch.
- {
- var it = then_table.iterator();
- while (it.next()) |entry| {
- table.removeAssertDiscard(entry.key);
- }
- }
-
- var else_table = std.AutoHashMap(*ir.Inst, void).init(table.allocator);
- defer else_table.deinit();
- try analyzeWithTable(arena, table, &else_table, inst.else_body);
-
- var then_entry_deaths = std.ArrayList(*ir.Inst).init(table.allocator);
- defer then_entry_deaths.deinit();
- var else_entry_deaths = std.ArrayList(*ir.Inst).init(table.allocator);
- defer else_entry_deaths.deinit();
-
- {
- var it = else_table.iterator();
- while (it.next()) |entry| {
- const else_death = entry.key;
- if (!then_table.contains(else_death)) {
- try then_entry_deaths.append(else_death);
- }
- }
- }
- // This loop is the same, except it's for the then branch, and it additionally
- // has to put its items back into the table to undo the reset.
- {
- var it = then_table.iterator();
- while (it.next()) |entry| {
- const then_death = entry.key;
- if (!else_table.contains(then_death)) {
- try else_entry_deaths.append(then_death);
- }
- _ = try table.put(then_death, {});
- }
- }
- // Now we have to correctly populate new_set.
- if (new_set) |ns| {
- try ns.ensureCapacity(@intCast(u32, ns.count() + then_table.count() + else_table.count()));
- var it = then_table.iterator();
- while (it.next()) |entry| {
- _ = ns.putAssumeCapacity(entry.key, {});
- }
- it = else_table.iterator();
- while (it.next()) |entry| {
- _ = ns.putAssumeCapacity(entry.key, {});
- }
- }
- inst.then_death_count = std.math.cast(@TypeOf(inst.then_death_count), then_entry_deaths.items.len) catch return error.OutOfMemory;
- inst.else_death_count = std.math.cast(@TypeOf(inst.else_death_count), else_entry_deaths.items.len) catch return error.OutOfMemory;
- const allocated_slice = try arena.alloc(*ir.Inst, then_entry_deaths.items.len + else_entry_deaths.items.len);
- inst.deaths = allocated_slice.ptr;
- std.mem.copy(*ir.Inst, inst.thenDeaths(), then_entry_deaths.items);
- std.mem.copy(*ir.Inst, inst.elseDeaths(), else_entry_deaths.items);
-
- // Continue on with the instruction analysis. The following code will find the condition
- // instruction, and the deaths flag for the CondBr instruction will indicate whether the
- // condition's lifetime ends immediately before entering any branch.
- },
- else => {},
- }
-
- const needed_bits = base.operandCount();
- if (needed_bits <= ir.Inst.deaths_bits) {
- var bit_i: ir.Inst.DeathsBitIndex = 0;
- while (base.getOperand(bit_i)) |operand| : (bit_i += 1) {
- const prev = try table.fetchPut(operand, {});
- if (prev == null) {
- // Death.
- base.deaths |= @as(ir.Inst.DeathsInt, 1) << bit_i;
- if (new_set) |ns| try ns.putNoClobber(operand, {});
- }
- }
- } else {
- @panic("Handle liveness analysis for instructions with many parameters");
- }
-
- std.log.scoped(.liveness).debug("analyze {}: 0b{b}\n", .{ base.tag, base.deaths });
-}
diff --git a/src-self-hosted/llvm.zig b/src-self-hosted/llvm.zig
deleted file mode 100644
index 64a6d4e8b56365020ac17500ae1fe6a6b8ee057b..0000000000000000000000000000000000000000
--- a/src-self-hosted/llvm.zig
+++ /dev/null
@@ -1,74 +0,0 @@
-//! We do this instead of @cImport because the self-hosted compiler is easier
-//! to bootstrap if it does not depend on translate-c.
-
-pub const Link = ZigLLDLink;
-extern fn ZigLLDLink(
- oformat: ObjectFormatType,
- args: [*:null]const ?[*:0]const u8,
- arg_count: usize,
- append_diagnostic: fn (context: usize, ptr: [*]const u8, len: usize) callconv(.C) void,
- context_stdout: usize,
- context_stderr: usize,
-) bool;
-
-pub const ObjectFormatType = extern enum(c_int) {
- Unknown,
- COFF,
- ELF,
- MachO,
- Wasm,
- XCOFF,
-};
-
-pub const GetHostCPUName = LLVMGetHostCPUName;
-extern fn LLVMGetHostCPUName() ?[*:0]u8;
-
-pub const GetNativeFeatures = ZigLLVMGetNativeFeatures;
-extern fn ZigLLVMGetNativeFeatures() ?[*:0]u8;
-
-pub const WriteArchive = ZigLLVMWriteArchive;
-extern fn ZigLLVMWriteArchive(
- archive_name: [*:0]const u8,
- file_names_ptr: [*]const [*:0]const u8,
- file_names_len: usize,
- os_type: OSType,
-) bool;
-
-pub const OSType = extern enum(c_int) {
- UnknownOS = 0,
- Ananas = 1,
- CloudABI = 2,
- Darwin = 3,
- DragonFly = 4,
- FreeBSD = 5,
- Fuchsia = 6,
- IOS = 7,
- KFreeBSD = 8,
- Linux = 9,
- Lv2 = 10,
- MacOSX = 11,
- NetBSD = 12,
- OpenBSD = 13,
- Solaris = 14,
- Win32 = 15,
- Haiku = 16,
- Minix = 17,
- RTEMS = 18,
- NaCl = 19,
- CNK = 20,
- AIX = 21,
- CUDA = 22,
- NVCL = 23,
- AMDHSA = 24,
- PS4 = 25,
- ELFIAMCU = 26,
- TvOS = 27,
- WatchOS = 28,
- Mesa3D = 29,
- Contiki = 30,
- AMDPAL = 31,
- HermitCore = 32,
- Hurd = 33,
- WASI = 34,
- Emscripten = 35,
-};
diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig
deleted file mode 100644
index 02ba72b51cd4d6ce0f5e19aa51fdbb0eb311f0eb..0000000000000000000000000000000000000000
--- a/src-self-hosted/main.zig
+++ /dev/null
@@ -1,2190 +0,0 @@
-const std = @import("std");
-const assert = std.debug.assert;
-const io = std.io;
-const fs = std.fs;
-const mem = std.mem;
-const process = std.process;
-const Allocator = mem.Allocator;
-const ArrayList = std.ArrayList;
-const ast = std.zig.ast;
-const Compilation = @import("Compilation.zig");
-const link = @import("link.zig");
-const Package = @import("Package.zig");
-const zir = @import("zir.zig");
-const build_options = @import("build_options");
-const warn = std.log.warn;
-const introspect = @import("introspect.zig");
-const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
-const translate_c = @import("translate_c.zig");
-
-pub fn fatal(comptime format: []const u8, args: anytype) noreturn {
- std.log.emerg(format, args);
- process.exit(1);
-}
-
-pub const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB
-
-pub const Color = enum {
- Auto,
- Off,
- On,
-};
-
-const usage =
- \\Usage: zig [command] [options]
- \\
- \\Commands:
- \\
- \\ build-exe Create executable from source or object files
- \\ build-lib Create library from source or object files
- \\ build-obj Create object from source or assembly
- \\ cc Use Zig as a drop-in C compiler
- \\ c++ Use Zig as a drop-in C++ compiler
- \\ env Print lib path, std path, compiler id and version
- \\ fmt Parse file and render in canonical zig format
- \\ libc Display native libc paths file or validate one
- \\ translate-c Convert C code to Zig code
- \\ targets List available compilation targets
- \\ version Print version number and exit
- \\ zen Print zen of zig and exit
- \\
- \\General Options:
- \\
- \\ --help Print command-specific usage
- \\
-;
-
-pub const log_level: std.log.Level = switch (std.builtin.mode) {
- .Debug => .debug,
- .ReleaseSafe, .ReleaseFast => .info,
- .ReleaseSmall => .crit,
-};
-
-pub fn log(
- comptime level: std.log.Level,
- comptime scope: @TypeOf(.EnumLiteral),
- comptime format: []const u8,
- args: anytype,
-) void {
- // Hide debug messages unless added with `-Dlog=foo`.
- if (@enumToInt(level) > @enumToInt(std.log.level) or
- @enumToInt(level) > @enumToInt(std.log.Level.info))
- {
- const scope_name = @tagName(scope);
- const ok = comptime for (build_options.log_scopes) |log_scope| {
- if (mem.eql(u8, log_scope, scope_name))
- break true;
- } else return;
- }
-
- // We only recognize 4 log levels in this application.
- const level_txt = switch (level) {
- .emerg, .alert, .crit, .err => "error",
- .warn => "warning",
- .notice, .info => "info",
- .debug => "debug",
- };
- const prefix1 = level_txt;
- const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";
-
- // Print the message to stderr, silently ignoring any errors
- std.debug.print(prefix1 ++ prefix2 ++ format ++ "\n", args);
-}
-
-var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
-
-pub fn main() anyerror!void {
- const gpa = if (std.builtin.link_libc) std.heap.c_allocator else &general_purpose_allocator.allocator;
- defer if (!std.builtin.link_libc) {
- _ = general_purpose_allocator.deinit();
- };
- var arena_instance = std.heap.ArenaAllocator.init(gpa);
- defer arena_instance.deinit();
- const arena = &arena_instance.allocator;
-
- const args = try process.argsAlloc(arena);
- return mainArgs(gpa, arena, args);
-}
-
-pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !void {
- if (args.len <= 1) {
- std.log.info("{}", .{usage});
- fatal("expected command argument", .{});
- }
-
- const cmd = args[1];
- const cmd_args = args[2..];
- if (mem.eql(u8, cmd, "build-exe")) {
- return buildOutputType(gpa, arena, args, .{ .build = .Exe });
- } else if (mem.eql(u8, cmd, "build-lib")) {
- return buildOutputType(gpa, arena, args, .{ .build = .Lib });
- } else if (mem.eql(u8, cmd, "build-obj")) {
- return buildOutputType(gpa, arena, args, .{ .build = .Obj });
- } else if (mem.eql(u8, cmd, "cc")) {
- return buildOutputType(gpa, arena, args, .cc);
- } else if (mem.eql(u8, cmd, "c++")) {
- return buildOutputType(gpa, arena, args, .cpp);
- } else if (mem.eql(u8, cmd, "translate-c")) {
- return buildOutputType(gpa, arena, args, .translate_c);
- } else if (mem.eql(u8, cmd, "clang") or
- mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as"))
- {
- return punt_to_clang(arena, args);
- } else if (mem.eql(u8, cmd, "fmt")) {
- return cmdFmt(gpa, cmd_args);
- } else if (mem.eql(u8, cmd, "libc")) {
- return cmdLibC(gpa, cmd_args);
- } else if (mem.eql(u8, cmd, "targets")) {
- const info = try detectNativeTargetInfo(arena, .{});
- const stdout = io.getStdOut().outStream();
- return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target);
- } else if (mem.eql(u8, cmd, "version")) {
- try std.io.getStdOut().writeAll(build_options.version ++ "\n");
- } else if (mem.eql(u8, cmd, "env")) {
- try @import("print_env.zig").cmdEnv(arena, cmd_args, io.getStdOut().outStream());
- } else if (mem.eql(u8, cmd, "zen")) {
- try io.getStdOut().writeAll(info_zen);
- } else if (mem.eql(u8, cmd, "help")) {
- try io.getStdOut().writeAll(usage);
- } else {
- std.log.info("{}", .{usage});
- fatal("unknown command: {}", .{args[1]});
- }
-}
-
-const usage_build_generic =
- \\Usage: zig build-exe [files]
- \\ zig build-lib [files]
- \\ zig build-obj [files]
- \\
- \\Supported file types:
- \\ .zig Zig source code
- \\ .zir Zig Intermediate Representation code
- \\ (planned) .o ELF object file
- \\ (planned) .o MACH-O (macOS) object file
- \\ (planned) .obj COFF (Windows) object file
- \\ (planned) .lib COFF (Windows) static library
- \\ (planned) .a ELF static library
- \\ (planned) .so ELF shared object (dynamic link)
- \\ (planned) .dll Windows Dynamic Link Library
- \\ (planned) .dylib MACH-O (macOS) dynamic library
- \\ (planned) .s Target-specific assembly source code
- \\ (planned) .S Assembly with C preprocessor (requires LLVM extensions)
- \\ (planned) .c C source code (requires LLVM extensions)
- \\ (planned) .cpp C++ source code (requires LLVM extensions)
- \\ Other C++ extensions: .C .cc .cxx
- \\
- \\General Options:
- \\ -h, --help Print this help and exit
- \\ --watch Enable compiler REPL
- \\ --color [auto|off|on] Enable or disable colored error messages
- \\ -femit-bin[=path] (default) output machine code
- \\ -fno-emit-bin Do not output machine code
- \\ --show-builtin Output the source of @import("builtin") then exit
- \\
- \\Compile Options:
- \\ -target [name] -- see the targets command
- \\ -mcpu [cpu] Specify target CPU and feature set
- \\ -mcmodel=[default|tiny| Limit range of code and data virtual addresses
- \\ small|kernel|
- \\ medium|large]
- \\ --name [name] Override root name (not a file path)
- \\ --mode [mode] Set the build mode
- \\ Debug (default) optimizations off, safety on
- \\ ReleaseFast Optimizations on, safety off
- \\ ReleaseSafe Optimizations on, safety on
- \\ ReleaseSmall Optimize for small binary, safety off
- \\ -fPIC Force-enable Position Independent Code
- \\ -fno-PIC Force-disable Position Independent Code
- \\ -fstack-check Enable stack probing in unsafe builds
- \\ -fno-stack-check Disable stack probing in safe builds
- \\ -fsanitize-c Enable C undefined behavior detection in unsafe builds
- \\ -fno-sanitize-c Disable C undefined behavior detection in safe builds
- \\ -fvalgrind Include valgrind client requests in release builds
- \\ -fno-valgrind Omit valgrind client requests in debug builds
- \\ -fdll-export-fns Mark exported functions as DLL exports (Windows)
- \\ -fno-dll-export-fns Force-disable marking exported functions as DLL exports
- \\ --strip Omit debug symbols
- \\ --single-threaded Code assumes it is only used single-threaded
- \\ -ofmt=[mode] Override target object format
- \\ elf Executable and Linking Format
- \\ c Compile to C source code
- \\ wasm WebAssembly
- \\ pe Portable Executable (Windows)
- \\ coff (planned) Common Object File Format (Windows)
- \\ macho (planned) macOS relocatables
- \\ hex (planned) Intel IHEX
- \\ raw (planned) Dump machine code directly
- \\ -dirafter [dir] Add directory to AFTER include search path
- \\ -isystem [dir] Add directory to SYSTEM include search path
- \\ -I[dir] Add directory to include search path
- \\ -D[macro]=[value] Define C [macro] to [value] (1 if [value] omitted)
- \\ --libc [file] Provide a file which specifies libc paths
- \\
- \\Link Options:
- \\ -l[lib], --library [lib] Link against system library
- \\ -L[d], --library-directory [d] Add a directory to the library search path
- \\ -T[script] Use a custom linker script
- \\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so)
- \\ --version [ver] Dynamic library semver
- \\ -rdynamic Add all symbols to the dynamic symbol table
- \\ -rpath [path] Add directory to the runtime library search path
- \\ --eh-frame-hdr Enable C++ exception handling by passing --eh-frame-hdr to linker
- \\ -dynamic Force output to be dynamically linked
- \\ -static Force output to be statically linked
- \\
- \\Debug Options (Zig Compiler Development):
- \\ -ftime-report Print timing diagnostics
- \\ --verbose-link Display linker invocations
- \\ --verbose-cc Display C compiler invocations
- \\ --verbose-tokenize Enable compiler debug output for tokenization
- \\ --verbose-ast Enable compiler debug output for AST parsing
- \\ --verbose-ir Enable compiler debug output for Zig IR
- \\ --verbose-llvm-ir Enable compiler debug output for LLVM IR
- \\ --verbose-cimport Enable compiler debug output for C imports
- \\ --verbose-llvm-cpu-features Enable compiler debug output for LLVM CPU features
- \\
-;
-
-const repl_help =
- \\Commands:
- \\ update Detect changes to source files and update output files.
- \\ help Print this text
- \\ exit Quit this repl
- \\
-;
-
-const Emit = union(enum) {
- no,
- yes_default_path,
- yes: []const u8,
-};
-
-pub fn buildOutputType(
- gpa: *Allocator,
- arena: *Allocator,
- all_args: []const []const u8,
- arg_mode: union(enum) {
- build: std.builtin.OutputMode,
- cc,
- cpp,
- translate_c,
- },
-) !void {
- var color: Color = .Auto;
- var build_mode: std.builtin.Mode = .Debug;
- var provided_name: ?[]const u8 = null;
- var link_mode: ?std.builtin.LinkMode = null;
- var dll_export_fns: ?bool = null;
- var root_src_file: ?[]const u8 = null;
- var version: std.builtin.Version = .{ .major = 0, .minor = 0, .patch = 0 };
- var have_version = false;
- var strip = false;
- var single_threaded = false;
- var watch = false;
- var verbose_link = false;
- var verbose_cc = false;
- var verbose_tokenize = false;
- var verbose_ast = false;
- var verbose_ir = false;
- var verbose_llvm_ir = false;
- var verbose_cimport = false;
- var verbose_llvm_cpu_features = false;
- var time_report = false;
- var show_builtin = false;
- var emit_bin: Emit = .yes_default_path;
- var emit_zir: Emit = .no;
- var target_arch_os_abi: []const u8 = "native";
- var target_mcpu: ?[]const u8 = null;
- var target_dynamic_linker: ?[]const u8 = null;
- var target_ofmt: ?[]const u8 = null;
- var output_mode: std.builtin.OutputMode = undefined;
- var emit_h: Emit = undefined;
- var ensure_libc_on_non_freestanding = false;
- var ensure_libcpp_on_non_freestanding = false;
- var link_libc = false;
- var link_libcpp = false;
- var want_native_include_dirs = false;
- var enable_cache: ?bool = null;
- var want_pic: ?bool = null;
- var want_sanitize_c: ?bool = null;
- var want_stack_check: ?bool = null;
- var want_valgrind: ?bool = null;
- var rdynamic: bool = false;
- var only_pp_or_asm = false;
- var linker_script: ?[]const u8 = null;
- var version_script: ?[]const u8 = null;
- var disable_c_depfile = false;
- var override_soname: ?[]const u8 = null;
- var linker_gc_sections: ?bool = null;
- var linker_allow_shlib_undefined: ?bool = null;
- var linker_bind_global_refs_locally: ?bool = null;
- var linker_z_nodelete = false;
- var linker_z_defs = false;
- var stack_size_override: ?u64 = null;
- var use_llvm: ?bool = null;
- var use_lld: ?bool = null;
- var use_clang: ?bool = null;
- var link_eh_frame_hdr = false;
- var libc_paths_file: ?[]const u8 = null;
- var machine_code_model: std.builtin.CodeModel = .default;
-
- var system_libs = std.ArrayList([]const u8).init(gpa);
- defer system_libs.deinit();
-
- var clang_argv = std.ArrayList([]const u8).init(gpa);
- defer clang_argv.deinit();
-
- var lld_argv = std.ArrayList([]const u8).init(gpa);
- defer lld_argv.deinit();
-
- var lib_dirs = std.ArrayList([]const u8).init(gpa);
- defer lib_dirs.deinit();
-
- var rpath_list = std.ArrayList([]const u8).init(gpa);
- defer rpath_list.deinit();
-
- var c_source_files = std.ArrayList(Compilation.CSourceFile).init(gpa);
- defer c_source_files.deinit();
-
- var link_objects = std.ArrayList([]const u8).init(gpa);
- defer link_objects.deinit();
-
- var framework_dirs = std.ArrayList([]const u8).init(gpa);
- defer framework_dirs.deinit();
-
- var frameworks = std.ArrayList([]const u8).init(gpa);
- defer frameworks.deinit();
-
- if (arg_mode == .build or arg_mode == .translate_c) {
- output_mode = switch (arg_mode) {
- .build => |m| m,
- .translate_c => .Obj,
- else => unreachable,
- };
- switch (arg_mode) {
- .build => switch (output_mode) {
- .Exe => emit_h = .no,
- .Obj, .Lib => emit_h = .yes_default_path,
- },
- .translate_c => emit_h = .no,
- else => unreachable,
- }
- const args = all_args[2..];
- var i: usize = 0;
- while (i < args.len) : (i += 1) {
- const arg = args[i];
- if (mem.startsWith(u8, arg, "-")) {
- if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
- try io.getStdOut().writeAll(usage_build_generic);
- process.exit(0);
- } else if (mem.eql(u8, arg, "--color")) {
- if (i + 1 >= args.len) {
- fatal("expected [auto|on|off] after --color", .{});
- }
- i += 1;
- const next_arg = args[i];
- if (mem.eql(u8, next_arg, "auto")) {
- color = .Auto;
- } else if (mem.eql(u8, next_arg, "on")) {
- color = .On;
- } else if (mem.eql(u8, next_arg, "off")) {
- color = .Off;
- } else {
- fatal("expected [auto|on|off] after --color, found '{}'", .{next_arg});
- }
- } else if (mem.eql(u8, arg, "--mode")) {
- if (i + 1 >= args.len) {
- fatal("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode", .{});
- }
- i += 1;
- const next_arg = args[i];
- if (mem.eql(u8, next_arg, "Debug")) {
- build_mode = .Debug;
- } else if (mem.eql(u8, next_arg, "ReleaseSafe")) {
- build_mode = .ReleaseSafe;
- } else if (mem.eql(u8, next_arg, "ReleaseFast")) {
- build_mode = .ReleaseFast;
- } else if (mem.eql(u8, next_arg, "ReleaseSmall")) {
- build_mode = .ReleaseSmall;
- } else {
- fatal("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode, found '{}'", .{next_arg});
- }
- } else if (mem.eql(u8, arg, "--stack")) {
- if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
- i += 1;
- stack_size_override = std.fmt.parseInt(u64, args[i], 10) catch |err| {
- fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
- };
- } else if (mem.eql(u8, arg, "--name")) {
- if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
- i += 1;
- provided_name = args[i];
- } else if (mem.eql(u8, arg, "-rpath")) {
- if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
- i += 1;
- try rpath_list.append(args[i]);
- } else if (mem.eql(u8, arg, "--library-directory") or mem.eql(u8, arg, "-L")) {
- if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
- i += 1;
- try lib_dirs.append(args[i]);
- } else if (mem.eql(u8, arg, "-T")) {
- if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
- i += 1;
- linker_script = args[i];
- } else if (mem.eql(u8, arg, "--version-script")) {
- if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
- i += 1;
- version_script = args[i];
- } else if (mem.eql(u8, arg, "--library") or mem.eql(u8, arg, "-l")) {
- if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
- // We don't know whether this library is part of libc or libc++ until we resolve the target.
- // So we simply append to the list for now.
- i += 1;
- try system_libs.append(args[i]);
- } else if (mem.eql(u8, arg, "-D") or
- mem.eql(u8, arg, "-isystem") or
- mem.eql(u8, arg, "-I") or
- mem.eql(u8, arg, "-dirafter"))
- {
- if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
- i += 1;
- try clang_argv.append(arg);
- try clang_argv.append(args[i]);
- } else if (mem.eql(u8, arg, "--version")) {
- if (i + 1 >= args.len) {
- fatal("expected parameter after --version", .{});
- }
- i += 1;
- version = std.builtin.Version.parse(args[i]) catch |err| {
- fatal("unable to parse --version '{}': {}", .{ args[i], @errorName(err) });
- };
- have_version = true;
- } else if (mem.eql(u8, arg, "-target")) {
- if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
- i += 1;
- target_arch_os_abi = args[i];
- } else if (mem.eql(u8, arg, "-mcpu")) {
- if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
- i += 1;
- target_mcpu = args[i];
- } else if (mem.eql(u8, arg, "-mcmodel")) {
- if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
- i += 1;
- machine_code_model = parseCodeModel(args[i]);
- } else if (mem.startsWith(u8, arg, "-ofmt=")) {
- target_ofmt = arg["-ofmt=".len..];
- } else if (mem.startsWith(u8, arg, "-mcpu=")) {
- target_mcpu = arg["-mcpu=".len..];
- } else if (mem.startsWith(u8, arg, "-mcmodel=")) {
- machine_code_model = parseCodeModel(arg["-mcmodel=".len..]);
- } else if (mem.eql(u8, arg, "--dynamic-linker")) {
- if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
- i += 1;
- target_dynamic_linker = args[i];
- } else if (mem.eql(u8, arg, "--libc")) {
- if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
- i += 1;
- libc_paths_file = args[i];
- } else if (mem.eql(u8, arg, "--watch")) {
- watch = true;
- } else if (mem.eql(u8, arg, "-ftime-report")) {
- time_report = true;
- } else if (mem.eql(u8, arg, "-fPIC")) {
- want_pic = true;
- } else if (mem.eql(u8, arg, "-fno-PIC")) {
- want_pic = false;
- } else if (mem.eql(u8, arg, "-fstack-check")) {
- want_stack_check = true;
- } else if (mem.eql(u8, arg, "-fno-stack-check")) {
- want_stack_check = false;
- } else if (mem.eql(u8, arg, "-fsanitize-c")) {
- want_sanitize_c = true;
- } else if (mem.eql(u8, arg, "-fno-sanitize-c")) {
- want_sanitize_c = false;
- } else if (mem.eql(u8, arg, "-fvalgrind")) {
- want_valgrind = true;
- } else if (mem.eql(u8, arg, "-fno-valgrind")) {
- want_valgrind = false;
- } else if (mem.eql(u8, arg, "-fLLVM")) {
- use_llvm = true;
- } else if (mem.eql(u8, arg, "-fno-LLVM")) {
- use_llvm = false;
- } else if (mem.eql(u8, arg, "-fLLD")) {
- use_lld = true;
- } else if (mem.eql(u8, arg, "-fno-LLD")) {
- use_lld = false;
- } else if (mem.eql(u8, arg, "-fClang")) {
- use_clang = true;
- } else if (mem.eql(u8, arg, "-fno-Clang")) {
- use_clang = false;
- } else if (mem.eql(u8, arg, "-rdynamic")) {
- rdynamic = true;
- } else if (mem.eql(u8, arg, "-femit-bin")) {
- emit_bin = .yes_default_path;
- } else if (mem.startsWith(u8, arg, "-femit-bin=")) {
- emit_bin = .{ .yes = arg["-femit-bin=".len..] };
- } else if (mem.eql(u8, arg, "-fno-emit-bin")) {
- emit_bin = .no;
- } else if (mem.eql(u8, arg, "-femit-zir")) {
- emit_zir = .yes_default_path;
- } else if (mem.startsWith(u8, arg, "-femit-zir=")) {
- emit_zir = .{ .yes = arg["-femit-zir=".len..] };
- } else if (mem.eql(u8, arg, "-fno-emit-zir")) {
- emit_zir = .no;
- } else if (mem.eql(u8, arg, "-femit-h")) {
- emit_h = .yes_default_path;
- } else if (mem.startsWith(u8, arg, "-femit-h=")) {
- emit_h = .{ .yes = arg["-femit-h=".len..] };
- } else if (mem.eql(u8, arg, "-fno-emit-h")) {
- emit_h = .no;
- } else if (mem.eql(u8, arg, "-dynamic")) {
- link_mode = .Dynamic;
- } else if (mem.eql(u8, arg, "-static")) {
- link_mode = .Static;
- } else if (mem.eql(u8, arg, "-fdll-export-fns")) {
- dll_export_fns = true;
- } else if (mem.eql(u8, arg, "-fno-dll-export-fns")) {
- dll_export_fns = false;
- } else if (mem.eql(u8, arg, "--show-builtin")) {
- show_builtin = true;
- } else if (mem.eql(u8, arg, "--strip")) {
- strip = true;
- } else if (mem.eql(u8, arg, "--single-threaded")) {
- single_threaded = true;
- } else if (mem.eql(u8, arg, "--eh-frame-hdr")) {
- link_eh_frame_hdr = true;
- } else if (mem.eql(u8, arg, "-Bsymbolic")) {
- linker_bind_global_refs_locally = true;
- } else if (mem.eql(u8, arg, "--verbose-link")) {
- verbose_link = true;
- } else if (mem.eql(u8, arg, "--verbose-cc")) {
- verbose_cc = true;
- } else if (mem.eql(u8, arg, "--verbose-tokenize")) {
- verbose_tokenize = true;
- } else if (mem.eql(u8, arg, "--verbose-ast")) {
- verbose_ast = true;
- } else if (mem.eql(u8, arg, "--verbose-ir")) {
- verbose_ir = true;
- } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) {
- verbose_llvm_ir = true;
- } else if (mem.eql(u8, arg, "--verbose-cimport")) {
- verbose_cimport = true;
- } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) {
- verbose_llvm_cpu_features = true;
- } else if (mem.startsWith(u8, arg, "-T")) {
- linker_script = arg[2..];
- } else if (mem.startsWith(u8, arg, "-L")) {
- try lib_dirs.append(arg[2..]);
- } else if (mem.startsWith(u8, arg, "-l")) {
- // We don't know whether this library is part of libc or libc++ until we resolve the target.
- // So we simply append to the list for now.
- try system_libs.append(arg[2..]);
- } else if (mem.startsWith(u8, arg, "-D") or
- mem.startsWith(u8, arg, "-I"))
- {
- try clang_argv.append(arg);
- } else {
- fatal("unrecognized parameter: '{}'", .{arg});
- }
- } else switch (Compilation.classifyFileExt(arg)) {
- .object, .static_library => {
- try link_objects.append(arg);
- },
- .assembly, .c, .cpp, .h, .ll, .bc => {
- // TODO a way to pass extra flags on the CLI
- try c_source_files.append(.{ .src_path = arg });
- },
- .shared_library => {
- fatal("linking against dynamic libraries not yet supported", .{});
- },
- .zig, .zir => {
- if (root_src_file) |other| {
- fatal("found another zig file '{}' after root source file '{}'", .{ arg, other });
- } else {
- root_src_file = arg;
- }
- },
- .unknown => {
- fatal("unrecognized file extension of parameter '{}'", .{arg});
- },
- }
- }
- } else {
- emit_h = .no;
- strip = true;
- ensure_libc_on_non_freestanding = true;
- ensure_libcpp_on_non_freestanding = arg_mode == .cpp;
- want_native_include_dirs = true;
-
- var c_arg = false;
- var is_shared_lib = false;
- var linker_args = std.ArrayList([]const u8).init(arena);
- var it = ClangArgIterator.init(arena, all_args);
- while (it.has_next) {
- it.next() catch |err| {
- fatal("unable to parse command line parameters: {}", .{@errorName(err)});
- };
- switch (it.zig_equivalent) {
- .target => target_arch_os_abi = it.only_arg, // example: -target riscv64-linux-unknown
- .o => {
- // -o
- emit_bin = .{ .yes = it.only_arg };
- enable_cache = true;
- },
- .c => c_arg = true, // -c
- .other => {
- try clang_argv.appendSlice(it.other_args);
- },
- .positional => {
- const file_ext = Compilation.classifyFileExt(mem.spanZ(it.only_arg));
- switch (file_ext) {
- .assembly, .c, .cpp, .ll, .bc, .h => try c_source_files.append(.{ .src_path = it.only_arg }),
- .unknown, .shared_library, .object, .static_library => {
- try link_objects.append(it.only_arg);
- },
- .zig, .zir => {
- if (root_src_file) |other| {
- fatal("found another zig file '{}' after root source file '{}'", .{ it.only_arg, other });
- } else {
- root_src_file = it.only_arg;
- }
- },
- }
- },
- .l => {
- // -l
- // We don't know whether this library is part of libc or libc++ until we resolve the target.
- // So we simply append to the list for now.
- try system_libs.append(it.only_arg);
- },
- .ignore => {},
- .driver_punt => {
- // Never mind what we're doing, just pass the args directly. For example --help.
- return punt_to_clang(arena, all_args);
- },
- .pic => want_pic = true,
- .no_pic => want_pic = false,
- .nostdlib => ensure_libc_on_non_freestanding = false,
- .nostdlib_cpp => ensure_libcpp_on_non_freestanding = false,
- .shared => {
- link_mode = .Dynamic;
- is_shared_lib = true;
- },
- .rdynamic => rdynamic = true,
- .wl => {
- var split_it = mem.split(it.only_arg, ",");
- while (split_it.next()) |linker_arg| {
- try linker_args.append(linker_arg);
- }
- },
- .pp_or_asm => {
- // This handles both -E and -S.
- only_pp_or_asm = true;
- try clang_argv.appendSlice(it.other_args);
- },
- .optimize => {
- // Alright, what release mode do they want?
- if (mem.eql(u8, it.only_arg, "Os")) {
- build_mode = .ReleaseSmall;
- } else if (mem.eql(u8, it.only_arg, "O2") or
- mem.eql(u8, it.only_arg, "O3") or
- mem.eql(u8, it.only_arg, "O4"))
- {
- build_mode = .ReleaseFast;
- } else if (mem.eql(u8, it.only_arg, "Og") or
- mem.eql(u8, it.only_arg, "O0"))
- {
- build_mode = .Debug;
- } else {
- try clang_argv.appendSlice(it.other_args);
- }
- },
- .debug => {
- strip = false;
- if (mem.eql(u8, it.only_arg, "-g")) {
- // We handled with strip = false above.
- } else {
- try clang_argv.appendSlice(it.other_args);
- }
- },
- .sanitize => {
- if (mem.eql(u8, it.only_arg, "undefined")) {
- want_sanitize_c = true;
- } else {
- try clang_argv.appendSlice(it.other_args);
- }
- },
- .linker_script => linker_script = it.only_arg,
- .verbose_cmds => {
- verbose_cc = true;
- verbose_link = true;
- },
- .for_linker => try linker_args.append(it.only_arg),
- .linker_input_z => {
- try linker_args.append("-z");
- try linker_args.append(it.only_arg);
- },
- .lib_dir => try lib_dirs.append(it.only_arg),
- .mcpu => target_mcpu = it.only_arg,
- .dep_file => {
- disable_c_depfile = true;
- try clang_argv.appendSlice(it.other_args);
- },
- .framework_dir => try framework_dirs.append(it.only_arg),
- .framework => try frameworks.append(it.only_arg),
- .nostdlibinc => want_native_include_dirs = false,
- }
- }
- // Parse linker args.
- var i: usize = 0;
- while (i < linker_args.items.len) : (i += 1) {
- const arg = linker_args.items[i];
- if (mem.eql(u8, arg, "-soname")) {
- i += 1;
- if (i >= linker_args.items.len) {
- fatal("expected linker arg after '{}'", .{arg});
- }
- const soname = linker_args.items[i];
- override_soname = soname;
- // Use it as --name.
- // Example: libsoundio.so.2
- var prefix: usize = 0;
- if (mem.startsWith(u8, soname, "lib")) {
- prefix = 3;
- }
- var end: usize = soname.len;
- if (mem.endsWith(u8, soname, ".so")) {
- end -= 3;
- } else {
- var found_digit = false;
- while (end > 0 and std.ascii.isDigit(soname[end - 1])) {
- found_digit = true;
- end -= 1;
- }
- if (found_digit and end > 0 and soname[end - 1] == '.') {
- end -= 1;
- } else {
- end = soname.len;
- }
- if (mem.endsWith(u8, soname[prefix..end], ".so")) {
- end -= 3;
- }
- }
- provided_name = soname[prefix..end];
- } else if (mem.eql(u8, arg, "-rpath")) {
- i += 1;
- if (i >= linker_args.items.len) {
- fatal("expected linker arg after '{}'", .{arg});
- }
- try rpath_list.append(linker_args.items[i]);
- } else if (mem.eql(u8, arg, "-I") or
- mem.eql(u8, arg, "--dynamic-linker") or
- mem.eql(u8, arg, "-dynamic-linker"))
- {
- i += 1;
- if (i >= linker_args.items.len) {
- fatal("expected linker arg after '{}'", .{arg});
- }
- target_dynamic_linker = linker_args.items[i];
- } else if (mem.eql(u8, arg, "-E") or
- mem.eql(u8, arg, "--export-dynamic") or
- mem.eql(u8, arg, "-export-dynamic"))
- {
- rdynamic = true;
- } else if (mem.eql(u8, arg, "--version-script")) {
- i += 1;
- if (i >= linker_args.items.len) {
- fatal("expected linker arg after '{}'", .{arg});
- }
- version_script = linker_args.items[i];
- } else if (mem.startsWith(u8, arg, "-O")) {
- try lld_argv.append(arg);
- } else if (mem.eql(u8, arg, "--gc-sections")) {
- linker_gc_sections = true;
- } else if (mem.eql(u8, arg, "--no-gc-sections")) {
- linker_gc_sections = false;
- } else if (mem.eql(u8, arg, "--allow-shlib-undefined") or
- mem.eql(u8, arg, "-allow-shlib-undefined"))
- {
- linker_allow_shlib_undefined = true;
- } else if (mem.eql(u8, arg, "--no-allow-shlib-undefined") or
- mem.eql(u8, arg, "-no-allow-shlib-undefined"))
- {
- linker_allow_shlib_undefined = false;
- } else if (mem.eql(u8, arg, "-Bsymbolic")) {
- linker_bind_global_refs_locally = true;
- } else if (mem.eql(u8, arg, "-z")) {
- i += 1;
- if (i >= linker_args.items.len) {
- fatal("expected linker arg after '{}'", .{arg});
- }
- const z_arg = linker_args.items[i];
- if (mem.eql(u8, z_arg, "nodelete")) {
- linker_z_nodelete = true;
- } else if (mem.eql(u8, z_arg, "defs")) {
- linker_z_defs = true;
- } else {
- warn("unsupported linker arg: -z {}", .{z_arg});
- }
- } else if (mem.eql(u8, arg, "--major-image-version")) {
- i += 1;
- if (i >= linker_args.items.len) {
- fatal("expected linker arg after '{}'", .{arg});
- }
- version.major = std.fmt.parseInt(u32, linker_args.items[i], 10) catch |err| {
- fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
- };
- have_version = true;
- } else if (mem.eql(u8, arg, "--minor-image-version")) {
- i += 1;
- if (i >= linker_args.items.len) {
- fatal("expected linker arg after '{}'", .{arg});
- }
- version.minor = std.fmt.parseInt(u32, linker_args.items[i], 10) catch |err| {
- fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
- };
- have_version = true;
- } else if (mem.eql(u8, arg, "--stack")) {
- i += 1;
- if (i >= linker_args.items.len) {
- fatal("expected linker arg after '{}'", .{arg});
- }
- stack_size_override = std.fmt.parseInt(u64, linker_args.items[i], 10) catch |err| {
- fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
- };
- } else {
- warn("unsupported linker arg: {}", .{arg});
- }
- }
-
- if (want_sanitize_c) |wsc| {
- if (wsc and build_mode == .ReleaseFast) {
- build_mode = .ReleaseSafe;
- }
- }
-
- if (only_pp_or_asm) {
- output_mode = .Obj;
- fatal("TODO implement using zig cc as a preprocessor", .{});
- //// Transfer "link_objects" into c_source_files so that all those
- //// args make it onto the command line.
- //try c_source_files.appendSlice(link_objects.items);
- //for (c_source_files.items) |c_source_file| {
- // const src_path = switch (emit_bin) {
- // .yes => |p| p,
- // else => c_source_file.source_path,
- // };
- // const basename = fs.path.basename(src_path);
- // c_source_file.preprocessor_only_basename = basename;
- //}
- //emit_bin = .no;
- } else if (!c_arg) {
- output_mode = if (is_shared_lib) .Lib else .Exe;
- switch (emit_bin) {
- .no, .yes_default_path => {
- emit_bin = .{ .yes = "a.out" };
- enable_cache = true;
- },
- .yes => {},
- }
- } else {
- output_mode = .Obj;
- }
- if (c_source_files.items.len == 0 and link_objects.items.len == 0) {
- // For example `zig cc` and no args should print the "no input files" message.
- return punt_to_clang(arena, all_args);
- }
- }
-
- if (arg_mode == .translate_c and c_source_files.items.len != 1) {
- fatal("translate-c expects exactly 1 source file (found {})", .{c_source_files.items.len});
- }
-
- const root_name = if (provided_name) |n| n else blk: {
- if (root_src_file) |file| {
- const basename = fs.path.basename(file);
- break :blk mem.split(basename, ".").next().?;
- } else if (c_source_files.items.len == 1) {
- const basename = fs.path.basename(c_source_files.items[0].src_path);
- break :blk mem.split(basename, ".").next().?;
- } else if (link_objects.items.len == 1) {
- const basename = fs.path.basename(link_objects.items[0]);
- break :blk mem.split(basename, ".").next().?;
- } else if (emit_bin == .yes) {
- const basename = fs.path.basename(emit_bin.yes);
- break :blk mem.split(basename, ".").next().?;
- } else if (show_builtin) {
- break :blk "builtin";
- } else {
- fatal("--name [name] not provided and unable to infer", .{});
- }
- };
-
- var diags: std.zig.CrossTarget.ParseOptions.Diagnostics = .{};
- const cross_target = std.zig.CrossTarget.parse(.{
- .arch_os_abi = target_arch_os_abi,
- .cpu_features = target_mcpu,
- .dynamic_linker = target_dynamic_linker,
- .diagnostics = &diags,
- }) catch |err| switch (err) {
- error.UnknownCpuModel => {
- help: {
- var help_text = std.ArrayList(u8).init(arena);
- for (diags.arch.?.allCpuModels()) |cpu| {
- help_text.writer().print(" {}\n", .{cpu.name}) catch break :help;
- }
- std.log.info("Available CPUs for architecture '{}': {}", .{
- @tagName(diags.arch.?), help_text.items,
- });
- }
- fatal("Unknown CPU: '{}'", .{diags.cpu_name.?});
- },
- error.UnknownCpuFeature => {
- help: {
- var help_text = std.ArrayList(u8).init(arena);
- for (diags.arch.?.allFeaturesList()) |feature| {
- help_text.writer().print(" {}: {}\n", .{ feature.name, feature.description }) catch break :help;
- }
- std.log.info("Available CPU features for architecture '{}': {}", .{
- @tagName(diags.arch.?), help_text.items,
- });
- }
- fatal("Unknown CPU feature: '{}'", .{diags.unknown_feature_name});
- },
- else => |e| return e,
- };
-
- const target_info = try detectNativeTargetInfo(gpa, cross_target);
-
- if (target_info.target.os.tag != .freestanding) {
- if (ensure_libc_on_non_freestanding)
- link_libc = true;
- if (ensure_libcpp_on_non_freestanding)
- link_libcpp = true;
- }
-
- // Now that we have target info, we can find out if any of the system libraries
- // are part of libc or libc++. We remove them from the list and communicate their
- // existence via flags instead.
- {
- var i: usize = 0;
- while (i < system_libs.items.len) {
- const lib_name = system_libs.items[i];
- if (is_libc_lib_name(target_info.target, lib_name)) {
- link_libc = true;
- _ = system_libs.orderedRemove(i);
- continue;
- }
- if (is_libcpp_lib_name(target_info.target, lib_name)) {
- link_libcpp = true;
- _ = system_libs.orderedRemove(i);
- continue;
- }
- i += 1;
- }
- }
-
- if (cross_target.isNativeOs() and (system_libs.items.len != 0 or want_native_include_dirs)) {
- const paths = std.zig.system.NativePaths.detect(arena) catch |err| {
- fatal("unable to detect native system paths: {}", .{@errorName(err)});
- };
- for (paths.warnings.items) |warning| {
- warn("{}", .{warning});
- }
- try clang_argv.ensureCapacity(clang_argv.items.len + paths.include_dirs.items.len * 2);
- for (paths.include_dirs.items) |include_dir| {
- clang_argv.appendAssumeCapacity("-isystem");
- clang_argv.appendAssumeCapacity(include_dir);
- }
- for (paths.lib_dirs.items) |lib_dir| {
- try lib_dirs.append(lib_dir);
- }
- for (paths.rpaths.items) |rpath| {
- try rpath_list.append(rpath);
- }
- }
-
- const object_format: ?std.Target.ObjectFormat = blk: {
- const ofmt = target_ofmt orelse break :blk null;
- if (mem.eql(u8, ofmt, "elf")) {
- break :blk .elf;
- } else if (mem.eql(u8, ofmt, "c")) {
- break :blk .c;
- } else if (mem.eql(u8, ofmt, "coff")) {
- break :blk .coff;
- } else if (mem.eql(u8, ofmt, "pe")) {
- break :blk .pe;
- } else if (mem.eql(u8, ofmt, "macho")) {
- break :blk .macho;
- } else if (mem.eql(u8, ofmt, "wasm")) {
- break :blk .wasm;
- } else if (mem.eql(u8, ofmt, "hex")) {
- break :blk .hex;
- } else if (mem.eql(u8, ofmt, "raw")) {
- break :blk .raw;
- } else {
- fatal("unsupported object format: {}", .{ofmt});
- }
- };
-
- var cleanup_emit_bin_dir: ?fs.Dir = null;
- defer if (cleanup_emit_bin_dir) |*dir| dir.close();
-
- const emit_bin_loc: ?Compilation.EmitLoc = switch (emit_bin) {
- .no => null,
- .yes_default_path => Compilation.EmitLoc{
- .directory = .{ .path = null, .handle = fs.cwd() },
- .basename = try std.zig.binNameAlloc(
- arena,
- root_name,
- target_info.target,
- output_mode,
- link_mode,
- object_format,
- ),
- },
- .yes => |full_path| b: {
- const basename = fs.path.basename(full_path);
- if (fs.path.dirname(full_path)) |dirname| {
- const handle = try fs.cwd().openDir(dirname, .{});
- cleanup_emit_bin_dir = handle;
- break :b Compilation.EmitLoc{
- .basename = basename,
- .directory = .{
- .path = dirname,
- .handle = handle,
- },
- };
- } else {
- break :b Compilation.EmitLoc{
- .basename = basename,
- .directory = .{ .path = null, .handle = fs.cwd() },
- };
- }
- },
- };
-
- var cleanup_emit_h_dir: ?fs.Dir = null;
- defer if (cleanup_emit_h_dir) |*dir| dir.close();
-
- const emit_h_loc: ?Compilation.EmitLoc = switch (emit_h) {
- .no => null,
- .yes_default_path => Compilation.EmitLoc{
- .directory = .{ .path = null, .handle = fs.cwd() },
- .basename = try std.fmt.allocPrint(arena, "{}.h", .{root_name}),
- },
- .yes => |full_path| b: {
- const basename = fs.path.basename(full_path);
- if (fs.path.dirname(full_path)) |dirname| {
- const handle = try fs.cwd().openDir(dirname, .{});
- cleanup_emit_h_dir = handle;
- break :b Compilation.EmitLoc{
- .basename = basename,
- .directory = .{
- .path = dirname,
- .handle = handle,
- },
- };
- } else {
- break :b Compilation.EmitLoc{
- .basename = basename,
- .directory = .{ .path = null, .handle = fs.cwd() },
- };
- }
- },
- };
-
- const zir_out_path: ?[]const u8 = switch (emit_zir) {
- .no => null,
- .yes_default_path => blk: {
- if (root_src_file) |rsf| {
- if (mem.endsWith(u8, rsf, ".zir")) {
- break :blk try std.fmt.allocPrint(arena, "{}.out.zir", .{root_name});
- }
- }
- break :blk try std.fmt.allocPrint(arena, "{}.zir", .{root_name});
- },
- .yes => |p| p,
- };
-
- var root_pkg_memory: Package = undefined;
- const root_pkg: ?*Package = if (root_src_file) |src_path| blk: {
- root_pkg_memory = .{
- .root_src_directory = .{ .path = null, .handle = fs.cwd() },
- .root_src_path = src_path,
- };
- break :blk &root_pkg_memory;
- } else null;
-
- const self_exe_path = try fs.selfExePathAlloc(arena);
- var zig_lib_directory = introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
- fatal("unable to find zig installation directory: {}\n", .{@errorName(err)});
- };
- defer zig_lib_directory.handle.close();
-
- const random_seed = blk: {
- var random_seed: u64 = undefined;
- try std.crypto.randomBytes(mem.asBytes(&random_seed));
- break :blk random_seed;
- };
- var default_prng = std.rand.DefaultPrng.init(random_seed);
-
- var libc_installation: ?LibCInstallation = null;
- defer if (libc_installation) |*l| l.deinit(gpa);
-
- if (libc_paths_file) |paths_file| {
- libc_installation = LibCInstallation.parse(gpa, paths_file) catch |err| {
- fatal("unable to parse libc paths file: {}", .{@errorName(err)});
- };
- }
-
- const cache_parent_dir = if (root_pkg) |pkg| pkg.root_src_directory.handle else fs.cwd();
- var cache_dir = try cache_parent_dir.makeOpenPath("zig-cache", .{});
- defer cache_dir.close();
- const zig_cache_directory: Compilation.Directory = .{
- .handle = cache_dir,
- .path = blk: {
- if (root_pkg) |pkg| {
- if (pkg.root_src_directory.path) |p| {
- break :blk try fs.path.join(arena, &[_][]const u8{ p, "zig-cache" });
- }
- }
- break :blk "zig-cache";
- },
- };
-
- gimmeMoreOfThoseSweetSweetFileDescriptors();
-
- const comp = Compilation.create(gpa, .{
- .zig_lib_directory = zig_lib_directory,
- .zig_cache_directory = zig_cache_directory,
- .root_name = root_name,
- .target = target_info.target,
- .is_native_os = cross_target.isNativeOs(),
- .dynamic_linker = target_info.dynamic_linker.get(),
- .output_mode = output_mode,
- .root_pkg = root_pkg,
- .emit_bin = emit_bin_loc,
- .emit_h = emit_h_loc,
- .link_mode = link_mode,
- .dll_export_fns = dll_export_fns,
- .object_format = object_format,
- .optimize_mode = build_mode,
- .keep_source_files_loaded = zir_out_path != null,
- .clang_argv = clang_argv.items,
- .lld_argv = lld_argv.items,
- .lib_dirs = lib_dirs.items,
- .rpath_list = rpath_list.items,
- .c_source_files = c_source_files.items,
- .link_objects = link_objects.items,
- .framework_dirs = framework_dirs.items,
- .frameworks = frameworks.items,
- .system_libs = system_libs.items,
- .link_libc = link_libc,
- .link_libcpp = link_libcpp,
- .want_pic = want_pic,
- .want_sanitize_c = want_sanitize_c,
- .want_stack_check = want_stack_check,
- .want_valgrind = want_valgrind,
- .use_llvm = use_llvm,
- .use_lld = use_lld,
- .use_clang = use_clang,
- .rdynamic = rdynamic,
- .linker_script = linker_script,
- .version_script = version_script,
- .disable_c_depfile = disable_c_depfile,
- .override_soname = override_soname,
- .linker_gc_sections = linker_gc_sections,
- .linker_allow_shlib_undefined = linker_allow_shlib_undefined,
- .linker_bind_global_refs_locally = linker_bind_global_refs_locally,
- .linker_z_nodelete = linker_z_nodelete,
- .linker_z_defs = linker_z_defs,
- .link_eh_frame_hdr = link_eh_frame_hdr,
- .stack_size_override = stack_size_override,
- .strip = strip,
- .single_threaded = single_threaded,
- .self_exe_path = self_exe_path,
- .rand = &default_prng.random,
- .clang_passthrough_mode = arg_mode != .build,
- .version = if (have_version) version else null,
- .libc_installation = if (libc_installation) |*lci| lci else null,
- .verbose_cc = verbose_cc,
- .verbose_link = verbose_link,
- .verbose_tokenize = verbose_tokenize,
- .verbose_ast = verbose_ast,
- .verbose_ir = verbose_ir,
- .verbose_llvm_ir = verbose_llvm_ir,
- .verbose_cimport = verbose_cimport,
- .verbose_llvm_cpu_features = verbose_llvm_cpu_features,
- .machine_code_model = machine_code_model,
- .color = color,
- .time_report = time_report,
- }) catch |err| {
- fatal("unable to create compilation: {}", .{@errorName(err)});
- };
- defer comp.destroy();
-
- if (show_builtin) {
- return std.io.getStdOut().writeAll(try comp.generateBuiltinZigSource(arena));
- }
- if (arg_mode == .translate_c) {
- return cmdTranslateC(comp, arena);
- }
-
- try updateModule(gpa, comp, zir_out_path);
-
- if (build_options.have_llvm and only_pp_or_asm) {
- // this may include dumping the output to stdout
- fatal("TODO: implement `zig cc` when using it as a preprocessor", .{});
- }
-
- if (build_options.is_stage1 and comp.stage1_lock != null and watch) {
- std.log.warn("--watch is not recommended with the stage1 backend; it leaks memory and is not capable of incremental compilation", .{});
- }
-
- const stdin = std.io.getStdIn().inStream();
- const stderr = std.io.getStdErr().outStream();
- var repl_buf: [1024]u8 = undefined;
-
- while (watch) {
- try stderr.print("🦎 ", .{});
- if (output_mode == .Exe) {
- try comp.makeBinFileExecutable();
- }
- if (stdin.readUntilDelimiterOrEof(&repl_buf, '\n') catch |err| {
- try stderr.print("\nUnable to parse command: {}\n", .{@errorName(err)});
- continue;
- }) |line| {
- const actual_line = mem.trimRight(u8, line, "\r\n ");
-
- if (mem.eql(u8, actual_line, "update")) {
- if (output_mode == .Exe) {
- try comp.makeBinFileWritable();
- }
- try updateModule(gpa, comp, zir_out_path);
- } else if (mem.eql(u8, actual_line, "exit")) {
- break;
- } else if (mem.eql(u8, actual_line, "help")) {
- try stderr.writeAll(repl_help);
- } else {
- try stderr.print("unknown command: {}\n", .{actual_line});
- }
- } else {
- break;
- }
- }
-}
-
-fn updateModule(gpa: *Allocator, comp: *Compilation, zir_out_path: ?[]const u8) !void {
- try comp.update();
-
- var errors = try comp.getAllErrorsAlloc();
- defer errors.deinit(comp.gpa);
-
- if (errors.list.len != 0) {
- for (errors.list) |full_err_msg| {
- full_err_msg.renderToStdErr();
- }
- }
-
- if (zir_out_path) |zop| {
- const module = comp.bin_file.options.module orelse
- fatal("-femit-zir with no zig source code", .{});
- var new_zir_module = try zir.emit(gpa, module);
- defer new_zir_module.deinit(gpa);
-
- const baf = try io.BufferedAtomicFile.create(gpa, fs.cwd(), zop, .{});
- defer baf.destroy();
-
- try new_zir_module.writeToStream(gpa, baf.stream());
-
- try baf.finish();
- }
-}
-
-fn cmdTranslateC(comp: *Compilation, arena: *Allocator) !void {
- if (!build_options.have_llvm)
- fatal("cannot translate-c: compiler built without LLVM extensions", .{});
-
- assert(comp.c_source_files.len == 1);
-
- var argv = std.ArrayList([]const u8).init(arena);
-
- const c_source_file = comp.c_source_files[0];
- const file_ext = Compilation.classifyFileExt(c_source_file.src_path);
- try comp.addCCArgs(arena, &argv, file_ext, true, null);
- try argv.append(c_source_file.src_path);
-
- if (comp.verbose_cc) {
- std.debug.print("clang ", .{});
- Compilation.dump_argv(argv.items);
- }
-
- // Convert to null terminated args.
- const new_argv_with_sentinel = try arena.alloc(?[*:0]const u8, argv.items.len + 1);
- new_argv_with_sentinel[argv.items.len] = null;
- const new_argv = new_argv_with_sentinel[0..argv.items.len :null];
- for (argv.items) |arg, i| {
- new_argv[i] = try arena.dupeZ(u8, arg);
- }
-
- const c_headers_dir_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"include"});
- const c_headers_dir_path_z = try arena.dupeZ(u8, c_headers_dir_path);
- var clang_errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{};
- const tree = translate_c.translate(
- comp.gpa,
- new_argv.ptr,
- new_argv.ptr + new_argv.len,
- &clang_errors,
- c_headers_dir_path_z,
- ) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- error.ASTUnitFailure => fatal("clang API returned errors but due to a clang bug, it is not exposing the errors for zig to see. For more details: https://github.com/ziglang/zig/issues/4455", .{}),
- error.SemanticAnalyzeFail => {
- for (clang_errors) |clang_err| {
- std.debug.print("{}:{}:{}: {}\n", .{
- if (clang_err.filename_ptr) |p| p[0..clang_err.filename_len] else "(no file)",
- clang_err.line + 1,
- clang_err.column + 1,
- clang_err.msg_ptr[0..clang_err.msg_len],
- });
- }
- process.exit(1);
- },
- };
- defer tree.deinit();
-
- var bos = io.bufferedOutStream(io.getStdOut().writer());
- _ = try std.zig.render(comp.gpa, bos.writer(), tree);
- try bos.flush();
-}
-
-pub const usage_libc =
- \\Usage: zig libc
- \\
- \\ Detect the native libc installation and print the resulting
- \\ paths to stdout. You can save this into a file and then edit
- \\ the paths to create a cross compilation libc kit. Then you
- \\ can pass `--libc [file]` for Zig to use it.
- \\
- \\Usage: zig libc [paths_file]
- \\
- \\ Parse a libc installation text file and validate it.
- \\
-;
-
-pub fn cmdLibC(gpa: *Allocator, args: []const []const u8) !void {
- var input_file: ?[]const u8 = null;
- {
- var i: usize = 0;
- while (i < args.len) : (i += 1) {
- const arg = args[i];
- if (mem.startsWith(u8, arg, "-")) {
- if (mem.eql(u8, arg, "--help")) {
- const stdout = io.getStdOut().writer();
- try stdout.writeAll(usage_libc);
- process.exit(0);
- } else {
- fatal("unrecognized parameter: '{}'", .{arg});
- }
- } else if (input_file != null) {
- fatal("unexpected extra parameter: '{}'", .{arg});
- } else {
- input_file = arg;
- }
- }
- }
- if (input_file) |libc_file| {
- var libc = LibCInstallation.parse(gpa, libc_file) catch |err| {
- fatal("unable to parse libc file: {}", .{@errorName(err)});
- };
- defer libc.deinit(gpa);
- } else {
- var libc = LibCInstallation.findNative(.{
- .allocator = gpa,
- .verbose = true,
- }) catch |err| {
- fatal("unable to detect native libc: {}", .{@errorName(err)});
- };
- defer libc.deinit(gpa);
-
- var bos = io.bufferedOutStream(io.getStdOut().writer());
- try libc.render(bos.writer());
- try bos.flush();
- }
-}
-
-pub const usage_fmt =
- \\Usage: zig fmt [file]...
- \\
- \\ Formats the input files and modifies them in-place.
- \\ Arguments can be files or directories, which are searched
- \\ recursively.
- \\
- \\Options:
- \\ --help Print this help and exit
- \\ --color [auto|off|on] Enable or disable colored error messages
- \\ --stdin Format code from stdin; output to stdout
- \\ --check List non-conforming files and exit with an error
- \\ if the list is non-empty
- \\
- \\
-;
-
-const Fmt = struct {
- seen: SeenMap,
- any_error: bool,
- color: Color,
- gpa: *Allocator,
- out_buffer: std.ArrayList(u8),
-
- const SeenMap = std.AutoHashMap(fs.File.INode, void);
-};
-
-pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
- const stderr_file = io.getStdErr();
- var color: Color = .Auto;
- var stdin_flag: bool = false;
- var check_flag: bool = false;
- var input_files = ArrayList([]const u8).init(gpa);
-
- {
- var i: usize = 0;
- while (i < args.len) : (i += 1) {
- const arg = args[i];
- if (mem.startsWith(u8, arg, "-")) {
- if (mem.eql(u8, arg, "--help")) {
- const stdout = io.getStdOut().outStream();
- try stdout.writeAll(usage_fmt);
- process.exit(0);
- } else if (mem.eql(u8, arg, "--color")) {
- if (i + 1 >= args.len) {
- fatal("expected [auto|on|off] after --color", .{});
- }
- i += 1;
- const next_arg = args[i];
- if (mem.eql(u8, next_arg, "auto")) {
- color = .Auto;
- } else if (mem.eql(u8, next_arg, "on")) {
- color = .On;
- } else if (mem.eql(u8, next_arg, "off")) {
- color = .Off;
- } else {
- fatal("expected [auto|on|off] after --color, found '{}'", .{next_arg});
- }
- } else if (mem.eql(u8, arg, "--stdin")) {
- stdin_flag = true;
- } else if (mem.eql(u8, arg, "--check")) {
- check_flag = true;
- } else {
- fatal("unrecognized parameter: '{}'", .{arg});
- }
- } else {
- try input_files.append(arg);
- }
- }
- }
-
- if (stdin_flag) {
- if (input_files.items.len != 0) {
- fatal("cannot use --stdin with positional arguments", .{});
- }
-
- const stdin = io.getStdIn().inStream();
-
- const source_code = try stdin.readAllAlloc(gpa, max_src_size);
- defer gpa.free(source_code);
-
- const tree = std.zig.parse(gpa, source_code) catch |err| {
- fatal("error parsing stdin: {}", .{err});
- };
- defer tree.deinit();
-
- for (tree.errors) |parse_error| {
- try printErrMsgToFile(gpa, parse_error, tree, "", stderr_file, color);
- }
- if (tree.errors.len != 0) {
- process.exit(1);
- }
- if (check_flag) {
- const anything_changed = try std.zig.render(gpa, io.null_out_stream, tree);
- const code = if (anything_changed) @as(u8, 1) else @as(u8, 0);
- process.exit(code);
- }
-
- var bos = io.bufferedOutStream(io.getStdOut().writer());
- _ = try std.zig.render(gpa, bos.writer(), tree);
- try bos.flush();
- return;
- }
-
- if (input_files.items.len == 0) {
- fatal("expected at least one source file argument", .{});
- }
-
- var fmt = Fmt{
- .gpa = gpa,
- .seen = Fmt.SeenMap.init(gpa),
- .any_error = false,
- .color = color,
- .out_buffer = std.ArrayList(u8).init(gpa),
- };
- defer fmt.seen.deinit();
- defer fmt.out_buffer.deinit();
-
- for (input_files.span()) |file_path| {
- // Get the real path here to avoid Windows failing on relative file paths with . or .. in them.
- const real_path = fs.realpathAlloc(gpa, file_path) catch |err| {
- fatal("unable to open '{}': {}", .{ file_path, err });
- };
- defer gpa.free(real_path);
-
- try fmtPath(&fmt, file_path, check_flag, fs.cwd(), real_path);
- }
- if (fmt.any_error) {
- process.exit(1);
- }
-}
-
-const FmtError = error{
- SystemResources,
- OperationAborted,
- IoPending,
- BrokenPipe,
- Unexpected,
- WouldBlock,
- FileClosed,
- DestinationAddressRequired,
- DiskQuota,
- FileTooBig,
- InputOutput,
- NoSpaceLeft,
- AccessDenied,
- OutOfMemory,
- RenameAcrossMountPoints,
- ReadOnlyFileSystem,
- LinkQuotaExceeded,
- FileBusy,
- EndOfStream,
- Unseekable,
- NotOpenForWriting,
-} || fs.File.OpenError;
-
-fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_path: []const u8) FmtError!void {
- fmtPathFile(fmt, file_path, check_mode, dir, sub_path) catch |err| switch (err) {
- error.IsDir, error.AccessDenied => return fmtPathDir(fmt, file_path, check_mode, dir, sub_path),
- else => {
- warn("unable to format '{}': {}", .{ file_path, err });
- fmt.any_error = true;
- return;
- },
- };
-}
-
-fn fmtPathDir(
- fmt: *Fmt,
- file_path: []const u8,
- check_mode: bool,
- parent_dir: fs.Dir,
- parent_sub_path: []const u8,
-) FmtError!void {
- var dir = try parent_dir.openDir(parent_sub_path, .{ .iterate = true });
- defer dir.close();
-
- const stat = try dir.stat();
- if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
-
- var dir_it = dir.iterate();
- while (try dir_it.next()) |entry| {
- const is_dir = entry.kind == .Directory;
- if (is_dir or mem.endsWith(u8, entry.name, ".zig")) {
- const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name });
- defer fmt.gpa.free(full_path);
-
- if (is_dir) {
- try fmtPathDir(fmt, full_path, check_mode, dir, entry.name);
- } else {
- fmtPathFile(fmt, full_path, check_mode, dir, entry.name) catch |err| {
- warn("unable to format '{}': {}", .{ full_path, err });
- fmt.any_error = true;
- return;
- };
- }
- }
- }
-}
-
-fn fmtPathFile(
- fmt: *Fmt,
- file_path: []const u8,
- check_mode: bool,
- dir: fs.Dir,
- sub_path: []const u8,
-) FmtError!void {
- const source_file = try dir.openFile(sub_path, .{});
- var file_closed = false;
- errdefer if (!file_closed) source_file.close();
-
- const stat = try source_file.stat();
-
- if (stat.kind == .Directory)
- return error.IsDir;
-
- const source_code = source_file.readToEndAllocOptions(
- fmt.gpa,
- max_src_size,
- stat.size,
- @alignOf(u8),
- null,
- ) catch |err| switch (err) {
- error.ConnectionResetByPeer => unreachable,
- error.ConnectionTimedOut => unreachable,
- error.NotOpenForReading => unreachable,
- else => |e| return e,
- };
- source_file.close();
- file_closed = true;
- defer fmt.gpa.free(source_code);
-
- // Add to set after no longer possible to get error.IsDir.
- if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
-
- const tree = try std.zig.parse(fmt.gpa, source_code);
- defer tree.deinit();
-
- for (tree.errors) |parse_error| {
- try printErrMsgToFile(fmt.gpa, parse_error, tree, file_path, std.io.getStdErr(), fmt.color);
- }
- if (tree.errors.len != 0) {
- fmt.any_error = true;
- return;
- }
-
- if (check_mode) {
- const anything_changed = try std.zig.render(fmt.gpa, io.null_out_stream, tree);
- if (anything_changed) {
- // TODO this should output to stdout instead of stderr.
- std.debug.print("{}\n", .{file_path});
- fmt.any_error = true;
- }
- } else {
- // As a heuristic, we make enough capacity for the same as the input source.
- try fmt.out_buffer.ensureCapacity(source_code.len);
- fmt.out_buffer.items.len = 0;
- const writer = fmt.out_buffer.writer();
- const anything_changed = try std.zig.render(fmt.gpa, writer, tree);
- if (!anything_changed)
- return; // Good thing we didn't waste any file system access on this.
-
- var af = try dir.atomicFile(sub_path, .{ .mode = stat.mode });
- defer af.deinit();
-
- try af.file.writeAll(fmt.out_buffer.items);
- try af.finish();
- // TODO this should output to stdout instead of stderr.
- std.debug.print("{}\n", .{file_path});
- }
-}
-
-fn printErrMsgToFile(
- gpa: *mem.Allocator,
- parse_error: ast.Error,
- tree: *ast.Tree,
- path: []const u8,
- file: fs.File,
- color: Color,
-) !void {
- const color_on = switch (color) {
- .Auto => file.isTty(),
- .On => true,
- .Off => false,
- };
- const lok_token = parse_error.loc();
- const span_first = lok_token;
- const span_last = lok_token;
-
- const first_token = tree.token_locs[span_first];
- const last_token = tree.token_locs[span_last];
- const start_loc = tree.tokenLocationLoc(0, first_token);
- const end_loc = tree.tokenLocationLoc(first_token.end, last_token);
-
- var text_buf = std.ArrayList(u8).init(gpa);
- defer text_buf.deinit();
- const out_stream = text_buf.outStream();
- try parse_error.render(tree.token_ids, out_stream);
- const text = text_buf.span();
-
- const stream = file.outStream();
- try stream.print("{}:{}:{}: error: {}\n", .{ path, start_loc.line + 1, start_loc.column + 1, text });
-
- if (!color_on) return;
-
- // Print \r and \t as one space each so that column counts line up
- for (tree.source[start_loc.line_start..start_loc.line_end]) |byte| {
- try stream.writeByte(switch (byte) {
- '\r', '\t' => ' ',
- else => byte,
- });
- }
- try stream.writeByte('\n');
- try stream.writeByteNTimes(' ', start_loc.column);
- try stream.writeByteNTimes('~', last_token.end - first_token.start);
- try stream.writeByte('\n');
-}
-
-pub const info_zen =
- \\
- \\ * Communicate intent precisely.
- \\ * Edge cases matter.
- \\ * Favor reading code over writing code.
- \\ * Only one obvious way to do things.
- \\ * Runtime crashes are better than bugs.
- \\ * Compile errors are better than runtime crashes.
- \\ * Incremental improvements.
- \\ * Avoid local maximums.
- \\ * Reduce the amount one must remember.
- \\ * Focus on code rather than style.
- \\ * Resource allocation may fail; resource deallocation must succeed.
- \\ * Memory is a resource.
- \\ * Together we serve the users.
- \\
- \\
-;
-
-extern "c" fn ZigClang_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
-
-/// TODO https://github.com/ziglang/zig/issues/3257
-fn punt_to_clang(arena: *Allocator, args: []const []const u8) error{OutOfMemory} {
- if (!build_options.have_llvm)
- fatal("`zig cc` and `zig c++` unavailable: compiler built without LLVM extensions", .{});
- // Convert the args to the format Clang expects.
- const argv = try arena.alloc(?[*:0]u8, args.len + 1);
- for (args) |arg, i| {
- argv[i] = try arena.dupeZ(u8, arg); // TODO If there was an argsAllocZ we could avoid this allocation.
- }
- argv[args.len] = null;
- const exit_code = ZigClang_main(@intCast(c_int, args.len), argv[0..args.len :null].ptr);
- process.exit(@bitCast(u8, @truncate(i8, exit_code)));
-}
-
-const clang_args = @import("clang_options.zig").list;
-
-pub const ClangArgIterator = struct {
- has_next: bool,
- zig_equivalent: ZigEquivalent,
- only_arg: []const u8,
- second_arg: []const u8,
- other_args: []const []const u8,
- argv: []const []const u8,
- next_index: usize,
- root_args: ?*Args,
- allocator: *Allocator,
-
- pub const ZigEquivalent = enum {
- target,
- o,
- c,
- other,
- positional,
- l,
- ignore,
- driver_punt,
- pic,
- no_pic,
- nostdlib,
- nostdlib_cpp,
- shared,
- rdynamic,
- wl,
- pp_or_asm,
- optimize,
- debug,
- sanitize,
- linker_script,
- verbose_cmds,
- for_linker,
- linker_input_z,
- lib_dir,
- mcpu,
- dep_file,
- framework_dir,
- framework,
- nostdlibinc,
- };
-
- const Args = struct {
- next_index: usize,
- argv: []const []const u8,
- };
-
- fn init(allocator: *Allocator, argv: []const []const u8) ClangArgIterator {
- return .{
- .next_index = 2, // `zig cc foo` this points to `foo`
- .has_next = argv.len > 2,
- .zig_equivalent = undefined,
- .only_arg = undefined,
- .second_arg = undefined,
- .other_args = undefined,
- .argv = argv,
- .root_args = null,
- .allocator = allocator,
- };
- }
-
- fn next(self: *ClangArgIterator) !void {
- assert(self.has_next);
- assert(self.next_index < self.argv.len);
- // In this state we know that the parameter we are looking at is a root parameter
- // rather than an argument to a parameter.
- // We adjust the len below when necessary.
- self.other_args = (self.argv.ptr + self.next_index)[0..1];
- var arg = mem.span(self.argv[self.next_index]);
- self.incrementArgIndex();
-
- if (mem.startsWith(u8, arg, "@")) {
- if (self.root_args != null) return error.NestedResponseFile;
-
- // This is a "compiler response file". We must parse the file and treat its
- // contents as command line parameters.
- const allocator = self.allocator;
- const max_bytes = 10 * 1024 * 1024; // 10 MiB of command line arguments is a reasonable limit
- const resp_file_path = arg[1..];
- const resp_contents = fs.cwd().readFileAlloc(allocator, resp_file_path, max_bytes) catch |err| {
- fatal("unable to read response file '{}': {}", .{ resp_file_path, @errorName(err) });
- };
- defer allocator.free(resp_contents);
- // TODO is there a specification for this file format? Let's find it and make this parsing more robust
- // at the very least I'm guessing this needs to handle quotes and `#` comments.
- var it = mem.tokenize(resp_contents, " \t\r\n");
- var resp_arg_list = std.ArrayList([]const u8).init(allocator);
- defer resp_arg_list.deinit();
- {
- errdefer {
- for (resp_arg_list.span()) |item| {
- allocator.free(mem.span(item));
- }
- }
- while (it.next()) |token| {
- const dupe_token = try mem.dupeZ(allocator, u8, token);
- errdefer allocator.free(dupe_token);
- try resp_arg_list.append(dupe_token);
- }
- const args = try allocator.create(Args);
- errdefer allocator.destroy(args);
- args.* = .{
- .next_index = self.next_index,
- .argv = self.argv,
- };
- self.root_args = args;
- }
- const resp_arg_slice = resp_arg_list.toOwnedSlice();
- self.next_index = 0;
- self.argv = resp_arg_slice;
-
- if (resp_arg_slice.len == 0) {
- self.resolveRespFileArgs();
- return;
- }
-
- self.has_next = true;
- self.other_args = (self.argv.ptr + self.next_index)[0..1]; // We adjust len below when necessary.
- arg = mem.span(self.argv[self.next_index]);
- self.incrementArgIndex();
- }
- if (!mem.startsWith(u8, arg, "-")) {
- self.zig_equivalent = .positional;
- self.only_arg = arg;
- return;
- }
-
- find_clang_arg: for (clang_args) |clang_arg| switch (clang_arg.syntax) {
- .flag => {
- const prefix_len = clang_arg.matchEql(arg);
- if (prefix_len > 0) {
- self.zig_equivalent = clang_arg.zig_equivalent;
- self.only_arg = arg[prefix_len..];
-
- break :find_clang_arg;
- }
- },
- .joined, .comma_joined => {
- // joined example: --target=foo
- // comma_joined example: -Wl,-soname,libsoundio.so.2
- const prefix_len = clang_arg.matchStartsWith(arg);
- if (prefix_len != 0) {
- self.zig_equivalent = clang_arg.zig_equivalent;
- self.only_arg = arg[prefix_len..]; // This will skip over the "--target=" part.
-
- break :find_clang_arg;
- }
- },
- .joined_or_separate => {
- // Examples: `-lfoo`, `-l foo`
- const prefix_len = clang_arg.matchStartsWith(arg);
- if (prefix_len == arg.len) {
- if (self.next_index >= self.argv.len) {
- fatal("Expected parameter after '{}'", .{arg});
- }
- self.only_arg = self.argv[self.next_index];
- self.incrementArgIndex();
- self.other_args.len += 1;
- self.zig_equivalent = clang_arg.zig_equivalent;
-
- break :find_clang_arg;
- } else if (prefix_len != 0) {
- self.zig_equivalent = clang_arg.zig_equivalent;
- self.only_arg = arg[prefix_len..];
-
- break :find_clang_arg;
- }
- },
- .joined_and_separate => {
- // Example: `-Xopenmp-target=riscv64-linux-unknown foo`
- const prefix_len = clang_arg.matchStartsWith(arg);
- if (prefix_len != 0) {
- self.only_arg = arg[prefix_len..];
- if (self.next_index >= self.argv.len) {
- fatal("Expected parameter after '{}'", .{arg});
- }
- self.second_arg = self.argv[self.next_index];
- self.incrementArgIndex();
- self.other_args.len += 1;
- self.zig_equivalent = clang_arg.zig_equivalent;
- break :find_clang_arg;
- }
- },
- .separate => if (clang_arg.matchEql(arg) > 0) {
- if (self.next_index >= self.argv.len) {
- fatal("Expected parameter after '{}'", .{arg});
- }
- self.only_arg = self.argv[self.next_index];
- self.incrementArgIndex();
- self.other_args.len += 1;
- self.zig_equivalent = clang_arg.zig_equivalent;
- break :find_clang_arg;
- },
- .remaining_args_joined => {
- const prefix_len = clang_arg.matchStartsWith(arg);
- if (prefix_len != 0) {
- @panic("TODO");
- }
- },
- .multi_arg => if (clang_arg.matchEql(arg) > 0) {
- @panic("TODO");
- },
- }
- else {
- fatal("Unknown Clang option: '{}'", .{arg});
- }
- }
-
- fn incrementArgIndex(self: *ClangArgIterator) void {
- self.next_index += 1;
- self.resolveRespFileArgs();
- }
-
- fn resolveRespFileArgs(self: *ClangArgIterator) void {
- const allocator = self.allocator;
- if (self.next_index >= self.argv.len) {
- if (self.root_args) |root_args| {
- self.next_index = root_args.next_index;
- self.argv = root_args.argv;
-
- allocator.destroy(root_args);
- self.root_args = null;
- }
- if (self.next_index >= self.argv.len) {
- self.has_next = false;
- }
- }
- }
-};
-
-fn eqlIgnoreCase(ignore_case: bool, a: []const u8, b: []const u8) bool {
- if (ignore_case) {
- return std.ascii.eqlIgnoreCase(a, b);
- } else {
- return mem.eql(u8, a, b);
- }
-}
-
-fn is_libc_lib_name(target: std.Target, name: []const u8) bool {
- const ignore_case = target.os.tag.isDarwin() or target.os.tag == .windows;
-
- if (eqlIgnoreCase(ignore_case, name, "c"))
- return true;
-
- if (target.isMinGW()) {
- if (eqlIgnoreCase(ignore_case, name, "m"))
- return true;
-
- return false;
- }
-
- if (target.abi.isGnu() or target.abi.isMusl() or target.os.tag.isDarwin()) {
- if (eqlIgnoreCase(ignore_case, name, "m"))
- return true;
- if (eqlIgnoreCase(ignore_case, name, "rt"))
- return true;
- if (eqlIgnoreCase(ignore_case, name, "pthread"))
- return true;
- if (eqlIgnoreCase(ignore_case, name, "crypt"))
- return true;
- if (eqlIgnoreCase(ignore_case, name, "util"))
- return true;
- if (eqlIgnoreCase(ignore_case, name, "xnet"))
- return true;
- if (eqlIgnoreCase(ignore_case, name, "resolv"))
- return true;
- if (eqlIgnoreCase(ignore_case, name, "dl"))
- return true;
- if (eqlIgnoreCase(ignore_case, name, "util"))
- return true;
- }
-
- if (target.os.tag.isDarwin() and eqlIgnoreCase(ignore_case, name, "System"))
- return true;
-
- return false;
-}
-
-fn is_libcpp_lib_name(target: std.Target, name: []const u8) bool {
- const ignore_case = target.os.tag.isDarwin() or target.os.tag == .windows;
-
- return eqlIgnoreCase(ignore_case, name, "c++") or
- eqlIgnoreCase(ignore_case, name, "stdc++") or
- eqlIgnoreCase(ignore_case, name, "c++abi");
-}
-
-fn parseCodeModel(arg: []const u8) std.builtin.CodeModel {
- return std.meta.stringToEnum(std.builtin.CodeModel, arg) orelse
- fatal("unsupported machine code model: '{}'", .{arg});
-}
-
-/// Raise the open file descriptor limit. Ask and ye shall receive.
-/// For one example of why this is handy, consider the case of building musl libc.
-/// We keep a lock open for each of the object files in the form of a file descriptor
-/// until they are finally put into an archive file. This is to allow a zig-cache
-/// garbage collector to run concurrently to zig processes, and to allow multiple
-/// zig processes to run concurrently with each other, without clobbering each other.
-fn gimmeMoreOfThoseSweetSweetFileDescriptors() void {
- switch (std.Target.current.os.tag) {
- .windows, .wasi, .uefi, .other, .freestanding => return,
- // std lib is missing getrlimit/setrlimit.
- // https://github.com/ziglang/zig/issues/6361
- //else => {},
- else => return,
- }
- const posix = std.os;
- var lim = posix.getrlimit(posix.RLIMIT_NOFILE, &lim) catch return; // Oh well; we tried.
- if (lim.cur == lim.max) return;
- while (true) {
- // Do a binary search for the limit.
- var min: posix.rlim_t = lim.cur;
- var max: posix.rlim_t = 1 << 20;
- // But if there's a defined upper bound, don't search, just set it.
- if (lim.max != posix.RLIM_INFINITY) {
- min = lim.max;
- max = lim.max;
- }
- while (true) {
- lim.cur = min + (max - min) / 2;
- if (posix.setrlimit(posix.RLIMIT_NOFILE, lim)) |_| {
- min = lim.cur;
- } else |_| {
- max = lim.cur;
- }
- if (min + 1 < max) continue;
- return;
- }
- }
-}
-
-test "fds" {
- gimmeMoreOfThoseSweetSweetFileDescriptors();
-}
-
-fn detectNativeCpuWithLLVM(
- arch: std.Target.Cpu.Arch,
- llvm_cpu_name_z: ?[*:0]const u8,
- llvm_cpu_features_opt: ?[*:0]const u8,
-) !std.Target.Cpu {
- var result = std.Target.Cpu.baseline(arch);
-
- if (llvm_cpu_name_z) |cpu_name_z| {
- const llvm_cpu_name = mem.spanZ(cpu_name_z);
-
- for (arch.allCpuModels()) |model| {
- const this_llvm_name = model.llvm_name orelse continue;
- if (mem.eql(u8, this_llvm_name, llvm_cpu_name)) {
- // Here we use the non-dependencies-populated set,
- // so that subtracting features later in this function
- // affect the prepopulated set.
- result = std.Target.Cpu{
- .arch = arch,
- .model = model,
- .features = model.features,
- };
- break;
- }
- }
- }
-
- const all_features = arch.allFeaturesList();
-
- if (llvm_cpu_features_opt) |llvm_cpu_features| {
- var it = mem.tokenize(mem.spanZ(llvm_cpu_features), ",");
- while (it.next()) |decorated_llvm_feat| {
- var op: enum {
- add,
- sub,
- } = undefined;
- var llvm_feat: []const u8 = undefined;
- if (mem.startsWith(u8, decorated_llvm_feat, "+")) {
- op = .add;
- llvm_feat = decorated_llvm_feat[1..];
- } else if (mem.startsWith(u8, decorated_llvm_feat, "-")) {
- op = .sub;
- llvm_feat = decorated_llvm_feat[1..];
- } else {
- return error.InvalidLlvmCpuFeaturesFormat;
- }
- for (all_features) |feature, index_usize| {
- const this_llvm_name = feature.llvm_name orelse continue;
- if (mem.eql(u8, llvm_feat, this_llvm_name)) {
- const index = @intCast(std.Target.Cpu.Feature.Set.Index, index_usize);
- switch (op) {
- .add => result.features.addFeature(index),
- .sub => result.features.removeFeature(index),
- }
- break;
- }
- }
- }
- }
-
- result.features.populateDependencies(all_features);
- return result;
-}
-
-fn detectNativeTargetInfo(gpa: *Allocator, cross_target: std.zig.CrossTarget) !std.zig.system.NativeTargetInfo {
- var info = try std.zig.system.NativeTargetInfo.detect(gpa, cross_target);
- if (info.cpu_detection_unimplemented) {
- const arch = std.Target.current.cpu.arch;
-
- // We want to just use detected_info.target but implementing
- // CPU model & feature detection is todo so here we rely on LLVM.
- // https://github.com/ziglang/zig/issues/4591
- if (!build_options.have_llvm)
- fatal("CPU features detection is not yet available for {} without LLVM extensions", .{@tagName(arch)});
-
- const llvm = @import("llvm.zig");
- const llvm_cpu_name = llvm.GetHostCPUName();
- const llvm_cpu_features = llvm.GetNativeFeatures();
- info.target.cpu = try detectNativeCpuWithLLVM(arch, llvm_cpu_name, llvm_cpu_features);
- cross_target.updateCpuFeatures(&info.target.cpu.features);
- info.target.cpu.arch = cross_target.getCpuArch();
- }
- return info;
-}
diff --git a/src-self-hosted/musl.zig b/src-self-hosted/musl.zig
deleted file mode 100644
index 88536b90fdc4e8ce8c90b697c5f3044c3610b231..0000000000000000000000000000000000000000
--- a/src-self-hosted/musl.zig
+++ /dev/null
@@ -1,1843 +0,0 @@
-//! TODO build musl libc from source
-
-pub const src_files = [_][]const u8{
- "musl/src/aio/aio.c",
- "musl/src/aio/aio_suspend.c",
- "musl/src/aio/lio_listio.c",
- "musl/src/complex/__cexp.c",
- "musl/src/complex/__cexpf.c",
- "musl/src/complex/cabs.c",
- "musl/src/complex/cabsf.c",
- "musl/src/complex/cabsl.c",
- "musl/src/complex/cacos.c",
- "musl/src/complex/cacosf.c",
- "musl/src/complex/cacosh.c",
- "musl/src/complex/cacoshf.c",
- "musl/src/complex/cacoshl.c",
- "musl/src/complex/cacosl.c",
- "musl/src/complex/carg.c",
- "musl/src/complex/cargf.c",
- "musl/src/complex/cargl.c",
- "musl/src/complex/casin.c",
- "musl/src/complex/casinf.c",
- "musl/src/complex/casinh.c",
- "musl/src/complex/casinhf.c",
- "musl/src/complex/casinhl.c",
- "musl/src/complex/casinl.c",
- "musl/src/complex/catan.c",
- "musl/src/complex/catanf.c",
- "musl/src/complex/catanh.c",
- "musl/src/complex/catanhf.c",
- "musl/src/complex/catanhl.c",
- "musl/src/complex/catanl.c",
- "musl/src/complex/ccos.c",
- "musl/src/complex/ccosf.c",
- "musl/src/complex/ccosh.c",
- "musl/src/complex/ccoshf.c",
- "musl/src/complex/ccoshl.c",
- "musl/src/complex/ccosl.c",
- "musl/src/complex/cexp.c",
- "musl/src/complex/cexpf.c",
- "musl/src/complex/cexpl.c",
- "musl/src/complex/cimag.c",
- "musl/src/complex/cimagf.c",
- "musl/src/complex/cimagl.c",
- "musl/src/complex/clog.c",
- "musl/src/complex/clogf.c",
- "musl/src/complex/clogl.c",
- "musl/src/complex/conj.c",
- "musl/src/complex/conjf.c",
- "musl/src/complex/conjl.c",
- "musl/src/complex/cpow.c",
- "musl/src/complex/cpowf.c",
- "musl/src/complex/cpowl.c",
- "musl/src/complex/cproj.c",
- "musl/src/complex/cprojf.c",
- "musl/src/complex/cprojl.c",
- "musl/src/complex/creal.c",
- "musl/src/complex/crealf.c",
- "musl/src/complex/creall.c",
- "musl/src/complex/csin.c",
- "musl/src/complex/csinf.c",
- "musl/src/complex/csinh.c",
- "musl/src/complex/csinhf.c",
- "musl/src/complex/csinhl.c",
- "musl/src/complex/csinl.c",
- "musl/src/complex/csqrt.c",
- "musl/src/complex/csqrtf.c",
- "musl/src/complex/csqrtl.c",
- "musl/src/complex/ctan.c",
- "musl/src/complex/ctanf.c",
- "musl/src/complex/ctanh.c",
- "musl/src/complex/ctanhf.c",
- "musl/src/complex/ctanhl.c",
- "musl/src/complex/ctanl.c",
- "musl/src/conf/confstr.c",
- "musl/src/conf/fpathconf.c",
- "musl/src/conf/legacy.c",
- "musl/src/conf/pathconf.c",
- "musl/src/conf/sysconf.c",
- "musl/src/crypt/crypt.c",
- "musl/src/crypt/crypt_blowfish.c",
- "musl/src/crypt/crypt_des.c",
- "musl/src/crypt/crypt_md5.c",
- "musl/src/crypt/crypt_r.c",
- "musl/src/crypt/crypt_sha256.c",
- "musl/src/crypt/crypt_sha512.c",
- "musl/src/crypt/encrypt.c",
- "musl/src/ctype/__ctype_b_loc.c",
- "musl/src/ctype/__ctype_get_mb_cur_max.c",
- "musl/src/ctype/__ctype_tolower_loc.c",
- "musl/src/ctype/__ctype_toupper_loc.c",
- "musl/src/ctype/isalnum.c",
- "musl/src/ctype/isalpha.c",
- "musl/src/ctype/isascii.c",
- "musl/src/ctype/isblank.c",
- "musl/src/ctype/iscntrl.c",
- "musl/src/ctype/isdigit.c",
- "musl/src/ctype/isgraph.c",
- "musl/src/ctype/islower.c",
- "musl/src/ctype/isprint.c",
- "musl/src/ctype/ispunct.c",
- "musl/src/ctype/isspace.c",
- "musl/src/ctype/isupper.c",
- "musl/src/ctype/iswalnum.c",
- "musl/src/ctype/iswalpha.c",
- "musl/src/ctype/iswblank.c",
- "musl/src/ctype/iswcntrl.c",
- "musl/src/ctype/iswctype.c",
- "musl/src/ctype/iswdigit.c",
- "musl/src/ctype/iswgraph.c",
- "musl/src/ctype/iswlower.c",
- "musl/src/ctype/iswprint.c",
- "musl/src/ctype/iswpunct.c",
- "musl/src/ctype/iswspace.c",
- "musl/src/ctype/iswupper.c",
- "musl/src/ctype/iswxdigit.c",
- "musl/src/ctype/isxdigit.c",
- "musl/src/ctype/toascii.c",
- "musl/src/ctype/tolower.c",
- "musl/src/ctype/toupper.c",
- "musl/src/ctype/towctrans.c",
- "musl/src/ctype/wcswidth.c",
- "musl/src/ctype/wctrans.c",
- "musl/src/ctype/wcwidth.c",
- "musl/src/dirent/alphasort.c",
- "musl/src/dirent/closedir.c",
- "musl/src/dirent/dirfd.c",
- "musl/src/dirent/fdopendir.c",
- "musl/src/dirent/opendir.c",
- "musl/src/dirent/readdir.c",
- "musl/src/dirent/readdir_r.c",
- "musl/src/dirent/rewinddir.c",
- "musl/src/dirent/scandir.c",
- "musl/src/dirent/seekdir.c",
- "musl/src/dirent/telldir.c",
- "musl/src/dirent/versionsort.c",
- "musl/src/env/__environ.c",
- "musl/src/env/__init_tls.c",
- "musl/src/env/__libc_start_main.c",
- "musl/src/env/__reset_tls.c",
- "musl/src/env/__stack_chk_fail.c",
- "musl/src/env/clearenv.c",
- "musl/src/env/getenv.c",
- "musl/src/env/putenv.c",
- "musl/src/env/secure_getenv.c",
- "musl/src/env/setenv.c",
- "musl/src/env/unsetenv.c",
- "musl/src/errno/__errno_location.c",
- "musl/src/errno/strerror.c",
- "musl/src/exit/_Exit.c",
- "musl/src/exit/abort.c",
- "musl/src/exit/arm/__aeabi_atexit.c",
- "musl/src/exit/assert.c",
- "musl/src/exit/at_quick_exit.c",
- "musl/src/exit/atexit.c",
- "musl/src/exit/exit.c",
- "musl/src/exit/quick_exit.c",
- "musl/src/fcntl/creat.c",
- "musl/src/fcntl/fcntl.c",
- "musl/src/fcntl/open.c",
- "musl/src/fcntl/openat.c",
- "musl/src/fcntl/posix_fadvise.c",
- "musl/src/fcntl/posix_fallocate.c",
- "musl/src/fenv/__flt_rounds.c",
- "musl/src/fenv/aarch64/fenv.s",
- "musl/src/fenv/arm/fenv-hf.S",
- "musl/src/fenv/arm/fenv.c",
- "musl/src/fenv/fegetexceptflag.c",
- "musl/src/fenv/feholdexcept.c",
- "musl/src/fenv/fenv.c",
- "musl/src/fenv/fesetexceptflag.c",
- "musl/src/fenv/fesetround.c",
- "musl/src/fenv/feupdateenv.c",
- "musl/src/fenv/i386/fenv.s",
- "musl/src/fenv/m68k/fenv.c",
- "musl/src/fenv/mips/fenv-sf.c",
- "musl/src/fenv/mips/fenv.S",
- "musl/src/fenv/mips64/fenv-sf.c",
- "musl/src/fenv/mips64/fenv.S",
- "musl/src/fenv/mipsn32/fenv-sf.c",
- "musl/src/fenv/mipsn32/fenv.S",
- "musl/src/fenv/powerpc/fenv-sf.c",
- "musl/src/fenv/powerpc/fenv.S",
- "musl/src/fenv/powerpc64/fenv.c",
- "musl/src/fenv/riscv64/fenv-sf.c",
- "musl/src/fenv/riscv64/fenv.S",
- "musl/src/fenv/s390x/fenv.c",
- "musl/src/fenv/sh/fenv-nofpu.c",
- "musl/src/fenv/sh/fenv.S",
- "musl/src/fenv/x32/fenv.s",
- "musl/src/fenv/x86_64/fenv.s",
- "musl/src/internal/defsysinfo.c",
- "musl/src/internal/floatscan.c",
- "musl/src/internal/i386/defsysinfo.s",
- "musl/src/internal/intscan.c",
- "musl/src/internal/libc.c",
- "musl/src/internal/procfdname.c",
- "musl/src/internal/sh/__shcall.c",
- "musl/src/internal/shgetc.c",
- "musl/src/internal/syscall_ret.c",
- "musl/src/internal/vdso.c",
- "musl/src/internal/version.c",
- "musl/src/ipc/ftok.c",
- "musl/src/ipc/msgctl.c",
- "musl/src/ipc/msgget.c",
- "musl/src/ipc/msgrcv.c",
- "musl/src/ipc/msgsnd.c",
- "musl/src/ipc/semctl.c",
- "musl/src/ipc/semget.c",
- "musl/src/ipc/semop.c",
- "musl/src/ipc/semtimedop.c",
- "musl/src/ipc/shmat.c",
- "musl/src/ipc/shmctl.c",
- "musl/src/ipc/shmdt.c",
- "musl/src/ipc/shmget.c",
- "musl/src/ldso/__dlsym.c",
- "musl/src/ldso/aarch64/dlsym.s",
- "musl/src/ldso/aarch64/tlsdesc.s",
- "musl/src/ldso/arm/dlsym.s",
- "musl/src/ldso/arm/dlsym_time64.S",
- "musl/src/ldso/arm/find_exidx.c",
- "musl/src/ldso/arm/tlsdesc.S",
- "musl/src/ldso/dl_iterate_phdr.c",
- "musl/src/ldso/dladdr.c",
- "musl/src/ldso/dlclose.c",
- "musl/src/ldso/dlerror.c",
- "musl/src/ldso/dlinfo.c",
- "musl/src/ldso/dlopen.c",
- "musl/src/ldso/dlsym.c",
- "musl/src/ldso/i386/dlsym.s",
- "musl/src/ldso/i386/dlsym_time64.S",
- "musl/src/ldso/i386/tlsdesc.s",
- "musl/src/ldso/m68k/dlsym.s",
- "musl/src/ldso/m68k/dlsym_time64.S",
- "musl/src/ldso/microblaze/dlsym.s",
- "musl/src/ldso/microblaze/dlsym_time64.S",
- "musl/src/ldso/mips/dlsym.s",
- "musl/src/ldso/mips/dlsym_time64.S",
- "musl/src/ldso/mips64/dlsym.s",
- "musl/src/ldso/mipsn32/dlsym.s",
- "musl/src/ldso/mipsn32/dlsym_time64.S",
- "musl/src/ldso/or1k/dlsym.s",
- "musl/src/ldso/or1k/dlsym_time64.S",
- "musl/src/ldso/powerpc/dlsym.s",
- "musl/src/ldso/powerpc/dlsym_time64.S",
- "musl/src/ldso/powerpc64/dlsym.s",
- "musl/src/ldso/riscv64/dlsym.s",
- "musl/src/ldso/s390x/dlsym.s",
- "musl/src/ldso/sh/dlsym.s",
- "musl/src/ldso/sh/dlsym_time64.S",
- "musl/src/ldso/tlsdesc.c",
- "musl/src/ldso/x32/dlsym.s",
- "musl/src/ldso/x86_64/dlsym.s",
- "musl/src/ldso/x86_64/tlsdesc.s",
- "musl/src/legacy/cuserid.c",
- "musl/src/legacy/daemon.c",
- "musl/src/legacy/err.c",
- "musl/src/legacy/euidaccess.c",
- "musl/src/legacy/ftw.c",
- "musl/src/legacy/futimes.c",
- "musl/src/legacy/getdtablesize.c",
- "musl/src/legacy/getloadavg.c",
- "musl/src/legacy/getpagesize.c",
- "musl/src/legacy/getpass.c",
- "musl/src/legacy/getusershell.c",
- "musl/src/legacy/isastream.c",
- "musl/src/legacy/lutimes.c",
- "musl/src/legacy/ulimit.c",
- "musl/src/legacy/utmpx.c",
- "musl/src/legacy/valloc.c",
- "musl/src/linux/adjtime.c",
- "musl/src/linux/adjtimex.c",
- "musl/src/linux/arch_prctl.c",
- "musl/src/linux/brk.c",
- "musl/src/linux/cache.c",
- "musl/src/linux/cap.c",
- "musl/src/linux/chroot.c",
- "musl/src/linux/clock_adjtime.c",
- "musl/src/linux/clone.c",
- "musl/src/linux/copy_file_range.c",
- "musl/src/linux/epoll.c",
- "musl/src/linux/eventfd.c",
- "musl/src/linux/fallocate.c",
- "musl/src/linux/fanotify.c",
- "musl/src/linux/flock.c",
- "musl/src/linux/getdents.c",
- "musl/src/linux/getrandom.c",
- "musl/src/linux/inotify.c",
- "musl/src/linux/ioperm.c",
- "musl/src/linux/iopl.c",
- "musl/src/linux/klogctl.c",
- "musl/src/linux/membarrier.c",
- "musl/src/linux/memfd_create.c",
- "musl/src/linux/mlock2.c",
- "musl/src/linux/module.c",
- "musl/src/linux/mount.c",
- "musl/src/linux/name_to_handle_at.c",
- "musl/src/linux/open_by_handle_at.c",
- "musl/src/linux/personality.c",
- "musl/src/linux/pivot_root.c",
- "musl/src/linux/ppoll.c",
- "musl/src/linux/prctl.c",
- "musl/src/linux/prlimit.c",
- "musl/src/linux/process_vm.c",
- "musl/src/linux/ptrace.c",
- "musl/src/linux/quotactl.c",
- "musl/src/linux/readahead.c",
- "musl/src/linux/reboot.c",
- "musl/src/linux/remap_file_pages.c",
- "musl/src/linux/sbrk.c",
- "musl/src/linux/sendfile.c",
- "musl/src/linux/setfsgid.c",
- "musl/src/linux/setfsuid.c",
- "musl/src/linux/setgroups.c",
- "musl/src/linux/sethostname.c",
- "musl/src/linux/setns.c",
- "musl/src/linux/settimeofday.c",
- "musl/src/linux/signalfd.c",
- "musl/src/linux/splice.c",
- "musl/src/linux/stime.c",
- "musl/src/linux/swap.c",
- "musl/src/linux/sync_file_range.c",
- "musl/src/linux/syncfs.c",
- "musl/src/linux/sysinfo.c",
- "musl/src/linux/tee.c",
- "musl/src/linux/timerfd.c",
- "musl/src/linux/unshare.c",
- "musl/src/linux/utimes.c",
- "musl/src/linux/vhangup.c",
- "musl/src/linux/vmsplice.c",
- "musl/src/linux/wait3.c",
- "musl/src/linux/wait4.c",
- "musl/src/linux/x32/sysinfo.c",
- "musl/src/linux/xattr.c",
- "musl/src/locale/__lctrans.c",
- "musl/src/locale/__mo_lookup.c",
- "musl/src/locale/bind_textdomain_codeset.c",
- "musl/src/locale/c_locale.c",
- "musl/src/locale/catclose.c",
- "musl/src/locale/catgets.c",
- "musl/src/locale/catopen.c",
- "musl/src/locale/dcngettext.c",
- "musl/src/locale/duplocale.c",
- "musl/src/locale/freelocale.c",
- "musl/src/locale/iconv.c",
- "musl/src/locale/iconv_close.c",
- "musl/src/locale/langinfo.c",
- "musl/src/locale/locale_map.c",
- "musl/src/locale/localeconv.c",
- "musl/src/locale/newlocale.c",
- "musl/src/locale/pleval.c",
- "musl/src/locale/setlocale.c",
- "musl/src/locale/strcoll.c",
- "musl/src/locale/strfmon.c",
- "musl/src/locale/strxfrm.c",
- "musl/src/locale/textdomain.c",
- "musl/src/locale/uselocale.c",
- "musl/src/locale/wcscoll.c",
- "musl/src/locale/wcsxfrm.c",
- "musl/src/malloc/aligned_alloc.c",
- "musl/src/malloc/expand_heap.c",
- "musl/src/malloc/lite_malloc.c",
- "musl/src/malloc/malloc.c",
- "musl/src/malloc/malloc_usable_size.c",
- "musl/src/malloc/memalign.c",
- "musl/src/malloc/posix_memalign.c",
- "musl/src/math/__cos.c",
- "musl/src/math/__cosdf.c",
- "musl/src/math/__cosl.c",
- "musl/src/math/__expo2.c",
- "musl/src/math/__expo2f.c",
- "musl/src/math/__fpclassify.c",
- "musl/src/math/__fpclassifyf.c",
- "musl/src/math/__fpclassifyl.c",
- "musl/src/math/__invtrigl.c",
- "musl/src/math/__math_divzero.c",
- "musl/src/math/__math_divzerof.c",
- "musl/src/math/__math_invalid.c",
- "musl/src/math/__math_invalidf.c",
- "musl/src/math/__math_oflow.c",
- "musl/src/math/__math_oflowf.c",
- "musl/src/math/__math_uflow.c",
- "musl/src/math/__math_uflowf.c",
- "musl/src/math/__math_xflow.c",
- "musl/src/math/__math_xflowf.c",
- "musl/src/math/__polevll.c",
- "musl/src/math/__rem_pio2.c",
- "musl/src/math/__rem_pio2_large.c",
- "musl/src/math/__rem_pio2f.c",
- "musl/src/math/__rem_pio2l.c",
- "musl/src/math/__signbit.c",
- "musl/src/math/__signbitf.c",
- "musl/src/math/__signbitl.c",
- "musl/src/math/__sin.c",
- "musl/src/math/__sindf.c",
- "musl/src/math/__sinl.c",
- "musl/src/math/__tan.c",
- "musl/src/math/__tandf.c",
- "musl/src/math/__tanl.c",
- "musl/src/math/aarch64/ceil.c",
- "musl/src/math/aarch64/ceilf.c",
- "musl/src/math/aarch64/fabs.c",
- "musl/src/math/aarch64/fabsf.c",
- "musl/src/math/aarch64/floor.c",
- "musl/src/math/aarch64/floorf.c",
- "musl/src/math/aarch64/fma.c",
- "musl/src/math/aarch64/fmaf.c",
- "musl/src/math/aarch64/fmax.c",
- "musl/src/math/aarch64/fmaxf.c",
- "musl/src/math/aarch64/fmin.c",
- "musl/src/math/aarch64/fminf.c",
- "musl/src/math/aarch64/llrint.c",
- "musl/src/math/aarch64/llrintf.c",
- "musl/src/math/aarch64/llround.c",
- "musl/src/math/aarch64/llroundf.c",
- "musl/src/math/aarch64/lrint.c",
- "musl/src/math/aarch64/lrintf.c",
- "musl/src/math/aarch64/lround.c",
- "musl/src/math/aarch64/lroundf.c",
- "musl/src/math/aarch64/nearbyint.c",
- "musl/src/math/aarch64/nearbyintf.c",
- "musl/src/math/aarch64/rint.c",
- "musl/src/math/aarch64/rintf.c",
- "musl/src/math/aarch64/round.c",
- "musl/src/math/aarch64/roundf.c",
- "musl/src/math/aarch64/sqrt.c",
- "musl/src/math/aarch64/sqrtf.c",
- "musl/src/math/aarch64/trunc.c",
- "musl/src/math/aarch64/truncf.c",
- "musl/src/math/acos.c",
- "musl/src/math/acosf.c",
- "musl/src/math/acosh.c",
- "musl/src/math/acoshf.c",
- "musl/src/math/acoshl.c",
- "musl/src/math/acosl.c",
- "musl/src/math/arm/fabs.c",
- "musl/src/math/arm/fabsf.c",
- "musl/src/math/arm/fma.c",
- "musl/src/math/arm/fmaf.c",
- "musl/src/math/arm/sqrt.c",
- "musl/src/math/arm/sqrtf.c",
- "musl/src/math/asin.c",
- "musl/src/math/asinf.c",
- "musl/src/math/asinh.c",
- "musl/src/math/asinhf.c",
- "musl/src/math/asinhl.c",
- "musl/src/math/asinl.c",
- "musl/src/math/atan.c",
- "musl/src/math/atan2.c",
- "musl/src/math/atan2f.c",
- "musl/src/math/atan2l.c",
- "musl/src/math/atanf.c",
- "musl/src/math/atanh.c",
- "musl/src/math/atanhf.c",
- "musl/src/math/atanhl.c",
- "musl/src/math/atanl.c",
- "musl/src/math/cbrt.c",
- "musl/src/math/cbrtf.c",
- "musl/src/math/cbrtl.c",
- "musl/src/math/ceil.c",
- "musl/src/math/ceilf.c",
- "musl/src/math/ceill.c",
- "musl/src/math/copysign.c",
- "musl/src/math/copysignf.c",
- "musl/src/math/copysignl.c",
- "musl/src/math/cos.c",
- "musl/src/math/cosf.c",
- "musl/src/math/cosh.c",
- "musl/src/math/coshf.c",
- "musl/src/math/coshl.c",
- "musl/src/math/cosl.c",
- "musl/src/math/erf.c",
- "musl/src/math/erff.c",
- "musl/src/math/erfl.c",
- "musl/src/math/exp.c",
- "musl/src/math/exp10.c",
- "musl/src/math/exp10f.c",
- "musl/src/math/exp10l.c",
- "musl/src/math/exp2.c",
- "musl/src/math/exp2f.c",
- "musl/src/math/exp2f_data.c",
- "musl/src/math/exp2l.c",
- "musl/src/math/exp_data.c",
- "musl/src/math/expf.c",
- "musl/src/math/expl.c",
- "musl/src/math/expm1.c",
- "musl/src/math/expm1f.c",
- "musl/src/math/expm1l.c",
- "musl/src/math/fabs.c",
- "musl/src/math/fabsf.c",
- "musl/src/math/fabsl.c",
- "musl/src/math/fdim.c",
- "musl/src/math/fdimf.c",
- "musl/src/math/fdiml.c",
- "musl/src/math/finite.c",
- "musl/src/math/finitef.c",
- "musl/src/math/floor.c",
- "musl/src/math/floorf.c",
- "musl/src/math/floorl.c",
- "musl/src/math/fma.c",
- "musl/src/math/fmaf.c",
- "musl/src/math/fmal.c",
- "musl/src/math/fmax.c",
- "musl/src/math/fmaxf.c",
- "musl/src/math/fmaxl.c",
- "musl/src/math/fmin.c",
- "musl/src/math/fminf.c",
- "musl/src/math/fminl.c",
- "musl/src/math/fmod.c",
- "musl/src/math/fmodf.c",
- "musl/src/math/fmodl.c",
- "musl/src/math/frexp.c",
- "musl/src/math/frexpf.c",
- "musl/src/math/frexpl.c",
- "musl/src/math/hypot.c",
- "musl/src/math/hypotf.c",
- "musl/src/math/hypotl.c",
- "musl/src/math/i386/__invtrigl.s",
- "musl/src/math/i386/acos.s",
- "musl/src/math/i386/acosf.s",
- "musl/src/math/i386/acosl.s",
- "musl/src/math/i386/asin.s",
- "musl/src/math/i386/asinf.s",
- "musl/src/math/i386/asinl.s",
- "musl/src/math/i386/atan.s",
- "musl/src/math/i386/atan2.s",
- "musl/src/math/i386/atan2f.s",
- "musl/src/math/i386/atan2l.s",
- "musl/src/math/i386/atanf.s",
- "musl/src/math/i386/atanl.s",
- "musl/src/math/i386/ceil.s",
- "musl/src/math/i386/ceilf.s",
- "musl/src/math/i386/ceill.s",
- "musl/src/math/i386/exp2l.s",
- "musl/src/math/i386/exp_ld.s",
- "musl/src/math/i386/expl.s",
- "musl/src/math/i386/expm1l.s",
- "musl/src/math/i386/fabs.s",
- "musl/src/math/i386/fabsf.s",
- "musl/src/math/i386/fabsl.s",
- "musl/src/math/i386/floor.s",
- "musl/src/math/i386/floorf.s",
- "musl/src/math/i386/floorl.s",
- "musl/src/math/i386/fmod.s",
- "musl/src/math/i386/fmodf.s",
- "musl/src/math/i386/fmodl.s",
- "musl/src/math/i386/hypot.s",
- "musl/src/math/i386/hypotf.s",
- "musl/src/math/i386/ldexp.s",
- "musl/src/math/i386/ldexpf.s",
- "musl/src/math/i386/ldexpl.s",
- "musl/src/math/i386/llrint.s",
- "musl/src/math/i386/llrintf.s",
- "musl/src/math/i386/llrintl.s",
- "musl/src/math/i386/log.s",
- "musl/src/math/i386/log10.s",
- "musl/src/math/i386/log10f.s",
- "musl/src/math/i386/log10l.s",
- "musl/src/math/i386/log1p.s",
- "musl/src/math/i386/log1pf.s",
- "musl/src/math/i386/log1pl.s",
- "musl/src/math/i386/log2.s",
- "musl/src/math/i386/log2f.s",
- "musl/src/math/i386/log2l.s",
- "musl/src/math/i386/logf.s",
- "musl/src/math/i386/logl.s",
- "musl/src/math/i386/lrint.s",
- "musl/src/math/i386/lrintf.s",
- "musl/src/math/i386/lrintl.s",
- "musl/src/math/i386/remainder.s",
- "musl/src/math/i386/remainderf.s",
- "musl/src/math/i386/remainderl.s",
- "musl/src/math/i386/remquo.s",
- "musl/src/math/i386/remquof.s",
- "musl/src/math/i386/remquol.s",
- "musl/src/math/i386/rint.s",
- "musl/src/math/i386/rintf.s",
- "musl/src/math/i386/rintl.s",
- "musl/src/math/i386/scalbln.s",
- "musl/src/math/i386/scalblnf.s",
- "musl/src/math/i386/scalblnl.s",
- "musl/src/math/i386/scalbn.s",
- "musl/src/math/i386/scalbnf.s",
- "musl/src/math/i386/scalbnl.s",
- "musl/src/math/i386/sqrt.s",
- "musl/src/math/i386/sqrtf.s",
- "musl/src/math/i386/sqrtl.s",
- "musl/src/math/i386/trunc.s",
- "musl/src/math/i386/truncf.s",
- "musl/src/math/i386/truncl.s",
- "musl/src/math/ilogb.c",
- "musl/src/math/ilogbf.c",
- "musl/src/math/ilogbl.c",
- "musl/src/math/j0.c",
- "musl/src/math/j0f.c",
- "musl/src/math/j1.c",
- "musl/src/math/j1f.c",
- "musl/src/math/jn.c",
- "musl/src/math/jnf.c",
- "musl/src/math/ldexp.c",
- "musl/src/math/ldexpf.c",
- "musl/src/math/ldexpl.c",
- "musl/src/math/lgamma.c",
- "musl/src/math/lgamma_r.c",
- "musl/src/math/lgammaf.c",
- "musl/src/math/lgammaf_r.c",
- "musl/src/math/lgammal.c",
- "musl/src/math/llrint.c",
- "musl/src/math/llrintf.c",
- "musl/src/math/llrintl.c",
- "musl/src/math/llround.c",
- "musl/src/math/llroundf.c",
- "musl/src/math/llroundl.c",
- "musl/src/math/log.c",
- "musl/src/math/log10.c",
- "musl/src/math/log10f.c",
- "musl/src/math/log10l.c",
- "musl/src/math/log1p.c",
- "musl/src/math/log1pf.c",
- "musl/src/math/log1pl.c",
- "musl/src/math/log2.c",
- "musl/src/math/log2_data.c",
- "musl/src/math/log2f.c",
- "musl/src/math/log2f_data.c",
- "musl/src/math/log2l.c",
- "musl/src/math/log_data.c",
- "musl/src/math/logb.c",
- "musl/src/math/logbf.c",
- "musl/src/math/logbl.c",
- "musl/src/math/logf.c",
- "musl/src/math/logf_data.c",
- "musl/src/math/logl.c",
- "musl/src/math/lrint.c",
- "musl/src/math/lrintf.c",
- "musl/src/math/lrintl.c",
- "musl/src/math/lround.c",
- "musl/src/math/lroundf.c",
- "musl/src/math/lroundl.c",
- "musl/src/math/mips/fabs.c",
- "musl/src/math/mips/fabsf.c",
- "musl/src/math/mips/sqrt.c",
- "musl/src/math/mips/sqrtf.c",
- "musl/src/math/modf.c",
- "musl/src/math/modff.c",
- "musl/src/math/modfl.c",
- "musl/src/math/nan.c",
- "musl/src/math/nanf.c",
- "musl/src/math/nanl.c",
- "musl/src/math/nearbyint.c",
- "musl/src/math/nearbyintf.c",
- "musl/src/math/nearbyintl.c",
- "musl/src/math/nextafter.c",
- "musl/src/math/nextafterf.c",
- "musl/src/math/nextafterl.c",
- "musl/src/math/nexttoward.c",
- "musl/src/math/nexttowardf.c",
- "musl/src/math/nexttowardl.c",
- "musl/src/math/pow.c",
- "musl/src/math/pow_data.c",
- "musl/src/math/powerpc/fabs.c",
- "musl/src/math/powerpc/fabsf.c",
- "musl/src/math/powerpc/fma.c",
- "musl/src/math/powerpc/fmaf.c",
- "musl/src/math/powerpc/sqrt.c",
- "musl/src/math/powerpc/sqrtf.c",
- "musl/src/math/powerpc64/ceil.c",
- "musl/src/math/powerpc64/ceilf.c",
- "musl/src/math/powerpc64/fabs.c",
- "musl/src/math/powerpc64/fabsf.c",
- "musl/src/math/powerpc64/floor.c",
- "musl/src/math/powerpc64/floorf.c",
- "musl/src/math/powerpc64/fma.c",
- "musl/src/math/powerpc64/fmaf.c",
- "musl/src/math/powerpc64/fmax.c",
- "musl/src/math/powerpc64/fmaxf.c",
- "musl/src/math/powerpc64/fmin.c",
- "musl/src/math/powerpc64/fminf.c",
- "musl/src/math/powerpc64/lrint.c",
- "musl/src/math/powerpc64/lrintf.c",
- "musl/src/math/powerpc64/lround.c",
- "musl/src/math/powerpc64/lroundf.c",
- "musl/src/math/powerpc64/round.c",
- "musl/src/math/powerpc64/roundf.c",
- "musl/src/math/powerpc64/sqrt.c",
- "musl/src/math/powerpc64/sqrtf.c",
- "musl/src/math/powerpc64/trunc.c",
- "musl/src/math/powerpc64/truncf.c",
- "musl/src/math/powf.c",
- "musl/src/math/powf_data.c",
- "musl/src/math/powl.c",
- "musl/src/math/remainder.c",
- "musl/src/math/remainderf.c",
- "musl/src/math/remainderl.c",
- "musl/src/math/remquo.c",
- "musl/src/math/remquof.c",
- "musl/src/math/remquol.c",
- "musl/src/math/rint.c",
- "musl/src/math/rintf.c",
- "musl/src/math/rintl.c",
- "musl/src/math/riscv64/copysign.c",
- "musl/src/math/riscv64/copysignf.c",
- "musl/src/math/riscv64/fabs.c",
- "musl/src/math/riscv64/fabsf.c",
- "musl/src/math/riscv64/fma.c",
- "musl/src/math/riscv64/fmaf.c",
- "musl/src/math/riscv64/fmax.c",
- "musl/src/math/riscv64/fmaxf.c",
- "musl/src/math/riscv64/fmin.c",
- "musl/src/math/riscv64/fminf.c",
- "musl/src/math/riscv64/sqrt.c",
- "musl/src/math/riscv64/sqrtf.c",
- "musl/src/math/round.c",
- "musl/src/math/roundf.c",
- "musl/src/math/roundl.c",
- "musl/src/math/s390x/ceil.c",
- "musl/src/math/s390x/ceilf.c",
- "musl/src/math/s390x/ceill.c",
- "musl/src/math/s390x/fabs.c",
- "musl/src/math/s390x/fabsf.c",
- "musl/src/math/s390x/fabsl.c",
- "musl/src/math/s390x/floor.c",
- "musl/src/math/s390x/floorf.c",
- "musl/src/math/s390x/floorl.c",
- "musl/src/math/s390x/fma.c",
- "musl/src/math/s390x/fmaf.c",
- "musl/src/math/s390x/nearbyint.c",
- "musl/src/math/s390x/nearbyintf.c",
- "musl/src/math/s390x/nearbyintl.c",
- "musl/src/math/s390x/rint.c",
- "musl/src/math/s390x/rintf.c",
- "musl/src/math/s390x/rintl.c",
- "musl/src/math/s390x/round.c",
- "musl/src/math/s390x/roundf.c",
- "musl/src/math/s390x/roundl.c",
- "musl/src/math/s390x/sqrt.c",
- "musl/src/math/s390x/sqrtf.c",
- "musl/src/math/s390x/sqrtl.c",
- "musl/src/math/s390x/trunc.c",
- "musl/src/math/s390x/truncf.c",
- "musl/src/math/s390x/truncl.c",
- "musl/src/math/scalb.c",
- "musl/src/math/scalbf.c",
- "musl/src/math/scalbln.c",
- "musl/src/math/scalblnf.c",
- "musl/src/math/scalblnl.c",
- "musl/src/math/scalbn.c",
- "musl/src/math/scalbnf.c",
- "musl/src/math/scalbnl.c",
- "musl/src/math/signgam.c",
- "musl/src/math/significand.c",
- "musl/src/math/significandf.c",
- "musl/src/math/sin.c",
- "musl/src/math/sincos.c",
- "musl/src/math/sincosf.c",
- "musl/src/math/sincosl.c",
- "musl/src/math/sinf.c",
- "musl/src/math/sinh.c",
- "musl/src/math/sinhf.c",
- "musl/src/math/sinhl.c",
- "musl/src/math/sinl.c",
- "musl/src/math/sqrt.c",
- "musl/src/math/sqrtf.c",
- "musl/src/math/sqrtl.c",
- "musl/src/math/tan.c",
- "musl/src/math/tanf.c",
- "musl/src/math/tanh.c",
- "musl/src/math/tanhf.c",
- "musl/src/math/tanhl.c",
- "musl/src/math/tanl.c",
- "musl/src/math/tgamma.c",
- "musl/src/math/tgammaf.c",
- "musl/src/math/tgammal.c",
- "musl/src/math/trunc.c",
- "musl/src/math/truncf.c",
- "musl/src/math/truncl.c",
- "musl/src/math/x32/__invtrigl.s",
- "musl/src/math/x32/acosl.s",
- "musl/src/math/x32/asinl.s",
- "musl/src/math/x32/atan2l.s",
- "musl/src/math/x32/atanl.s",
- "musl/src/math/x32/ceill.s",
- "musl/src/math/x32/exp2l.s",
- "musl/src/math/x32/expl.s",
- "musl/src/math/x32/expm1l.s",
- "musl/src/math/x32/fabs.s",
- "musl/src/math/x32/fabsf.s",
- "musl/src/math/x32/fabsl.s",
- "musl/src/math/x32/floorl.s",
- "musl/src/math/x32/fma.c",
- "musl/src/math/x32/fmaf.c",
- "musl/src/math/x32/fmodl.s",
- "musl/src/math/x32/llrint.s",
- "musl/src/math/x32/llrintf.s",
- "musl/src/math/x32/llrintl.s",
- "musl/src/math/x32/log10l.s",
- "musl/src/math/x32/log1pl.s",
- "musl/src/math/x32/log2l.s",
- "musl/src/math/x32/logl.s",
- "musl/src/math/x32/lrint.s",
- "musl/src/math/x32/lrintf.s",
- "musl/src/math/x32/lrintl.s",
- "musl/src/math/x32/remainderl.s",
- "musl/src/math/x32/rintl.s",
- "musl/src/math/x32/sqrt.s",
- "musl/src/math/x32/sqrtf.s",
- "musl/src/math/x32/sqrtl.s",
- "musl/src/math/x32/truncl.s",
- "musl/src/math/x86_64/__invtrigl.s",
- "musl/src/math/x86_64/acosl.s",
- "musl/src/math/x86_64/asinl.s",
- "musl/src/math/x86_64/atan2l.s",
- "musl/src/math/x86_64/atanl.s",
- "musl/src/math/x86_64/ceill.s",
- "musl/src/math/x86_64/exp2l.s",
- "musl/src/math/x86_64/expl.s",
- "musl/src/math/x86_64/expm1l.s",
- "musl/src/math/x86_64/fabs.s",
- "musl/src/math/x86_64/fabsf.s",
- "musl/src/math/x86_64/fabsl.s",
- "musl/src/math/x86_64/floorl.s",
- "musl/src/math/x86_64/fma.c",
- "musl/src/math/x86_64/fmaf.c",
- "musl/src/math/x86_64/fmodl.s",
- "musl/src/math/x86_64/llrint.s",
- "musl/src/math/x86_64/llrintf.s",
- "musl/src/math/x86_64/llrintl.s",
- "musl/src/math/x86_64/log10l.s",
- "musl/src/math/x86_64/log1pl.s",
- "musl/src/math/x86_64/log2l.s",
- "musl/src/math/x86_64/logl.s",
- "musl/src/math/x86_64/lrint.s",
- "musl/src/math/x86_64/lrintf.s",
- "musl/src/math/x86_64/lrintl.s",
- "musl/src/math/x86_64/remainderl.s",
- "musl/src/math/x86_64/rintl.s",
- "musl/src/math/x86_64/sqrt.s",
- "musl/src/math/x86_64/sqrtf.s",
- "musl/src/math/x86_64/sqrtl.s",
- "musl/src/math/x86_64/truncl.s",
- "musl/src/misc/a64l.c",
- "musl/src/misc/basename.c",
- "musl/src/misc/dirname.c",
- "musl/src/misc/ffs.c",
- "musl/src/misc/ffsl.c",
- "musl/src/misc/ffsll.c",
- "musl/src/misc/fmtmsg.c",
- "musl/src/misc/forkpty.c",
- "musl/src/misc/get_current_dir_name.c",
- "musl/src/misc/getauxval.c",
- "musl/src/misc/getdomainname.c",
- "musl/src/misc/getentropy.c",
- "musl/src/misc/gethostid.c",
- "musl/src/misc/getopt.c",
- "musl/src/misc/getopt_long.c",
- "musl/src/misc/getpriority.c",
- "musl/src/misc/getresgid.c",
- "musl/src/misc/getresuid.c",
- "musl/src/misc/getrlimit.c",
- "musl/src/misc/getrusage.c",
- "musl/src/misc/getsubopt.c",
- "musl/src/misc/initgroups.c",
- "musl/src/misc/ioctl.c",
- "musl/src/misc/issetugid.c",
- "musl/src/misc/lockf.c",
- "musl/src/misc/login_tty.c",
- "musl/src/misc/mntent.c",
- "musl/src/misc/nftw.c",
- "musl/src/misc/openpty.c",
- "musl/src/misc/ptsname.c",
- "musl/src/misc/pty.c",
- "musl/src/misc/realpath.c",
- "musl/src/misc/setdomainname.c",
- "musl/src/misc/setpriority.c",
- "musl/src/misc/setrlimit.c",
- "musl/src/misc/syscall.c",
- "musl/src/misc/syslog.c",
- "musl/src/misc/uname.c",
- "musl/src/misc/wordexp.c",
- "musl/src/mman/madvise.c",
- "musl/src/mman/mincore.c",
- "musl/src/mman/mlock.c",
- "musl/src/mman/mlockall.c",
- "musl/src/mman/mmap.c",
- "musl/src/mman/mprotect.c",
- "musl/src/mman/mremap.c",
- "musl/src/mman/msync.c",
- "musl/src/mman/munlock.c",
- "musl/src/mman/munlockall.c",
- "musl/src/mman/munmap.c",
- "musl/src/mman/posix_madvise.c",
- "musl/src/mman/shm_open.c",
- "musl/src/mq/mq_close.c",
- "musl/src/mq/mq_getattr.c",
- "musl/src/mq/mq_notify.c",
- "musl/src/mq/mq_open.c",
- "musl/src/mq/mq_receive.c",
- "musl/src/mq/mq_send.c",
- "musl/src/mq/mq_setattr.c",
- "musl/src/mq/mq_timedreceive.c",
- "musl/src/mq/mq_timedsend.c",
- "musl/src/mq/mq_unlink.c",
- "musl/src/multibyte/btowc.c",
- "musl/src/multibyte/c16rtomb.c",
- "musl/src/multibyte/c32rtomb.c",
- "musl/src/multibyte/internal.c",
- "musl/src/multibyte/mblen.c",
- "musl/src/multibyte/mbrlen.c",
- "musl/src/multibyte/mbrtoc16.c",
- "musl/src/multibyte/mbrtoc32.c",
- "musl/src/multibyte/mbrtowc.c",
- "musl/src/multibyte/mbsinit.c",
- "musl/src/multibyte/mbsnrtowcs.c",
- "musl/src/multibyte/mbsrtowcs.c",
- "musl/src/multibyte/mbstowcs.c",
- "musl/src/multibyte/mbtowc.c",
- "musl/src/multibyte/wcrtomb.c",
- "musl/src/multibyte/wcsnrtombs.c",
- "musl/src/multibyte/wcsrtombs.c",
- "musl/src/multibyte/wcstombs.c",
- "musl/src/multibyte/wctob.c",
- "musl/src/multibyte/wctomb.c",
- "musl/src/network/accept.c",
- "musl/src/network/accept4.c",
- "musl/src/network/bind.c",
- "musl/src/network/connect.c",
- "musl/src/network/dn_comp.c",
- "musl/src/network/dn_expand.c",
- "musl/src/network/dn_skipname.c",
- "musl/src/network/dns_parse.c",
- "musl/src/network/ent.c",
- "musl/src/network/ether.c",
- "musl/src/network/freeaddrinfo.c",
- "musl/src/network/gai_strerror.c",
- "musl/src/network/getaddrinfo.c",
- "musl/src/network/gethostbyaddr.c",
- "musl/src/network/gethostbyaddr_r.c",
- "musl/src/network/gethostbyname.c",
- "musl/src/network/gethostbyname2.c",
- "musl/src/network/gethostbyname2_r.c",
- "musl/src/network/gethostbyname_r.c",
- "musl/src/network/getifaddrs.c",
- "musl/src/network/getnameinfo.c",
- "musl/src/network/getpeername.c",
- "musl/src/network/getservbyname.c",
- "musl/src/network/getservbyname_r.c",
- "musl/src/network/getservbyport.c",
- "musl/src/network/getservbyport_r.c",
- "musl/src/network/getsockname.c",
- "musl/src/network/getsockopt.c",
- "musl/src/network/h_errno.c",
- "musl/src/network/herror.c",
- "musl/src/network/hstrerror.c",
- "musl/src/network/htonl.c",
- "musl/src/network/htons.c",
- "musl/src/network/if_freenameindex.c",
- "musl/src/network/if_indextoname.c",
- "musl/src/network/if_nameindex.c",
- "musl/src/network/if_nametoindex.c",
- "musl/src/network/in6addr_any.c",
- "musl/src/network/in6addr_loopback.c",
- "musl/src/network/inet_addr.c",
- "musl/src/network/inet_aton.c",
- "musl/src/network/inet_legacy.c",
- "musl/src/network/inet_ntoa.c",
- "musl/src/network/inet_ntop.c",
- "musl/src/network/inet_pton.c",
- "musl/src/network/listen.c",
- "musl/src/network/lookup_ipliteral.c",
- "musl/src/network/lookup_name.c",
- "musl/src/network/lookup_serv.c",
- "musl/src/network/netlink.c",
- "musl/src/network/netname.c",
- "musl/src/network/ns_parse.c",
- "musl/src/network/ntohl.c",
- "musl/src/network/ntohs.c",
- "musl/src/network/proto.c",
- "musl/src/network/recv.c",
- "musl/src/network/recvfrom.c",
- "musl/src/network/recvmmsg.c",
- "musl/src/network/recvmsg.c",
- "musl/src/network/res_init.c",
- "musl/src/network/res_mkquery.c",
- "musl/src/network/res_msend.c",
- "musl/src/network/res_query.c",
- "musl/src/network/res_querydomain.c",
- "musl/src/network/res_send.c",
- "musl/src/network/res_state.c",
- "musl/src/network/resolvconf.c",
- "musl/src/network/send.c",
- "musl/src/network/sendmmsg.c",
- "musl/src/network/sendmsg.c",
- "musl/src/network/sendto.c",
- "musl/src/network/serv.c",
- "musl/src/network/setsockopt.c",
- "musl/src/network/shutdown.c",
- "musl/src/network/sockatmark.c",
- "musl/src/network/socket.c",
- "musl/src/network/socketpair.c",
- "musl/src/passwd/fgetgrent.c",
- "musl/src/passwd/fgetpwent.c",
- "musl/src/passwd/fgetspent.c",
- "musl/src/passwd/getgr_a.c",
- "musl/src/passwd/getgr_r.c",
- "musl/src/passwd/getgrent.c",
- "musl/src/passwd/getgrent_a.c",
- "musl/src/passwd/getgrouplist.c",
- "musl/src/passwd/getpw_a.c",
- "musl/src/passwd/getpw_r.c",
- "musl/src/passwd/getpwent.c",
- "musl/src/passwd/getpwent_a.c",
- "musl/src/passwd/getspent.c",
- "musl/src/passwd/getspnam.c",
- "musl/src/passwd/getspnam_r.c",
- "musl/src/passwd/lckpwdf.c",
- "musl/src/passwd/nscd_query.c",
- "musl/src/passwd/putgrent.c",
- "musl/src/passwd/putpwent.c",
- "musl/src/passwd/putspent.c",
- "musl/src/prng/__rand48_step.c",
- "musl/src/prng/__seed48.c",
- "musl/src/prng/drand48.c",
- "musl/src/prng/lcong48.c",
- "musl/src/prng/lrand48.c",
- "musl/src/prng/mrand48.c",
- "musl/src/prng/rand.c",
- "musl/src/prng/rand_r.c",
- "musl/src/prng/random.c",
- "musl/src/prng/seed48.c",
- "musl/src/prng/srand48.c",
- "musl/src/process/arm/vfork.s",
- "musl/src/process/execl.c",
- "musl/src/process/execle.c",
- "musl/src/process/execlp.c",
- "musl/src/process/execv.c",
- "musl/src/process/execve.c",
- "musl/src/process/execvp.c",
- "musl/src/process/fexecve.c",
- "musl/src/process/fork.c",
- "musl/src/process/i386/vfork.s",
- "musl/src/process/posix_spawn.c",
- "musl/src/process/posix_spawn_file_actions_addchdir.c",
- "musl/src/process/posix_spawn_file_actions_addclose.c",
- "musl/src/process/posix_spawn_file_actions_adddup2.c",
- "musl/src/process/posix_spawn_file_actions_addfchdir.c",
- "musl/src/process/posix_spawn_file_actions_addopen.c",
- "musl/src/process/posix_spawn_file_actions_destroy.c",
- "musl/src/process/posix_spawn_file_actions_init.c",
- "musl/src/process/posix_spawnattr_destroy.c",
- "musl/src/process/posix_spawnattr_getflags.c",
- "musl/src/process/posix_spawnattr_getpgroup.c",
- "musl/src/process/posix_spawnattr_getsigdefault.c",
- "musl/src/process/posix_spawnattr_getsigmask.c",
- "musl/src/process/posix_spawnattr_init.c",
- "musl/src/process/posix_spawnattr_sched.c",
- "musl/src/process/posix_spawnattr_setflags.c",
- "musl/src/process/posix_spawnattr_setpgroup.c",
- "musl/src/process/posix_spawnattr_setsigdefault.c",
- "musl/src/process/posix_spawnattr_setsigmask.c",
- "musl/src/process/posix_spawnp.c",
- "musl/src/process/s390x/vfork.s",
- "musl/src/process/sh/vfork.s",
- "musl/src/process/system.c",
- "musl/src/process/vfork.c",
- "musl/src/process/wait.c",
- "musl/src/process/waitid.c",
- "musl/src/process/waitpid.c",
- "musl/src/process/x32/vfork.s",
- "musl/src/process/x86_64/vfork.s",
- "musl/src/regex/fnmatch.c",
- "musl/src/regex/glob.c",
- "musl/src/regex/regcomp.c",
- "musl/src/regex/regerror.c",
- "musl/src/regex/regexec.c",
- "musl/src/regex/tre-mem.c",
- "musl/src/sched/affinity.c",
- "musl/src/sched/sched_cpucount.c",
- "musl/src/sched/sched_get_priority_max.c",
- "musl/src/sched/sched_getcpu.c",
- "musl/src/sched/sched_getparam.c",
- "musl/src/sched/sched_getscheduler.c",
- "musl/src/sched/sched_rr_get_interval.c",
- "musl/src/sched/sched_setparam.c",
- "musl/src/sched/sched_setscheduler.c",
- "musl/src/sched/sched_yield.c",
- "musl/src/search/hsearch.c",
- "musl/src/search/insque.c",
- "musl/src/search/lsearch.c",
- "musl/src/search/tdelete.c",
- "musl/src/search/tdestroy.c",
- "musl/src/search/tfind.c",
- "musl/src/search/tsearch.c",
- "musl/src/search/twalk.c",
- "musl/src/select/poll.c",
- "musl/src/select/pselect.c",
- "musl/src/select/select.c",
- "musl/src/setjmp/aarch64/longjmp.s",
- "musl/src/setjmp/aarch64/setjmp.s",
- "musl/src/setjmp/arm/longjmp.S",
- "musl/src/setjmp/arm/setjmp.S",
- "musl/src/setjmp/i386/longjmp.s",
- "musl/src/setjmp/i386/setjmp.s",
- "musl/src/setjmp/longjmp.c",
- "musl/src/setjmp/m68k/longjmp.s",
- "musl/src/setjmp/m68k/setjmp.s",
- "musl/src/setjmp/microblaze/longjmp.s",
- "musl/src/setjmp/microblaze/setjmp.s",
- "musl/src/setjmp/mips/longjmp.S",
- "musl/src/setjmp/mips/setjmp.S",
- "musl/src/setjmp/mips64/longjmp.S",
- "musl/src/setjmp/mips64/setjmp.S",
- "musl/src/setjmp/mipsn32/longjmp.S",
- "musl/src/setjmp/mipsn32/setjmp.S",
- "musl/src/setjmp/or1k/longjmp.s",
- "musl/src/setjmp/or1k/setjmp.s",
- "musl/src/setjmp/powerpc/longjmp.S",
- "musl/src/setjmp/powerpc/setjmp.S",
- "musl/src/setjmp/powerpc64/longjmp.s",
- "musl/src/setjmp/powerpc64/setjmp.s",
- "musl/src/setjmp/riscv64/longjmp.S",
- "musl/src/setjmp/riscv64/setjmp.S",
- "musl/src/setjmp/s390x/longjmp.s",
- "musl/src/setjmp/s390x/setjmp.s",
- "musl/src/setjmp/setjmp.c",
- "musl/src/setjmp/sh/longjmp.S",
- "musl/src/setjmp/sh/setjmp.S",
- "musl/src/setjmp/x32/longjmp.s",
- "musl/src/setjmp/x32/setjmp.s",
- "musl/src/setjmp/x86_64/longjmp.s",
- "musl/src/setjmp/x86_64/setjmp.s",
- "musl/src/signal/aarch64/restore.s",
- "musl/src/signal/aarch64/sigsetjmp.s",
- "musl/src/signal/arm/restore.s",
- "musl/src/signal/arm/sigsetjmp.s",
- "musl/src/signal/block.c",
- "musl/src/signal/getitimer.c",
- "musl/src/signal/i386/restore.s",
- "musl/src/signal/i386/sigsetjmp.s",
- "musl/src/signal/kill.c",
- "musl/src/signal/killpg.c",
- "musl/src/signal/m68k/sigsetjmp.s",
- "musl/src/signal/microblaze/restore.s",
- "musl/src/signal/microblaze/sigsetjmp.s",
- "musl/src/signal/mips/restore.s",
- "musl/src/signal/mips/sigsetjmp.s",
- "musl/src/signal/mips64/restore.s",
- "musl/src/signal/mips64/sigsetjmp.s",
- "musl/src/signal/mipsn32/restore.s",
- "musl/src/signal/mipsn32/sigsetjmp.s",
- "musl/src/signal/or1k/sigsetjmp.s",
- "musl/src/signal/powerpc/restore.s",
- "musl/src/signal/powerpc/sigsetjmp.s",
- "musl/src/signal/powerpc64/restore.s",
- "musl/src/signal/powerpc64/sigsetjmp.s",
- "musl/src/signal/psiginfo.c",
- "musl/src/signal/psignal.c",
- "musl/src/signal/raise.c",
- "musl/src/signal/restore.c",
- "musl/src/signal/riscv64/restore.s",
- "musl/src/signal/riscv64/sigsetjmp.s",
- "musl/src/signal/s390x/restore.s",
- "musl/src/signal/s390x/sigsetjmp.s",
- "musl/src/signal/setitimer.c",
- "musl/src/signal/sh/restore.s",
- "musl/src/signal/sh/sigsetjmp.s",
- "musl/src/signal/sigaction.c",
- "musl/src/signal/sigaddset.c",
- "musl/src/signal/sigaltstack.c",
- "musl/src/signal/sigandset.c",
- "musl/src/signal/sigdelset.c",
- "musl/src/signal/sigemptyset.c",
- "musl/src/signal/sigfillset.c",
- "musl/src/signal/sighold.c",
- "musl/src/signal/sigignore.c",
- "musl/src/signal/siginterrupt.c",
- "musl/src/signal/sigisemptyset.c",
- "musl/src/signal/sigismember.c",
- "musl/src/signal/siglongjmp.c",
- "musl/src/signal/signal.c",
- "musl/src/signal/sigorset.c",
- "musl/src/signal/sigpause.c",
- "musl/src/signal/sigpending.c",
- "musl/src/signal/sigprocmask.c",
- "musl/src/signal/sigqueue.c",
- "musl/src/signal/sigrelse.c",
- "musl/src/signal/sigrtmax.c",
- "musl/src/signal/sigrtmin.c",
- "musl/src/signal/sigset.c",
- "musl/src/signal/sigsetjmp.c",
- "musl/src/signal/sigsetjmp_tail.c",
- "musl/src/signal/sigsuspend.c",
- "musl/src/signal/sigtimedwait.c",
- "musl/src/signal/sigwait.c",
- "musl/src/signal/sigwaitinfo.c",
- "musl/src/signal/x32/getitimer.c",
- "musl/src/signal/x32/restore.s",
- "musl/src/signal/x32/setitimer.c",
- "musl/src/signal/x32/sigsetjmp.s",
- "musl/src/signal/x86_64/restore.s",
- "musl/src/signal/x86_64/sigsetjmp.s",
- "musl/src/stat/__xstat.c",
- "musl/src/stat/chmod.c",
- "musl/src/stat/fchmod.c",
- "musl/src/stat/fchmodat.c",
- "musl/src/stat/fstat.c",
- "musl/src/stat/fstatat.c",
- "musl/src/stat/futimens.c",
- "musl/src/stat/futimesat.c",
- "musl/src/stat/lchmod.c",
- "musl/src/stat/lstat.c",
- "musl/src/stat/mkdir.c",
- "musl/src/stat/mkdirat.c",
- "musl/src/stat/mkfifo.c",
- "musl/src/stat/mkfifoat.c",
- "musl/src/stat/mknod.c",
- "musl/src/stat/mknodat.c",
- "musl/src/stat/stat.c",
- "musl/src/stat/statvfs.c",
- "musl/src/stat/umask.c",
- "musl/src/stat/utimensat.c",
- "musl/src/stdio/__fclose_ca.c",
- "musl/src/stdio/__fdopen.c",
- "musl/src/stdio/__fmodeflags.c",
- "musl/src/stdio/__fopen_rb_ca.c",
- "musl/src/stdio/__lockfile.c",
- "musl/src/stdio/__overflow.c",
- "musl/src/stdio/__stdio_close.c",
- "musl/src/stdio/__stdio_exit.c",
- "musl/src/stdio/__stdio_read.c",
- "musl/src/stdio/__stdio_seek.c",
- "musl/src/stdio/__stdio_write.c",
- "musl/src/stdio/__stdout_write.c",
- "musl/src/stdio/__string_read.c",
- "musl/src/stdio/__toread.c",
- "musl/src/stdio/__towrite.c",
- "musl/src/stdio/__uflow.c",
- "musl/src/stdio/asprintf.c",
- "musl/src/stdio/clearerr.c",
- "musl/src/stdio/dprintf.c",
- "musl/src/stdio/ext.c",
- "musl/src/stdio/ext2.c",
- "musl/src/stdio/fclose.c",
- "musl/src/stdio/feof.c",
- "musl/src/stdio/ferror.c",
- "musl/src/stdio/fflush.c",
- "musl/src/stdio/fgetc.c",
- "musl/src/stdio/fgetln.c",
- "musl/src/stdio/fgetpos.c",
- "musl/src/stdio/fgets.c",
- "musl/src/stdio/fgetwc.c",
- "musl/src/stdio/fgetws.c",
- "musl/src/stdio/fileno.c",
- "musl/src/stdio/flockfile.c",
- "musl/src/stdio/fmemopen.c",
- "musl/src/stdio/fopen.c",
- "musl/src/stdio/fopencookie.c",
- "musl/src/stdio/fprintf.c",
- "musl/src/stdio/fputc.c",
- "musl/src/stdio/fputs.c",
- "musl/src/stdio/fputwc.c",
- "musl/src/stdio/fputws.c",
- "musl/src/stdio/fread.c",
- "musl/src/stdio/freopen.c",
- "musl/src/stdio/fscanf.c",
- "musl/src/stdio/fseek.c",
- "musl/src/stdio/fsetpos.c",
- "musl/src/stdio/ftell.c",
- "musl/src/stdio/ftrylockfile.c",
- "musl/src/stdio/funlockfile.c",
- "musl/src/stdio/fwide.c",
- "musl/src/stdio/fwprintf.c",
- "musl/src/stdio/fwrite.c",
- "musl/src/stdio/fwscanf.c",
- "musl/src/stdio/getc.c",
- "musl/src/stdio/getc_unlocked.c",
- "musl/src/stdio/getchar.c",
- "musl/src/stdio/getchar_unlocked.c",
- "musl/src/stdio/getdelim.c",
- "musl/src/stdio/getline.c",
- "musl/src/stdio/gets.c",
- "musl/src/stdio/getw.c",
- "musl/src/stdio/getwc.c",
- "musl/src/stdio/getwchar.c",
- "musl/src/stdio/ofl.c",
- "musl/src/stdio/ofl_add.c",
- "musl/src/stdio/open_memstream.c",
- "musl/src/stdio/open_wmemstream.c",
- "musl/src/stdio/pclose.c",
- "musl/src/stdio/perror.c",
- "musl/src/stdio/popen.c",
- "musl/src/stdio/printf.c",
- "musl/src/stdio/putc.c",
- "musl/src/stdio/putc_unlocked.c",
- "musl/src/stdio/putchar.c",
- "musl/src/stdio/putchar_unlocked.c",
- "musl/src/stdio/puts.c",
- "musl/src/stdio/putw.c",
- "musl/src/stdio/putwc.c",
- "musl/src/stdio/putwchar.c",
- "musl/src/stdio/remove.c",
- "musl/src/stdio/rename.c",
- "musl/src/stdio/rewind.c",
- "musl/src/stdio/scanf.c",
- "musl/src/stdio/setbuf.c",
- "musl/src/stdio/setbuffer.c",
- "musl/src/stdio/setlinebuf.c",
- "musl/src/stdio/setvbuf.c",
- "musl/src/stdio/snprintf.c",
- "musl/src/stdio/sprintf.c",
- "musl/src/stdio/sscanf.c",
- "musl/src/stdio/stderr.c",
- "musl/src/stdio/stdin.c",
- "musl/src/stdio/stdout.c",
- "musl/src/stdio/swprintf.c",
- "musl/src/stdio/swscanf.c",
- "musl/src/stdio/tempnam.c",
- "musl/src/stdio/tmpfile.c",
- "musl/src/stdio/tmpnam.c",
- "musl/src/stdio/ungetc.c",
- "musl/src/stdio/ungetwc.c",
- "musl/src/stdio/vasprintf.c",
- "musl/src/stdio/vdprintf.c",
- "musl/src/stdio/vfprintf.c",
- "musl/src/stdio/vfscanf.c",
- "musl/src/stdio/vfwprintf.c",
- "musl/src/stdio/vfwscanf.c",
- "musl/src/stdio/vprintf.c",
- "musl/src/stdio/vscanf.c",
- "musl/src/stdio/vsnprintf.c",
- "musl/src/stdio/vsprintf.c",
- "musl/src/stdio/vsscanf.c",
- "musl/src/stdio/vswprintf.c",
- "musl/src/stdio/vswscanf.c",
- "musl/src/stdio/vwprintf.c",
- "musl/src/stdio/vwscanf.c",
- "musl/src/stdio/wprintf.c",
- "musl/src/stdio/wscanf.c",
- "musl/src/stdlib/abs.c",
- "musl/src/stdlib/atof.c",
- "musl/src/stdlib/atoi.c",
- "musl/src/stdlib/atol.c",
- "musl/src/stdlib/atoll.c",
- "musl/src/stdlib/bsearch.c",
- "musl/src/stdlib/div.c",
- "musl/src/stdlib/ecvt.c",
- "musl/src/stdlib/fcvt.c",
- "musl/src/stdlib/gcvt.c",
- "musl/src/stdlib/imaxabs.c",
- "musl/src/stdlib/imaxdiv.c",
- "musl/src/stdlib/labs.c",
- "musl/src/stdlib/ldiv.c",
- "musl/src/stdlib/llabs.c",
- "musl/src/stdlib/lldiv.c",
- "musl/src/stdlib/qsort.c",
- "musl/src/stdlib/strtod.c",
- "musl/src/stdlib/strtol.c",
- "musl/src/stdlib/wcstod.c",
- "musl/src/stdlib/wcstol.c",
- "musl/src/string/arm/__aeabi_memcpy.s",
- "musl/src/string/arm/__aeabi_memset.s",
- "musl/src/string/arm/memcpy.c",
- "musl/src/string/arm/memcpy_le.S",
- "musl/src/string/bcmp.c",
- "musl/src/string/bcopy.c",
- "musl/src/string/bzero.c",
- "musl/src/string/explicit_bzero.c",
- "musl/src/string/i386/memcpy.s",
- "musl/src/string/i386/memmove.s",
- "musl/src/string/i386/memset.s",
- "musl/src/string/index.c",
- "musl/src/string/memccpy.c",
- "musl/src/string/memchr.c",
- "musl/src/string/memcmp.c",
- "musl/src/string/memcpy.c",
- "musl/src/string/memmem.c",
- "musl/src/string/memmove.c",
- "musl/src/string/mempcpy.c",
- "musl/src/string/memrchr.c",
- "musl/src/string/memset.c",
- "musl/src/string/rindex.c",
- "musl/src/string/stpcpy.c",
- "musl/src/string/stpncpy.c",
- "musl/src/string/strcasecmp.c",
- "musl/src/string/strcasestr.c",
- "musl/src/string/strcat.c",
- "musl/src/string/strchr.c",
- "musl/src/string/strchrnul.c",
- "musl/src/string/strcmp.c",
- "musl/src/string/strcpy.c",
- "musl/src/string/strcspn.c",
- "musl/src/string/strdup.c",
- "musl/src/string/strerror_r.c",
- "musl/src/string/strlcat.c",
- "musl/src/string/strlcpy.c",
- "musl/src/string/strlen.c",
- "musl/src/string/strncasecmp.c",
- "musl/src/string/strncat.c",
- "musl/src/string/strncmp.c",
- "musl/src/string/strncpy.c",
- "musl/src/string/strndup.c",
- "musl/src/string/strnlen.c",
- "musl/src/string/strpbrk.c",
- "musl/src/string/strrchr.c",
- "musl/src/string/strsep.c",
- "musl/src/string/strsignal.c",
- "musl/src/string/strspn.c",
- "musl/src/string/strstr.c",
- "musl/src/string/strtok.c",
- "musl/src/string/strtok_r.c",
- "musl/src/string/strverscmp.c",
- "musl/src/string/swab.c",
- "musl/src/string/wcpcpy.c",
- "musl/src/string/wcpncpy.c",
- "musl/src/string/wcscasecmp.c",
- "musl/src/string/wcscasecmp_l.c",
- "musl/src/string/wcscat.c",
- "musl/src/string/wcschr.c",
- "musl/src/string/wcscmp.c",
- "musl/src/string/wcscpy.c",
- "musl/src/string/wcscspn.c",
- "musl/src/string/wcsdup.c",
- "musl/src/string/wcslen.c",
- "musl/src/string/wcsncasecmp.c",
- "musl/src/string/wcsncasecmp_l.c",
- "musl/src/string/wcsncat.c",
- "musl/src/string/wcsncmp.c",
- "musl/src/string/wcsncpy.c",
- "musl/src/string/wcsnlen.c",
- "musl/src/string/wcspbrk.c",
- "musl/src/string/wcsrchr.c",
- "musl/src/string/wcsspn.c",
- "musl/src/string/wcsstr.c",
- "musl/src/string/wcstok.c",
- "musl/src/string/wcswcs.c",
- "musl/src/string/wmemchr.c",
- "musl/src/string/wmemcmp.c",
- "musl/src/string/wmemcpy.c",
- "musl/src/string/wmemmove.c",
- "musl/src/string/wmemset.c",
- "musl/src/string/x86_64/memcpy.s",
- "musl/src/string/x86_64/memmove.s",
- "musl/src/string/x86_64/memset.s",
- "musl/src/temp/__randname.c",
- "musl/src/temp/mkdtemp.c",
- "musl/src/temp/mkostemp.c",
- "musl/src/temp/mkostemps.c",
- "musl/src/temp/mkstemp.c",
- "musl/src/temp/mkstemps.c",
- "musl/src/temp/mktemp.c",
- "musl/src/termios/cfgetospeed.c",
- "musl/src/termios/cfmakeraw.c",
- "musl/src/termios/cfsetospeed.c",
- "musl/src/termios/tcdrain.c",
- "musl/src/termios/tcflow.c",
- "musl/src/termios/tcflush.c",
- "musl/src/termios/tcgetattr.c",
- "musl/src/termios/tcgetsid.c",
- "musl/src/termios/tcsendbreak.c",
- "musl/src/termios/tcsetattr.c",
- "musl/src/thread/__lock.c",
- "musl/src/thread/__set_thread_area.c",
- "musl/src/thread/__syscall_cp.c",
- "musl/src/thread/__timedwait.c",
- "musl/src/thread/__tls_get_addr.c",
- "musl/src/thread/__unmapself.c",
- "musl/src/thread/__wait.c",
- "musl/src/thread/aarch64/__set_thread_area.s",
- "musl/src/thread/aarch64/__unmapself.s",
- "musl/src/thread/aarch64/clone.s",
- "musl/src/thread/aarch64/syscall_cp.s",
- "musl/src/thread/arm/__aeabi_read_tp.s",
- "musl/src/thread/arm/__set_thread_area.c",
- "musl/src/thread/arm/__unmapself.s",
- "musl/src/thread/arm/atomics.s",
- "musl/src/thread/arm/clone.s",
- "musl/src/thread/arm/syscall_cp.s",
- "musl/src/thread/call_once.c",
- "musl/src/thread/clone.c",
- "musl/src/thread/cnd_broadcast.c",
- "musl/src/thread/cnd_destroy.c",
- "musl/src/thread/cnd_init.c",
- "musl/src/thread/cnd_signal.c",
- "musl/src/thread/cnd_timedwait.c",
- "musl/src/thread/cnd_wait.c",
- "musl/src/thread/default_attr.c",
- "musl/src/thread/i386/__set_thread_area.s",
- "musl/src/thread/i386/__unmapself.s",
- "musl/src/thread/i386/clone.s",
- "musl/src/thread/i386/syscall_cp.s",
- "musl/src/thread/i386/tls.s",
- "musl/src/thread/lock_ptc.c",
- "musl/src/thread/m68k/__m68k_read_tp.s",
- "musl/src/thread/m68k/clone.s",
- "musl/src/thread/m68k/syscall_cp.s",
- "musl/src/thread/microblaze/__set_thread_area.s",
- "musl/src/thread/microblaze/__unmapself.s",
- "musl/src/thread/microblaze/clone.s",
- "musl/src/thread/microblaze/syscall_cp.s",
- "musl/src/thread/mips/__unmapself.s",
- "musl/src/thread/mips/clone.s",
- "musl/src/thread/mips/syscall_cp.s",
- "musl/src/thread/mips64/__unmapself.s",
- "musl/src/thread/mips64/clone.s",
- "musl/src/thread/mips64/syscall_cp.s",
- "musl/src/thread/mipsn32/__unmapself.s",
- "musl/src/thread/mipsn32/clone.s",
- "musl/src/thread/mipsn32/syscall_cp.s",
- "musl/src/thread/mtx_destroy.c",
- "musl/src/thread/mtx_init.c",
- "musl/src/thread/mtx_lock.c",
- "musl/src/thread/mtx_timedlock.c",
- "musl/src/thread/mtx_trylock.c",
- "musl/src/thread/mtx_unlock.c",
- "musl/src/thread/or1k/__set_thread_area.s",
- "musl/src/thread/or1k/__unmapself.s",
- "musl/src/thread/or1k/clone.s",
- "musl/src/thread/or1k/syscall_cp.s",
- "musl/src/thread/powerpc/__set_thread_area.s",
- "musl/src/thread/powerpc/__unmapself.s",
- "musl/src/thread/powerpc/clone.s",
- "musl/src/thread/powerpc/syscall_cp.s",
- "musl/src/thread/powerpc64/__set_thread_area.s",
- "musl/src/thread/powerpc64/__unmapself.s",
- "musl/src/thread/powerpc64/clone.s",
- "musl/src/thread/powerpc64/syscall_cp.s",
- "musl/src/thread/pthread_atfork.c",
- "musl/src/thread/pthread_attr_destroy.c",
- "musl/src/thread/pthread_attr_get.c",
- "musl/src/thread/pthread_attr_init.c",
- "musl/src/thread/pthread_attr_setdetachstate.c",
- "musl/src/thread/pthread_attr_setguardsize.c",
- "musl/src/thread/pthread_attr_setinheritsched.c",
- "musl/src/thread/pthread_attr_setschedparam.c",
- "musl/src/thread/pthread_attr_setschedpolicy.c",
- "musl/src/thread/pthread_attr_setscope.c",
- "musl/src/thread/pthread_attr_setstack.c",
- "musl/src/thread/pthread_attr_setstacksize.c",
- "musl/src/thread/pthread_barrier_destroy.c",
- "musl/src/thread/pthread_barrier_init.c",
- "musl/src/thread/pthread_barrier_wait.c",
- "musl/src/thread/pthread_barrierattr_destroy.c",
- "musl/src/thread/pthread_barrierattr_init.c",
- "musl/src/thread/pthread_barrierattr_setpshared.c",
- "musl/src/thread/pthread_cancel.c",
- "musl/src/thread/pthread_cleanup_push.c",
- "musl/src/thread/pthread_cond_broadcast.c",
- "musl/src/thread/pthread_cond_destroy.c",
- "musl/src/thread/pthread_cond_init.c",
- "musl/src/thread/pthread_cond_signal.c",
- "musl/src/thread/pthread_cond_timedwait.c",
- "musl/src/thread/pthread_cond_wait.c",
- "musl/src/thread/pthread_condattr_destroy.c",
- "musl/src/thread/pthread_condattr_init.c",
- "musl/src/thread/pthread_condattr_setclock.c",
- "musl/src/thread/pthread_condattr_setpshared.c",
- "musl/src/thread/pthread_create.c",
- "musl/src/thread/pthread_detach.c",
- "musl/src/thread/pthread_equal.c",
- "musl/src/thread/pthread_getattr_np.c",
- "musl/src/thread/pthread_getconcurrency.c",
- "musl/src/thread/pthread_getcpuclockid.c",
- "musl/src/thread/pthread_getschedparam.c",
- "musl/src/thread/pthread_getspecific.c",
- "musl/src/thread/pthread_join.c",
- "musl/src/thread/pthread_key_create.c",
- "musl/src/thread/pthread_kill.c",
- "musl/src/thread/pthread_mutex_consistent.c",
- "musl/src/thread/pthread_mutex_destroy.c",
- "musl/src/thread/pthread_mutex_getprioceiling.c",
- "musl/src/thread/pthread_mutex_init.c",
- "musl/src/thread/pthread_mutex_lock.c",
- "musl/src/thread/pthread_mutex_setprioceiling.c",
- "musl/src/thread/pthread_mutex_timedlock.c",
- "musl/src/thread/pthread_mutex_trylock.c",
- "musl/src/thread/pthread_mutex_unlock.c",
- "musl/src/thread/pthread_mutexattr_destroy.c",
- "musl/src/thread/pthread_mutexattr_init.c",
- "musl/src/thread/pthread_mutexattr_setprotocol.c",
- "musl/src/thread/pthread_mutexattr_setpshared.c",
- "musl/src/thread/pthread_mutexattr_setrobust.c",
- "musl/src/thread/pthread_mutexattr_settype.c",
- "musl/src/thread/pthread_once.c",
- "musl/src/thread/pthread_rwlock_destroy.c",
- "musl/src/thread/pthread_rwlock_init.c",
- "musl/src/thread/pthread_rwlock_rdlock.c",
- "musl/src/thread/pthread_rwlock_timedrdlock.c",
- "musl/src/thread/pthread_rwlock_timedwrlock.c",
- "musl/src/thread/pthread_rwlock_tryrdlock.c",
- "musl/src/thread/pthread_rwlock_trywrlock.c",
- "musl/src/thread/pthread_rwlock_unlock.c",
- "musl/src/thread/pthread_rwlock_wrlock.c",
- "musl/src/thread/pthread_rwlockattr_destroy.c",
- "musl/src/thread/pthread_rwlockattr_init.c",
- "musl/src/thread/pthread_rwlockattr_setpshared.c",
- "musl/src/thread/pthread_self.c",
- "musl/src/thread/pthread_setattr_default_np.c",
- "musl/src/thread/pthread_setcancelstate.c",
- "musl/src/thread/pthread_setcanceltype.c",
- "musl/src/thread/pthread_setconcurrency.c",
- "musl/src/thread/pthread_setname_np.c",
- "musl/src/thread/pthread_setschedparam.c",
- "musl/src/thread/pthread_setschedprio.c",
- "musl/src/thread/pthread_setspecific.c",
- "musl/src/thread/pthread_sigmask.c",
- "musl/src/thread/pthread_spin_destroy.c",
- "musl/src/thread/pthread_spin_init.c",
- "musl/src/thread/pthread_spin_lock.c",
- "musl/src/thread/pthread_spin_trylock.c",
- "musl/src/thread/pthread_spin_unlock.c",
- "musl/src/thread/pthread_testcancel.c",
- "musl/src/thread/riscv64/__set_thread_area.s",
- "musl/src/thread/riscv64/__unmapself.s",
- "musl/src/thread/riscv64/clone.s",
- "musl/src/thread/riscv64/syscall_cp.s",
- "musl/src/thread/s390x/__set_thread_area.s",
- "musl/src/thread/s390x/__tls_get_offset.s",
- "musl/src/thread/s390x/__unmapself.s",
- "musl/src/thread/s390x/clone.s",
- "musl/src/thread/s390x/syscall_cp.s",
- "musl/src/thread/sem_destroy.c",
- "musl/src/thread/sem_getvalue.c",
- "musl/src/thread/sem_init.c",
- "musl/src/thread/sem_open.c",
- "musl/src/thread/sem_post.c",
- "musl/src/thread/sem_timedwait.c",
- "musl/src/thread/sem_trywait.c",
- "musl/src/thread/sem_unlink.c",
- "musl/src/thread/sem_wait.c",
- "musl/src/thread/sh/__set_thread_area.c",
- "musl/src/thread/sh/__unmapself.c",
- "musl/src/thread/sh/__unmapself_mmu.s",
- "musl/src/thread/sh/atomics.s",
- "musl/src/thread/sh/clone.s",
- "musl/src/thread/sh/syscall_cp.s",
- "musl/src/thread/synccall.c",
- "musl/src/thread/syscall_cp.c",
- "musl/src/thread/thrd_create.c",
- "musl/src/thread/thrd_exit.c",
- "musl/src/thread/thrd_join.c",
- "musl/src/thread/thrd_sleep.c",
- "musl/src/thread/thrd_yield.c",
- "musl/src/thread/tls.c",
- "musl/src/thread/tss_create.c",
- "musl/src/thread/tss_delete.c",
- "musl/src/thread/tss_set.c",
- "musl/src/thread/vmlock.c",
- "musl/src/thread/x32/__set_thread_area.s",
- "musl/src/thread/x32/__unmapself.s",
- "musl/src/thread/x32/clone.s",
- "musl/src/thread/x32/syscall_cp.s",
- "musl/src/thread/x86_64/__set_thread_area.s",
- "musl/src/thread/x86_64/__unmapself.s",
- "musl/src/thread/x86_64/clone.s",
- "musl/src/thread/x86_64/syscall_cp.s",
- "musl/src/time/__map_file.c",
- "musl/src/time/__month_to_secs.c",
- "musl/src/time/__secs_to_tm.c",
- "musl/src/time/__tm_to_secs.c",
- "musl/src/time/__tz.c",
- "musl/src/time/__year_to_secs.c",
- "musl/src/time/asctime.c",
- "musl/src/time/asctime_r.c",
- "musl/src/time/clock.c",
- "musl/src/time/clock_getcpuclockid.c",
- "musl/src/time/clock_getres.c",
- "musl/src/time/clock_gettime.c",
- "musl/src/time/clock_nanosleep.c",
- "musl/src/time/clock_settime.c",
- "musl/src/time/ctime.c",
- "musl/src/time/ctime_r.c",
- "musl/src/time/difftime.c",
- "musl/src/time/ftime.c",
- "musl/src/time/getdate.c",
- "musl/src/time/gettimeofday.c",
- "musl/src/time/gmtime.c",
- "musl/src/time/gmtime_r.c",
- "musl/src/time/localtime.c",
- "musl/src/time/localtime_r.c",
- "musl/src/time/mktime.c",
- "musl/src/time/nanosleep.c",
- "musl/src/time/strftime.c",
- "musl/src/time/strptime.c",
- "musl/src/time/time.c",
- "musl/src/time/timegm.c",
- "musl/src/time/timer_create.c",
- "musl/src/time/timer_delete.c",
- "musl/src/time/timer_getoverrun.c",
- "musl/src/time/timer_gettime.c",
- "musl/src/time/timer_settime.c",
- "musl/src/time/times.c",
- "musl/src/time/timespec_get.c",
- "musl/src/time/utime.c",
- "musl/src/time/wcsftime.c",
- "musl/src/unistd/_exit.c",
- "musl/src/unistd/access.c",
- "musl/src/unistd/acct.c",
- "musl/src/unistd/alarm.c",
- "musl/src/unistd/chdir.c",
- "musl/src/unistd/chown.c",
- "musl/src/unistd/close.c",
- "musl/src/unistd/ctermid.c",
- "musl/src/unistd/dup.c",
- "musl/src/unistd/dup2.c",
- "musl/src/unistd/dup3.c",
- "musl/src/unistd/faccessat.c",
- "musl/src/unistd/fchdir.c",
- "musl/src/unistd/fchown.c",
- "musl/src/unistd/fchownat.c",
- "musl/src/unistd/fdatasync.c",
- "musl/src/unistd/fsync.c",
- "musl/src/unistd/ftruncate.c",
- "musl/src/unistd/getcwd.c",
- "musl/src/unistd/getegid.c",
- "musl/src/unistd/geteuid.c",
- "musl/src/unistd/getgid.c",
- "musl/src/unistd/getgroups.c",
- "musl/src/unistd/gethostname.c",
- "musl/src/unistd/getlogin.c",
- "musl/src/unistd/getlogin_r.c",
- "musl/src/unistd/getpgid.c",
- "musl/src/unistd/getpgrp.c",
- "musl/src/unistd/getpid.c",
- "musl/src/unistd/getppid.c",
- "musl/src/unistd/getsid.c",
- "musl/src/unistd/getuid.c",
- "musl/src/unistd/isatty.c",
- "musl/src/unistd/lchown.c",
- "musl/src/unistd/link.c",
- "musl/src/unistd/linkat.c",
- "musl/src/unistd/lseek.c",
- "musl/src/unistd/mips/pipe.s",
- "musl/src/unistd/mips64/pipe.s",
- "musl/src/unistd/mipsn32/lseek.c",
- "musl/src/unistd/mipsn32/pipe.s",
- "musl/src/unistd/nice.c",
- "musl/src/unistd/pause.c",
- "musl/src/unistd/pipe.c",
- "musl/src/unistd/pipe2.c",
- "musl/src/unistd/posix_close.c",
- "musl/src/unistd/pread.c",
- "musl/src/unistd/preadv.c",
- "musl/src/unistd/pwrite.c",
- "musl/src/unistd/pwritev.c",
- "musl/src/unistd/read.c",
- "musl/src/unistd/readlink.c",
- "musl/src/unistd/readlinkat.c",
- "musl/src/unistd/readv.c",
- "musl/src/unistd/renameat.c",
- "musl/src/unistd/rmdir.c",
- "musl/src/unistd/setegid.c",
- "musl/src/unistd/seteuid.c",
- "musl/src/unistd/setgid.c",
- "musl/src/unistd/setpgid.c",
- "musl/src/unistd/setpgrp.c",
- "musl/src/unistd/setregid.c",
- "musl/src/unistd/setresgid.c",
- "musl/src/unistd/setresuid.c",
- "musl/src/unistd/setreuid.c",
- "musl/src/unistd/setsid.c",
- "musl/src/unistd/setuid.c",
- "musl/src/unistd/setxid.c",
- "musl/src/unistd/sh/pipe.s",
- "musl/src/unistd/sleep.c",
- "musl/src/unistd/symlink.c",
- "musl/src/unistd/symlinkat.c",
- "musl/src/unistd/sync.c",
- "musl/src/unistd/tcgetpgrp.c",
- "musl/src/unistd/tcsetpgrp.c",
- "musl/src/unistd/truncate.c",
- "musl/src/unistd/ttyname.c",
- "musl/src/unistd/ttyname_r.c",
- "musl/src/unistd/ualarm.c",
- "musl/src/unistd/unlink.c",
- "musl/src/unistd/unlinkat.c",
- "musl/src/unistd/usleep.c",
- "musl/src/unistd/write.c",
- "musl/src/unistd/writev.c",
- "musl/src/unistd/x32/lseek.c",
-};
-pub const compat_time32_files = [_][]const u8{
- "musl/compat/time32/__xstat.c",
- "musl/compat/time32/adjtime32.c",
- "musl/compat/time32/adjtimex_time32.c",
- "musl/compat/time32/aio_suspend_time32.c",
- "musl/compat/time32/clock_adjtime32.c",
- "musl/compat/time32/clock_getres_time32.c",
- "musl/compat/time32/clock_gettime32.c",
- "musl/compat/time32/clock_nanosleep_time32.c",
- "musl/compat/time32/clock_settime32.c",
- "musl/compat/time32/cnd_timedwait_time32.c",
- "musl/compat/time32/ctime32.c",
- "musl/compat/time32/ctime32_r.c",
- "musl/compat/time32/difftime32.c",
- "musl/compat/time32/fstat_time32.c",
- "musl/compat/time32/fstatat_time32.c",
- "musl/compat/time32/ftime32.c",
- "musl/compat/time32/futimens_time32.c",
- "musl/compat/time32/futimes_time32.c",
- "musl/compat/time32/futimesat_time32.c",
- "musl/compat/time32/getitimer_time32.c",
- "musl/compat/time32/getrusage_time32.c",
- "musl/compat/time32/gettimeofday_time32.c",
- "musl/compat/time32/gmtime32.c",
- "musl/compat/time32/gmtime32_r.c",
- "musl/compat/time32/localtime32.c",
- "musl/compat/time32/localtime32_r.c",
- "musl/compat/time32/lstat_time32.c",
- "musl/compat/time32/lutimes_time32.c",
- "musl/compat/time32/mktime32.c",
- "musl/compat/time32/mq_timedreceive_time32.c",
- "musl/compat/time32/mq_timedsend_time32.c",
- "musl/compat/time32/mtx_timedlock_time32.c",
- "musl/compat/time32/nanosleep_time32.c",
- "musl/compat/time32/ppoll_time32.c",
- "musl/compat/time32/pselect_time32.c",
- "musl/compat/time32/pthread_cond_timedwait_time32.c",
- "musl/compat/time32/pthread_mutex_timedlock_time32.c",
- "musl/compat/time32/pthread_rwlock_timedrdlock_time32.c",
- "musl/compat/time32/pthread_rwlock_timedwrlock_time32.c",
- "musl/compat/time32/pthread_timedjoin_np_time32.c",
- "musl/compat/time32/recvmmsg_time32.c",
- "musl/compat/time32/sched_rr_get_interval_time32.c",
- "musl/compat/time32/select_time32.c",
- "musl/compat/time32/sem_timedwait_time32.c",
- "musl/compat/time32/semtimedop_time32.c",
- "musl/compat/time32/setitimer_time32.c",
- "musl/compat/time32/settimeofday_time32.c",
- "musl/compat/time32/sigtimedwait_time32.c",
- "musl/compat/time32/stat_time32.c",
- "musl/compat/time32/stime32.c",
- "musl/compat/time32/thrd_sleep_time32.c",
- "musl/compat/time32/time32.c",
- "musl/compat/time32/time32gm.c",
- "musl/compat/time32/timer_gettime32.c",
- "musl/compat/time32/timer_settime32.c",
- "musl/compat/time32/timerfd_gettime32.c",
- "musl/compat/time32/timerfd_settime32.c",
- "musl/compat/time32/timespec_get_time32.c",
- "musl/compat/time32/utime_time32.c",
- "musl/compat/time32/utimensat_time32.c",
- "musl/compat/time32/utimes_time32.c",
- "musl/compat/time32/wait3_time32.c",
- "musl/compat/time32/wait4_time32.c",
-};
diff --git a/src-self-hosted/print_env.zig b/src-self-hosted/print_env.zig
deleted file mode 100644
index d1956911e9300625e6da291cefc165f11ef46863..0000000000000000000000000000000000000000
--- a/src-self-hosted/print_env.zig
+++ /dev/null
@@ -1,47 +0,0 @@
-const std = @import("std");
-const build_options = @import("build_options");
-const introspect = @import("introspect.zig");
-const Allocator = std.mem.Allocator;
-const fatal = @import("main.zig").fatal;
-
-pub fn cmdEnv(gpa: *Allocator, args: []const []const u8, stdout: anytype) !void {
- const self_exe_path = try std.fs.selfExePathAlloc(gpa);
- defer gpa.free(self_exe_path);
-
- var zig_lib_directory = introspect.findZigLibDirFromSelfExe(gpa, self_exe_path) catch |err| {
- fatal("unable to find zig installation directory: {}\n", .{@errorName(err)});
- };
- defer gpa.free(zig_lib_directory.path.?);
- defer zig_lib_directory.handle.close();
-
- const zig_std_dir = try std.fs.path.join(gpa, &[_][]const u8{ zig_lib_directory.path.?, "std" });
- defer gpa.free(zig_std_dir);
-
- const global_cache_dir = try introspect.resolveGlobalCacheDir(gpa);
- defer gpa.free(global_cache_dir);
-
- var bos = std.io.bufferedOutStream(stdout);
- const bos_stream = bos.outStream();
-
- var jws = std.json.WriteStream(@TypeOf(bos_stream), 6).init(bos_stream);
- try jws.beginObject();
-
- try jws.objectField("zig_exe");
- try jws.emitString(self_exe_path);
-
- try jws.objectField("lib_dir");
- try jws.emitString(zig_lib_directory.path.?);
-
- try jws.objectField("std_dir");
- try jws.emitString(zig_std_dir);
-
- try jws.objectField("global_cache_dir");
- try jws.emitString(global_cache_dir);
-
- try jws.objectField("version");
- try jws.emitString(build_options.version);
-
- try jws.endObject();
- try bos_stream.writeByte('\n');
- try bos.flush();
-}
diff --git a/src-self-hosted/print_targets.zig b/src-self-hosted/print_targets.zig
deleted file mode 100644
index 724cb7a9ac394bfeb17563d51041a6944b386e42..0000000000000000000000000000000000000000
--- a/src-self-hosted/print_targets.zig
+++ /dev/null
@@ -1,161 +0,0 @@
-const std = @import("std");
-const fs = std.fs;
-const io = std.io;
-const mem = std.mem;
-const Allocator = mem.Allocator;
-const Target = std.Target;
-const target = @import("target.zig");
-const assert = std.debug.assert;
-const glibc = @import("glibc.zig");
-const introspect = @import("introspect.zig");
-const fatal = @import("main.zig").fatal;
-
-pub fn cmdTargets(
- allocator: *Allocator,
- args: []const []const u8,
- /// Output stream
- stdout: anytype,
- native_target: Target,
-) !void {
- var zig_lib_directory = introspect.findZigLibDir(allocator) catch |err| {
- fatal("unable to find zig installation directory: {}\n", .{@errorName(err)});
- };
- defer zig_lib_directory.handle.close();
- defer allocator.free(zig_lib_directory.path.?);
-
- const glibc_abi = try glibc.loadMetaData(allocator, zig_lib_directory.handle);
- defer glibc_abi.destroy(allocator);
-
- var bos = io.bufferedOutStream(stdout);
- const bos_stream = bos.outStream();
- var jws = std.json.WriteStream(@TypeOf(bos_stream), 6).init(bos_stream);
-
- try jws.beginObject();
-
- try jws.objectField("arch");
- try jws.beginArray();
- {
- inline for (@typeInfo(Target.Cpu.Arch).Enum.fields) |field| {
- try jws.arrayElem();
- try jws.emitString(field.name);
- }
- }
- try jws.endArray();
-
- try jws.objectField("os");
- try jws.beginArray();
- inline for (@typeInfo(Target.Os.Tag).Enum.fields) |field| {
- try jws.arrayElem();
- try jws.emitString(field.name);
- }
- try jws.endArray();
-
- try jws.objectField("abi");
- try jws.beginArray();
- inline for (@typeInfo(Target.Abi).Enum.fields) |field| {
- try jws.arrayElem();
- try jws.emitString(field.name);
- }
- try jws.endArray();
-
- try jws.objectField("libc");
- try jws.beginArray();
- for (target.available_libcs) |libc| {
- const tmp = try std.fmt.allocPrint(allocator, "{}-{}-{}", .{
- @tagName(libc.arch), @tagName(libc.os), @tagName(libc.abi),
- });
- defer allocator.free(tmp);
- try jws.arrayElem();
- try jws.emitString(tmp);
- }
- try jws.endArray();
-
- try jws.objectField("glibc");
- try jws.beginArray();
- for (glibc_abi.all_versions) |ver| {
- try jws.arrayElem();
-
- const tmp = try std.fmt.allocPrint(allocator, "{}", .{ver});
- defer allocator.free(tmp);
- try jws.emitString(tmp);
- }
- try jws.endArray();
-
- try jws.objectField("cpus");
- try jws.beginObject();
- inline for (@typeInfo(Target.Cpu.Arch).Enum.fields) |field| {
- try jws.objectField(field.name);
- try jws.beginObject();
- const arch = @field(Target.Cpu.Arch, field.name);
- for (arch.allCpuModels()) |model| {
- try jws.objectField(model.name);
- try jws.beginArray();
- for (arch.allFeaturesList()) |feature, i| {
- if (model.features.isEnabled(@intCast(u8, i))) {
- try jws.arrayElem();
- try jws.emitString(feature.name);
- }
- }
- try jws.endArray();
- }
- try jws.endObject();
- }
- try jws.endObject();
-
- try jws.objectField("cpuFeatures");
- try jws.beginObject();
- inline for (@typeInfo(Target.Cpu.Arch).Enum.fields) |field| {
- try jws.objectField(field.name);
- try jws.beginArray();
- const arch = @field(Target.Cpu.Arch, field.name);
- for (arch.allFeaturesList()) |feature| {
- try jws.arrayElem();
- try jws.emitString(feature.name);
- }
- try jws.endArray();
- }
- try jws.endObject();
-
- try jws.objectField("native");
- try jws.beginObject();
- {
- const triple = try native_target.zigTriple(allocator);
- defer allocator.free(triple);
- try jws.objectField("triple");
- try jws.emitString(triple);
- }
- {
- try jws.objectField("cpu");
- try jws.beginObject();
- try jws.objectField("arch");
- try jws.emitString(@tagName(native_target.cpu.arch));
-
- try jws.objectField("name");
- const cpu = native_target.cpu;
- try jws.emitString(cpu.model.name);
-
- {
- try jws.objectField("features");
- try jws.beginArray();
- for (native_target.cpu.arch.allFeaturesList()) |feature, i_usize| {
- const index = @intCast(Target.Cpu.Feature.Set.Index, i_usize);
- if (cpu.features.isEnabled(index)) {
- try jws.arrayElem();
- try jws.emitString(feature.name);
- }
- }
- try jws.endArray();
- }
- try jws.endObject();
- }
- try jws.objectField("os");
- try jws.emitString(@tagName(native_target.os.tag));
- try jws.objectField("abi");
- try jws.emitString(@tagName(native_target.abi));
- try jws.endObject();
-
- try jws.endObject();
-
- try bos_stream.writeByte('\n');
- return bos.flush();
-}
diff --git a/src-self-hosted/stage1.zig b/src-self-hosted/stage1.zig
deleted file mode 100644
index 1ff7b4cf4ca9b0d5c4198873d6fded02204f9177..0000000000000000000000000000000000000000
--- a/src-self-hosted/stage1.zig
+++ /dev/null
@@ -1,351 +0,0 @@
-//! This is the main entry point for the Zig/C++ hybrid compiler (stage1).
-//! It has the functions exported from Zig, called in C++, and bindings for
-//! the functions exported from C++, called from Zig.
-
-const std = @import("std");
-const assert = std.debug.assert;
-const mem = std.mem;
-const build_options = @import("build_options");
-const stage2 = @import("main.zig");
-const fatal = stage2.fatal;
-const CrossTarget = std.zig.CrossTarget;
-const Target = std.Target;
-const Compilation = @import("Compilation.zig");
-
-comptime {
- assert(std.builtin.link_libc);
- assert(build_options.is_stage1);
- _ = @import("compiler_rt");
-}
-
-pub const log = stage2.log;
-pub const log_level = stage2.log_level;
-
-pub export fn main(argc: c_int, argv: [*]const [*:0]const u8) c_int {
- std.debug.maybeEnableSegfaultHandler();
-
- zig_stage1_os_init();
-
- const gpa = std.heap.c_allocator;
- var arena_instance = std.heap.ArenaAllocator.init(gpa);
- defer arena_instance.deinit();
- const arena = &arena_instance.allocator;
-
- const args = arena.alloc([]const u8, @intCast(usize, argc)) catch fatal("out of memory", .{});
- for (args) |*arg, i| {
- arg.* = mem.spanZ(argv[i]);
- }
- stage2.mainArgs(gpa, arena, args) catch |err| fatal("{}", .{err});
- return 0;
-}
-
-/// Matches stage2.Color;
-pub const ErrColor = c_int;
-/// Matches std.builtin.CodeModel
-pub const CodeModel = c_int;
-/// Matches std.Target.Os.Tag
-pub const OS = c_int;
-/// Matches std.builtin.BuildMode
-pub const BuildMode = c_int;
-
-pub const TargetSubsystem = extern enum(c_int) {
- Console,
- Windows,
- Posix,
- Native,
- EfiApplication,
- EfiBootServiceDriver,
- EfiRom,
- EfiRuntimeDriver,
- Auto,
-};
-
-pub const Pkg = extern struct {
- name_ptr: [*]const u8,
- name_len: usize,
- path_ptr: [*]const u8,
- path_len: usize,
- children_ptr: [*]*Pkg,
- children_len: usize,
- parent: ?*Pkg,
-};
-
-pub const Module = extern struct {
- root_name_ptr: [*]const u8,
- root_name_len: usize,
- output_dir_ptr: [*]const u8,
- output_dir_len: usize,
- builtin_zig_path_ptr: [*]const u8,
- builtin_zig_path_len: usize,
- test_filter_ptr: [*]const u8,
- test_filter_len: usize,
- test_name_prefix_ptr: [*]const u8,
- test_name_prefix_len: usize,
- userdata: usize,
- root_pkg: *Pkg,
- main_progress_node: ?*std.Progress.Node,
- code_model: CodeModel,
- subsystem: TargetSubsystem,
- err_color: ErrColor,
- pic: bool,
- link_libc: bool,
- link_libcpp: bool,
- strip: bool,
- is_single_threaded: bool,
- dll_export_fns: bool,
- link_mode_dynamic: bool,
- valgrind_enabled: bool,
- function_sections: bool,
- enable_stack_probing: bool,
- enable_time_report: bool,
- enable_stack_report: bool,
- dump_analysis: bool,
- enable_doc_generation: bool,
- emit_bin: bool,
- emit_asm: bool,
- emit_llvm_ir: bool,
- test_is_evented: bool,
- verbose_tokenize: bool,
- verbose_ast: bool,
- verbose_ir: bool,
- verbose_llvm_ir: bool,
- verbose_cimport: bool,
- verbose_llvm_cpu_features: bool,
-
- pub fn build_object(mod: *Module) void {
- zig_stage1_build_object(mod);
- }
-
- pub fn destroy(mod: *Module) void {
- zig_stage1_destroy(mod);
- }
-};
-
-extern fn zig_stage1_os_init() void;
-
-pub const create = zig_stage1_create;
-extern fn zig_stage1_create(
- optimize_mode: BuildMode,
- main_pkg_path_ptr: [*]const u8,
- main_pkg_path_len: usize,
- root_src_path_ptr: [*]const u8,
- root_src_path_len: usize,
- zig_lib_dir_ptr: [*c]const u8,
- zig_lib_dir_len: usize,
- target: [*c]const Stage2Target,
- is_test_build: bool,
-) ?*Module;
-
-extern fn zig_stage1_build_object(*Module) void;
-extern fn zig_stage1_destroy(*Module) void;
-
-// ABI warning
-export fn stage2_panic(ptr: [*]const u8, len: usize) void {
- @panic(ptr[0..len]);
-}
-
-// ABI warning
-const Error = extern enum {
- None,
- OutOfMemory,
- InvalidFormat,
- SemanticAnalyzeFail,
- AccessDenied,
- Interrupted,
- SystemResources,
- FileNotFound,
- FileSystem,
- FileTooBig,
- DivByZero,
- Overflow,
- PathAlreadyExists,
- Unexpected,
- ExactDivRemainder,
- NegativeDenominator,
- ShiftedOutOneBits,
- CCompileErrors,
- EndOfFile,
- IsDir,
- NotDir,
- UnsupportedOperatingSystem,
- SharingViolation,
- PipeBusy,
- PrimitiveTypeNotFound,
- CacheUnavailable,
- PathTooLong,
- CCompilerCannotFindFile,
- NoCCompilerInstalled,
- ReadingDepFile,
- InvalidDepFile,
- MissingArchitecture,
- MissingOperatingSystem,
- UnknownArchitecture,
- UnknownOperatingSystem,
- UnknownABI,
- InvalidFilename,
- DiskQuota,
- DiskSpace,
- UnexpectedWriteFailure,
- UnexpectedSeekFailure,
- UnexpectedFileTruncationFailure,
- Unimplemented,
- OperationAborted,
- BrokenPipe,
- NoSpaceLeft,
- NotLazy,
- IsAsync,
- ImportOutsidePkgPath,
- UnknownCpuModel,
- UnknownCpuFeature,
- InvalidCpuFeatures,
- InvalidLlvmCpuFeaturesFormat,
- UnknownApplicationBinaryInterface,
- ASTUnitFailure,
- BadPathName,
- SymLinkLoop,
- ProcessFdQuotaExceeded,
- SystemFdQuotaExceeded,
- NoDevice,
- DeviceBusy,
- UnableToSpawnCCompiler,
- CCompilerExitCode,
- CCompilerCrashed,
- CCompilerCannotFindHeaders,
- LibCRuntimeNotFound,
- LibCStdLibHeaderNotFound,
- LibCKernel32LibNotFound,
- UnsupportedArchitecture,
- WindowsSdkNotFound,
- UnknownDynamicLinkerPath,
- TargetHasNoDynamicLinker,
- InvalidAbiVersion,
- InvalidOperatingSystemVersion,
- UnknownClangOption,
- NestedResponseFile,
- ZigIsTheCCompiler,
- FileBusy,
- Locked,
-};
-
-// ABI warning
-export fn stage2_attach_segfault_handler() void {
- if (std.debug.runtime_safety and std.debug.have_segfault_handling_support) {
- std.debug.attachSegfaultHandler();
- }
-}
-
-// ABI warning
-export fn stage2_progress_create() *std.Progress {
- const ptr = std.heap.c_allocator.create(std.Progress) catch @panic("out of memory");
- ptr.* = std.Progress{};
- return ptr;
-}
-
-// ABI warning
-export fn stage2_progress_destroy(progress: *std.Progress) void {
- std.heap.c_allocator.destroy(progress);
-}
-
-// ABI warning
-export fn stage2_progress_start_root(
- progress: *std.Progress,
- name_ptr: [*]const u8,
- name_len: usize,
- estimated_total_items: usize,
-) *std.Progress.Node {
- return progress.start(
- name_ptr[0..name_len],
- if (estimated_total_items == 0) null else estimated_total_items,
- ) catch @panic("timer unsupported");
-}
-
-// ABI warning
-export fn stage2_progress_disable_tty(progress: *std.Progress) void {
- progress.terminal = null;
-}
-
-// ABI warning
-export fn stage2_progress_start(
- node: *std.Progress.Node,
- name_ptr: [*]const u8,
- name_len: usize,
- estimated_total_items: usize,
-) *std.Progress.Node {
- const child_node = std.heap.c_allocator.create(std.Progress.Node) catch @panic("out of memory");
- child_node.* = node.start(
- name_ptr[0..name_len],
- if (estimated_total_items == 0) null else estimated_total_items,
- );
- child_node.activate();
- return child_node;
-}
-
-// ABI warning
-export fn stage2_progress_end(node: *std.Progress.Node) void {
- node.end();
- if (&node.context.root != node) {
- std.heap.c_allocator.destroy(node);
- }
-}
-
-// ABI warning
-export fn stage2_progress_complete_one(node: *std.Progress.Node) void {
- node.completeOne();
-}
-
-// ABI warning
-export fn stage2_progress_update_node(node: *std.Progress.Node, done_count: usize, total_count: usize) void {
- node.completed_items = done_count;
- node.estimated_total_items = total_count;
- node.activate();
- node.context.maybeRefresh();
-}
-
-// ABI warning
-pub const Stage2Target = extern struct {
- arch: c_int,
- os: OS,
- abi: c_int,
-
- is_native_os: bool,
- is_native_cpu: bool,
-
- llvm_cpu_name: ?[*:0]const u8,
- llvm_cpu_features: ?[*:0]const u8,
-};
-
-// ABI warning
-const Stage2SemVer = extern struct {
- major: u32,
- minor: u32,
- patch: u32,
-};
-
-// ABI warning
-export fn stage2_cimport(stage1: *Module) [*:0]const u8 {
- @panic("TODO implement stage2_cimport");
-}
-
-export fn stage2_add_link_lib(
- stage1: *Module,
- lib_name_ptr: [*c]const u8,
- lib_name_len: usize,
- symbol_name_ptr: [*c]const u8,
- symbol_name_len: usize,
-) ?[*:0]const u8 {
- return null; // no error
-}
-
-export fn stage2_fetch_file(
- stage1: *Module,
- path_ptr: [*]const u8,
- path_len: usize,
- result_len: *usize,
-) ?[*]const u8 {
- const comp = @intToPtr(*Compilation, stage1.userdata);
- const file_path = path_ptr[0..path_len];
- const max_file_size = std.math.maxInt(u32);
- const contents = comp.stage1_cache_hash.addFilePostFetch(file_path, max_file_size) catch return null;
- result_len.* = contents.len;
- return contents.ptr;
-}
diff --git a/src-self-hosted/target.zig b/src-self-hosted/target.zig
deleted file mode 100644
index 8ea880e8981f2e134c785822afe1db11c82a024b..0000000000000000000000000000000000000000
--- a/src-self-hosted/target.zig
+++ /dev/null
@@ -1,211 +0,0 @@
-const std = @import("std");
-const llvm = @import("llvm.zig");
-
-pub const ArchOsAbi = struct {
- arch: std.Target.Cpu.Arch,
- os: std.Target.Os.Tag,
- abi: std.Target.Abi,
-};
-
-pub const available_libcs = [_]ArchOsAbi{
- .{ .arch = .aarch64_be, .os = .linux, .abi = .gnu },
- .{ .arch = .aarch64_be, .os = .linux, .abi = .musl },
- .{ .arch = .aarch64_be, .os = .windows, .abi = .gnu },
- .{ .arch = .aarch64, .os = .linux, .abi = .gnu },
- .{ .arch = .aarch64, .os = .linux, .abi = .musl },
- .{ .arch = .aarch64, .os = .windows, .abi = .gnu },
- .{ .arch = .armeb, .os = .linux, .abi = .gnueabi },
- .{ .arch = .armeb, .os = .linux, .abi = .gnueabihf },
- .{ .arch = .armeb, .os = .linux, .abi = .musleabi },
- .{ .arch = .armeb, .os = .linux, .abi = .musleabihf },
- .{ .arch = .armeb, .os = .windows, .abi = .gnu },
- .{ .arch = .arm, .os = .linux, .abi = .gnueabi },
- .{ .arch = .arm, .os = .linux, .abi = .gnueabihf },
- .{ .arch = .arm, .os = .linux, .abi = .musleabi },
- .{ .arch = .arm, .os = .linux, .abi = .musleabihf },
- .{ .arch = .arm, .os = .windows, .abi = .gnu },
- .{ .arch = .i386, .os = .linux, .abi = .gnu },
- .{ .arch = .i386, .os = .linux, .abi = .musl },
- .{ .arch = .i386, .os = .windows, .abi = .gnu },
- .{ .arch = .mips64el, .os = .linux, .abi = .gnuabi64 },
- .{ .arch = .mips64el, .os = .linux, .abi = .gnuabin32 },
- .{ .arch = .mips64el, .os = .linux, .abi = .musl },
- .{ .arch = .mips64, .os = .linux, .abi = .gnuabi64 },
- .{ .arch = .mips64, .os = .linux, .abi = .gnuabin32 },
- .{ .arch = .mips64, .os = .linux, .abi = .musl },
- .{ .arch = .mipsel, .os = .linux, .abi = .gnu },
- .{ .arch = .mipsel, .os = .linux, .abi = .musl },
- .{ .arch = .mips, .os = .linux, .abi = .gnu },
- .{ .arch = .mips, .os = .linux, .abi = .musl },
- .{ .arch = .powerpc64le, .os = .linux, .abi = .gnu },
- .{ .arch = .powerpc64le, .os = .linux, .abi = .musl },
- .{ .arch = .powerpc64, .os = .linux, .abi = .gnu },
- .{ .arch = .powerpc64, .os = .linux, .abi = .musl },
- .{ .arch = .powerpc, .os = .linux, .abi = .gnu },
- .{ .arch = .powerpc, .os = .linux, .abi = .musl },
- .{ .arch = .riscv64, .os = .linux, .abi = .gnu },
- .{ .arch = .riscv64, .os = .linux, .abi = .musl },
- .{ .arch = .s390x, .os = .linux, .abi = .gnu },
- .{ .arch = .s390x, .os = .linux, .abi = .musl },
- .{ .arch = .sparc, .os = .linux, .abi = .gnu },
- .{ .arch = .sparcv9, .os = .linux, .abi = .gnu },
- .{ .arch = .wasm32, .os = .freestanding, .abi = .musl },
- .{ .arch = .x86_64, .os = .linux, .abi = .gnu },
- .{ .arch = .x86_64, .os = .linux, .abi = .gnux32 },
- .{ .arch = .x86_64, .os = .linux, .abi = .musl },
- .{ .arch = .x86_64, .os = .windows, .abi = .gnu },
-};
-
-pub fn libCGenericName(target: std.Target) [:0]const u8 {
- if (target.os.tag == .windows)
- return "mingw";
- switch (target.abi) {
- .gnu,
- .gnuabin32,
- .gnuabi64,
- .gnueabi,
- .gnueabihf,
- .gnux32,
- => return "glibc",
- .musl,
- .musleabi,
- .musleabihf,
- .none,
- => return "musl",
- .code16,
- .eabi,
- .eabihf,
- .android,
- .msvc,
- .itanium,
- .cygnus,
- .coreclr,
- .simulator,
- .macabi,
- => unreachable,
- }
-}
-
-pub fn archMuslName(arch: std.Target.Cpu.Arch) [:0]const u8 {
- switch (arch) {
- .aarch64, .aarch64_be => return "aarch64",
- .arm, .armeb => return "arm",
- .mips, .mipsel => return "mips",
- .mips64el, .mips64 => return "mips64",
- .powerpc => return "powerpc",
- .powerpc64, .powerpc64le => return "powerpc64",
- .s390x => return "s390x",
- .i386 => return "i386",
- .x86_64 => return "x86_64",
- .riscv64 => return "riscv64",
- else => unreachable,
- }
-}
-
-pub fn canBuildLibC(target: std.Target) bool {
- for (available_libcs) |libc| {
- if (target.cpu.arch == libc.arch and target.os.tag == libc.os and target.abi == libc.abi) {
- return true;
- }
- }
- return false;
-}
-
-pub fn cannotDynamicLink(target: std.Target) bool {
- return switch (target.os.tag) {
- .freestanding, .other => true,
- else => false,
- };
-}
-
-/// On Darwin, we always link libSystem which contains libc.
-/// Similarly on FreeBSD and NetBSD we always link system libc
-/// since this is the stable syscall interface.
-pub fn osRequiresLibC(target: std.Target) bool {
- return switch (target.os.tag) {
- .freebsd, .netbsd, .dragonfly, .macosx, .ios, .watchos, .tvos => true,
- else => false,
- };
-}
-
-pub fn requiresPIE(target: std.Target) bool {
- return target.isAndroid();
-}
-
-/// This function returns whether non-pic code is completely invalid on the given target.
-pub fn requiresPIC(target: std.Target, linking_libc: bool) bool {
- return target.isAndroid() or
- target.os.tag == .windows or target.os.tag == .uefi or
- osRequiresLibC(target) or
- (linking_libc and target.isGnuLibC());
-}
-
-/// This is not whether the target supports Position Independent Code, but whether the -fPIC
-/// C compiler argument is valid to Clang.
-pub fn supports_fpic(target: std.Target) bool {
- return target.os.tag != .windows;
-}
-
-pub fn libc_needs_crti_crtn(target: std.Target) bool {
- return !(target.cpu.arch.isRISCV() or target.isAndroid());
-}
-
-pub fn isSingleThreaded(target: std.Target) bool {
- return target.isWasm();
-}
-
-/// Valgrind supports more, but Zig does not support them yet.
-pub fn hasValgrindSupport(target: std.Target) bool {
- switch (target.cpu.arch) {
- .x86_64 => {
- return target.os.tag == .linux or target.isDarwin() or target.os.tag == .solaris or
- (target.os.tag == .windows and target.abi != .msvc);
- },
- else => return false,
- }
-}
-
-pub fn supportsStackProbing(target: std.Target) bool {
- return target.os.tag != .windows and target.os.tag != .uefi and
- (target.cpu.arch == .i386 or target.cpu.arch == .x86_64);
-}
-
-pub fn osToLLVM(os_tag: std.Target.Os.Tag) llvm.OSType {
- return switch (os_tag) {
- .freestanding, .other => .UnknownOS,
- .windows, .uefi => .Win32,
- .ananas => .Ananas,
- .cloudabi => .CloudABI,
- .dragonfly => .DragonFly,
- .freebsd => .FreeBSD,
- .fuchsia => .Fuchsia,
- .ios => .IOS,
- .kfreebsd => .KFreeBSD,
- .linux => .Linux,
- .lv2 => .Lv2,
- .macosx => .MacOSX,
- .netbsd => .NetBSD,
- .openbsd => .OpenBSD,
- .solaris => .Solaris,
- .haiku => .Haiku,
- .minix => .Minix,
- .rtems => .RTEMS,
- .nacl => .NaCl,
- .cnk => .CNK,
- .aix => .AIX,
- .cuda => .CUDA,
- .nvcl => .NVCL,
- .amdhsa => .AMDHSA,
- .ps4 => .PS4,
- .elfiamcu => .ELFIAMCU,
- .tvos => .TvOS,
- .watchos => .WatchOS,
- .mesa3d => .Mesa3D,
- .contiki => .Contiki,
- .amdpal => .AMDPAL,
- .hermit => .HermitCore,
- .hurd => .Hurd,
- .wasi => .WASI,
- .emscripten => .Emscripten,
- };
-}
diff --git a/src-self-hosted/test.zig b/src-self-hosted/test.zig
deleted file mode 100644
index 154a839a3c8dc98e7945be3b9140b3920283e71b..0000000000000000000000000000000000000000
--- a/src-self-hosted/test.zig
+++ /dev/null
@@ -1,812 +0,0 @@
-const std = @import("std");
-const link = @import("link.zig");
-const Compilation = @import("Compilation.zig");
-const Allocator = std.mem.Allocator;
-const zir = @import("zir.zig");
-const Package = @import("Package.zig");
-const introspect = @import("introspect.zig");
-const build_options = @import("build_options");
-const enable_qemu: bool = build_options.enable_qemu;
-const enable_wine: bool = build_options.enable_wine;
-const enable_wasmtime: bool = build_options.enable_wasmtime;
-const glibc_multi_install_dir: ?[]const u8 = build_options.glibc_multi_install_dir;
-
-const cheader = @embedFile("link/cbe.h");
-
-test "self-hosted" {
- var ctx = TestContext.init();
- defer ctx.deinit();
-
- try @import("stage2_tests").addCases(&ctx);
-
- try ctx.run();
-}
-
-const ErrorMsg = struct {
- msg: []const u8,
- line: u32,
- column: u32,
-};
-
-pub const TestContext = struct {
- /// TODO: find a way to treat cases as individual tests (shouldn't show "1 test passed" if there are 200 cases)
- cases: std.ArrayList(Case),
-
- pub const Update = struct {
- /// The input to the current update. We simulate an incremental update
- /// with the file's contents changed to this value each update.
- ///
- /// This value can change entirely between updates, which would be akin
- /// to deleting the source file and creating a new one from scratch; or
- /// you can keep it mostly consistent, with small changes, testing the
- /// effects of the incremental compilation.
- src: [:0]const u8,
- case: union(enum) {
- /// A transformation update transforms the input and tests against
- /// the expected output ZIR.
- Transformation: [:0]const u8,
- /// An error update attempts to compile bad code, and ensures that it
- /// fails to compile, and for the expected reasons.
- /// A slice containing the expected errors *in sequential order*.
- Error: []const ErrorMsg,
- /// An execution update compiles and runs the input, testing the
- /// stdout against the expected results
- /// This is a slice containing the expected message.
- Execution: []const u8,
- },
- };
-
- pub const TestType = enum {
- Zig,
- ZIR,
- };
-
- /// A Case consists of a set of *updates*. The same Compilation is used for each
- /// update, so each update's source is treated as a single file being
- /// updated by the test harness and incrementally compiled.
- pub const Case = struct {
- /// The name of the test case. This is shown if a test fails, and
- /// otherwise ignored.
- name: []const u8,
- /// The platform the test targets. For non-native platforms, an emulator
- /// such as QEMU is required for tests to complete.
- target: std.zig.CrossTarget,
- /// In order to be able to run e.g. Execution updates, this must be set
- /// to Executable.
- output_mode: std.builtin.OutputMode,
- updates: std.ArrayList(Update),
- extension: TestType,
- cbe: bool = false,
-
- /// Adds a subcase in which the module is updated with `src`, and the
- /// resulting ZIR is validated against `result`.
- pub fn addTransform(self: *Case, src: [:0]const u8, result: [:0]const u8) void {
- self.updates.append(.{
- .src = src,
- .case = .{ .Transformation = result },
- }) catch unreachable;
- }
-
- /// Adds a subcase in which the module is updated with `src`, compiled,
- /// run, and the output is tested against `result`.
- pub fn addCompareOutput(self: *Case, src: [:0]const u8, result: []const u8) void {
- self.updates.append(.{
- .src = src,
- .case = .{ .Execution = result },
- }) catch unreachable;
- }
-
- /// Adds a subcase in which the module is updated with `src`, which
- /// should contain invalid input, and ensures that compilation fails
- /// for the expected reasons, given in sequential order in `errors` in
- /// the form `:line:column: error: message`.
- pub fn addError(self: *Case, src: [:0]const u8, errors: []const []const u8) void {
- var array = self.updates.allocator.alloc(ErrorMsg, errors.len) catch unreachable;
- for (errors) |e, i| {
- if (e[0] != ':') {
- @panic("Invalid test: error must be specified as follows:\n:line:column: error: message\n=========\n");
- }
- var cur = e[1..];
- var line_index = std.mem.indexOf(u8, cur, ":");
- if (line_index == null) {
- @panic("Invalid test: error must be specified as follows:\n:line:column: error: message\n=========\n");
- }
- const line = std.fmt.parseInt(u32, cur[0..line_index.?], 10) catch @panic("Unable to parse line number");
- cur = cur[line_index.? + 1 ..];
- const column_index = std.mem.indexOf(u8, cur, ":");
- if (column_index == null) {
- @panic("Invalid test: error must be specified as follows:\n:line:column: error: message\n=========\n");
- }
- const column = std.fmt.parseInt(u32, cur[0..column_index.?], 10) catch @panic("Unable to parse column number");
- cur = cur[column_index.? + 2 ..];
- if (!std.mem.eql(u8, cur[0..7], "error: ")) {
- @panic("Invalid test: error must be specified as follows:\n:line:column: error: message\n=========\n");
- }
- const msg = cur[7..];
-
- if (line == 0 or column == 0) {
- @panic("Invalid test: error line and column must be specified starting at one!");
- }
-
- array[i] = .{
- .msg = msg,
- .line = line - 1,
- .column = column - 1,
- };
- }
- self.updates.append(.{ .src = src, .case = .{ .Error = array } }) catch unreachable;
- }
-
- /// Adds a subcase in which the module is updated with `src`, and
- /// asserts that it compiles without issue
- pub fn compiles(self: *Case, src: [:0]const u8) void {
- self.addError(src, &[_][]const u8{});
- }
- };
-
- pub fn addExe(
- ctx: *TestContext,
- name: []const u8,
- target: std.zig.CrossTarget,
- T: TestType,
- ) *Case {
- ctx.cases.append(Case{
- .name = name,
- .target = target,
- .updates = std.ArrayList(Update).init(ctx.cases.allocator),
- .output_mode = .Exe,
- .extension = T,
- }) catch unreachable;
- return &ctx.cases.items[ctx.cases.items.len - 1];
- }
-
- /// Adds a test case for Zig input, producing an executable
- pub fn exe(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case {
- return ctx.addExe(name, target, .Zig);
- }
-
- /// Adds a test case for ZIR input, producing an executable
- pub fn exeZIR(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case {
- return ctx.addExe(name, target, .ZIR);
- }
-
- pub fn addObj(
- ctx: *TestContext,
- name: []const u8,
- target: std.zig.CrossTarget,
- T: TestType,
- ) *Case {
- ctx.cases.append(Case{
- .name = name,
- .target = target,
- .updates = std.ArrayList(Update).init(ctx.cases.allocator),
- .output_mode = .Obj,
- .extension = T,
- }) catch unreachable;
- return &ctx.cases.items[ctx.cases.items.len - 1];
- }
-
- /// Adds a test case for Zig input, producing an object file
- pub fn obj(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case {
- return ctx.addObj(name, target, .Zig);
- }
-
- /// Adds a test case for ZIR input, producing an object file
- pub fn objZIR(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case {
- return ctx.addObj(name, target, .ZIR);
- }
-
- pub fn addC(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget, T: TestType) *Case {
- ctx.cases.append(Case{
- .name = name,
- .target = target,
- .updates = std.ArrayList(Update).init(ctx.cases.allocator),
- .output_mode = .Obj,
- .extension = T,
- .cbe = true,
- }) catch unreachable;
- return &ctx.cases.items[ctx.cases.items.len - 1];
- }
-
- pub fn c(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget, src: [:0]const u8, comptime out: [:0]const u8) void {
- ctx.addC(name, target, .Zig).addTransform(src, cheader ++ out);
- }
-
- pub fn addCompareOutput(
- ctx: *TestContext,
- name: []const u8,
- T: TestType,
- src: [:0]const u8,
- expected_stdout: []const u8,
- ) void {
- ctx.addExe(name, .{}, T).addCompareOutput(src, expected_stdout);
- }
-
- /// Adds a test case that compiles the Zig source given in `src`, executes
- /// it, runs it, and tests the output against `expected_stdout`
- pub fn compareOutput(
- ctx: *TestContext,
- name: []const u8,
- src: [:0]const u8,
- expected_stdout: []const u8,
- ) void {
- return ctx.addCompareOutput(name, .Zig, src, expected_stdout);
- }
-
- /// Adds a test case that compiles the ZIR source given in `src`, executes
- /// it, runs it, and tests the output against `expected_stdout`
- pub fn compareOutputZIR(
- ctx: *TestContext,
- name: []const u8,
- src: [:0]const u8,
- expected_stdout: []const u8,
- ) void {
- ctx.addCompareOutput(name, .ZIR, src, expected_stdout);
- }
-
- pub fn addTransform(
- ctx: *TestContext,
- name: []const u8,
- target: std.zig.CrossTarget,
- T: TestType,
- src: [:0]const u8,
- result: [:0]const u8,
- ) void {
- ctx.addObj(name, target, T).addTransform(src, result);
- }
-
- /// Adds a test case that compiles the Zig given in `src` to ZIR and tests
- /// the ZIR against `result`
- pub fn transform(
- ctx: *TestContext,
- name: []const u8,
- target: std.zig.CrossTarget,
- src: [:0]const u8,
- result: [:0]const u8,
- ) void {
- ctx.addTransform(name, target, .Zig, src, result);
- }
-
- /// Adds a test case that cleans up the ZIR source given in `src`, and
- /// tests the resulting ZIR against `result`
- pub fn transformZIR(
- ctx: *TestContext,
- name: []const u8,
- target: std.zig.CrossTarget,
- src: [:0]const u8,
- result: [:0]const u8,
- ) void {
- ctx.addTransform(name, target, .ZIR, src, result);
- }
-
- pub fn addError(
- ctx: *TestContext,
- name: []const u8,
- target: std.zig.CrossTarget,
- T: TestType,
- src: [:0]const u8,
- expected_errors: []const []const u8,
- ) void {
- ctx.addObj(name, target, T).addError(src, expected_errors);
- }
-
- /// Adds a test case that ensures that the Zig given in `src` fails to
- /// compile for the expected reasons, given in sequential order in
- /// `expected_errors` in the form `:line:column: error: message`.
- pub fn compileError(
- ctx: *TestContext,
- name: []const u8,
- target: std.zig.CrossTarget,
- src: [:0]const u8,
- expected_errors: []const []const u8,
- ) void {
- ctx.addError(name, target, .Zig, src, expected_errors);
- }
-
- /// Adds a test case that ensures that the ZIR given in `src` fails to
- /// compile for the expected reasons, given in sequential order in
- /// `expected_errors` in the form `:line:column: error: message`.
- pub fn compileErrorZIR(
- ctx: *TestContext,
- name: []const u8,
- target: std.zig.CrossTarget,
- src: [:0]const u8,
- expected_errors: []const []const u8,
- ) void {
- ctx.addError(name, target, .ZIR, src, expected_errors);
- }
-
- pub fn addCompiles(
- ctx: *TestContext,
- name: []const u8,
- target: std.zig.CrossTarget,
- T: TestType,
- src: [:0]const u8,
- ) void {
- ctx.addObj(name, target, T).compiles(src);
- }
-
- /// Adds a test case that asserts that the Zig given in `src` compiles
- /// without any errors.
- pub fn compiles(
- ctx: *TestContext,
- name: []const u8,
- target: std.zig.CrossTarget,
- src: [:0]const u8,
- ) void {
- ctx.addCompiles(name, target, .Zig, src);
- }
-
- /// Adds a test case that asserts that the ZIR given in `src` compiles
- /// without any errors.
- pub fn compilesZIR(
- ctx: *TestContext,
- name: []const u8,
- target: std.zig.CrossTarget,
- src: [:0]const u8,
- ) void {
- ctx.addCompiles(name, target, .ZIR, src);
- }
-
- /// Adds a test case that first ensures that the Zig given in `src` fails
- /// to compile for the reasons given in sequential order in
- /// `expected_errors` in the form `:line:column: error: message`, then
- /// asserts that fixing the source (updating with `fixed_src`) isn't broken
- /// by incremental compilation.
- pub fn incrementalFailure(
- ctx: *TestContext,
- name: []const u8,
- target: std.zig.CrossTarget,
- src: [:0]const u8,
- expected_errors: []const []const u8,
- fixed_src: [:0]const u8,
- ) void {
- var case = ctx.addObj(name, target, .Zig);
- case.addError(src, expected_errors);
- case.compiles(fixed_src);
- }
-
- /// Adds a test case that first ensures that the ZIR given in `src` fails
- /// to compile for the reasons given in sequential order in
- /// `expected_errors` in the form `:line:column: error: message`, then
- /// asserts that fixing the source (updating with `fixed_src`) isn't broken
- /// by incremental compilation.
- pub fn incrementalFailureZIR(
- ctx: *TestContext,
- name: []const u8,
- target: std.zig.CrossTarget,
- src: [:0]const u8,
- expected_errors: []const []const u8,
- fixed_src: [:0]const u8,
- ) void {
- var case = ctx.addObj(name, target, .ZIR);
- case.addError(src, expected_errors);
- case.compiles(fixed_src);
- }
-
- fn init() TestContext {
- const allocator = std.heap.page_allocator;
- return .{ .cases = std.ArrayList(Case).init(allocator) };
- }
-
- fn deinit(self: *TestContext) void {
- for (self.cases.items) |case| {
- for (case.updates.items) |u| {
- if (u.case == .Error) {
- case.updates.allocator.free(u.case.Error);
- }
- }
- case.updates.deinit();
- }
- self.cases.deinit();
- self.* = undefined;
- }
-
- fn run(self: *TestContext) !void {
- var progress = std.Progress{};
- const root_node = try progress.start("tests", self.cases.items.len);
- defer root_node.end();
-
- var zig_lib_directory = try introspect.findZigLibDir(std.testing.allocator);
- defer zig_lib_directory.handle.close();
- defer std.testing.allocator.free(zig_lib_directory.path.?);
-
- const random_seed = blk: {
- var random_seed: u64 = undefined;
- try std.crypto.randomBytes(std.mem.asBytes(&random_seed));
- break :blk random_seed;
- };
- var default_prng = std.rand.DefaultPrng.init(random_seed);
-
- for (self.cases.items) |case| {
- var prg_node = root_node.start(case.name, case.updates.items.len);
- prg_node.activate();
- defer prg_node.end();
-
- // So that we can see which test case failed when the leak checker goes off,
- // or there's an internal error
- progress.initial_delay_ns = 0;
- progress.refresh_rate_ns = 0;
-
- try self.runOneCase(std.testing.allocator, &prg_node, case, zig_lib_directory, &default_prng.random);
- }
- }
-
- fn runOneCase(
- self: *TestContext,
- allocator: *Allocator,
- root_node: *std.Progress.Node,
- case: Case,
- zig_lib_directory: Compilation.Directory,
- rand: *std.rand.Random,
- ) !void {
- const target_info = try std.zig.system.NativeTargetInfo.detect(allocator, case.target);
- const target = target_info.target;
-
- var arena_allocator = std.heap.ArenaAllocator.init(allocator);
- defer arena_allocator.deinit();
- const arena = &arena_allocator.allocator;
-
- var tmp = std.testing.tmpDir(.{});
- defer tmp.cleanup();
-
- var cache_dir = try tmp.dir.makeOpenPath("zig-cache", .{});
- defer cache_dir.close();
- const bogus_path = "bogus"; // TODO this will need to be fixed before we can test LLVM extensions
- const zig_cache_directory: Compilation.Directory = .{
- .handle = cache_dir,
- .path = try std.fs.path.join(arena, &[_][]const u8{ bogus_path, "zig-cache" }),
- };
-
- const tmp_src_path = if (case.extension == .Zig) "test_case.zig" else if (case.extension == .ZIR) "test_case.zir" else unreachable;
- const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path);
- defer root_pkg.destroy(allocator);
-
- const ofmt: ?std.builtin.ObjectFormat = if (case.cbe) .c else null;
- const bin_name = try std.zig.binNameAlloc(arena, "test_case", target, case.output_mode, null, ofmt);
-
- const emit_directory: Compilation.Directory = .{
- .path = bogus_path,
- .handle = tmp.dir,
- };
- const emit_bin: Compilation.EmitLoc = .{
- .directory = emit_directory,
- .basename = bin_name,
- };
- const comp = try Compilation.create(allocator, .{
- .zig_cache_directory = zig_cache_directory,
- .zig_lib_directory = zig_lib_directory,
- .rand = rand,
- .root_name = "test_case",
- .target = target,
- // TODO: support tests for object file building, and library builds
- // and linking. This will require a rework to support multi-file
- // tests.
- .output_mode = case.output_mode,
- // TODO: support testing optimizations
- .optimize_mode = .Debug,
- .emit_bin = emit_bin,
- .root_pkg = root_pkg,
- .keep_source_files_loaded = true,
- .object_format = ofmt,
- .is_native_os = case.target.isNativeOs(),
- });
- defer comp.destroy();
-
- for (case.updates.items) |update, update_index| {
- var update_node = root_node.start("update", 3);
- update_node.activate();
- defer update_node.end();
-
- var sync_node = update_node.start("write", null);
- sync_node.activate();
- try tmp.dir.writeFile(tmp_src_path, update.src);
- sync_node.end();
-
- var module_node = update_node.start("parse/analysis/codegen", null);
- module_node.activate();
- try comp.makeBinFileWritable();
- try comp.update();
- module_node.end();
-
- if (update.case != .Error) {
- var all_errors = try comp.getAllErrorsAlloc();
- defer all_errors.deinit(allocator);
- if (all_errors.list.len != 0) {
- std.debug.print("\nErrors occurred updating the compilation:\n================\n", .{});
- for (all_errors.list) |err| {
- std.debug.print(":{}:{}: error: {}\n================\n", .{ err.line + 1, err.column + 1, err.msg });
- }
- if (case.cbe) {
- const C = comp.bin_file.cast(link.File.C).?;
- std.debug.print("Generated C: \n===============\n{}\n\n===========\n\n", .{C.main.items});
- }
- std.debug.print("Test failed.\n", .{});
- std.process.exit(1);
- }
- }
-
- switch (update.case) {
- .Transformation => |expected_output| {
- if (case.cbe) {
- // The C file is always closed after an update, because we don't support
- // incremental updates
- var file = try tmp.dir.openFile(bin_name, .{ .read = true });
- defer file.close();
- var out = file.reader().readAllAlloc(arena, 1024 * 1024) catch @panic("Unable to read C output!");
-
- if (expected_output.len != out.len) {
- std.debug.print("\nTransformed C length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });
- std.process.exit(1);
- }
- for (expected_output) |e, i| {
- if (out[i] != e) {
- std.debug.print("\nTransformed C differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });
- std.process.exit(1);
- }
- }
- } else {
- update_node.estimated_total_items = 5;
- var emit_node = update_node.start("emit", null);
- emit_node.activate();
- var new_zir_module = try zir.emit(allocator, comp.bin_file.options.module.?);
- defer new_zir_module.deinit(allocator);
- emit_node.end();
-
- var write_node = update_node.start("write", null);
- write_node.activate();
- var out_zir = std.ArrayList(u8).init(allocator);
- defer out_zir.deinit();
- try new_zir_module.writeToStream(allocator, out_zir.outStream());
- write_node.end();
-
- var test_node = update_node.start("assert", null);
- test_node.activate();
- defer test_node.end();
-
- if (expected_output.len != out_zir.items.len) {
- std.debug.print("{}\nTransformed ZIR length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });
- std.process.exit(1);
- }
- for (expected_output) |e, i| {
- if (out_zir.items[i] != e) {
- std.debug.print("{}\nTransformed ZIR differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });
- std.process.exit(1);
- }
- }
- }
- },
- .Error => |e| {
- var test_node = update_node.start("assert", null);
- test_node.activate();
- defer test_node.end();
- var handled_errors = try arena.alloc(bool, e.len);
- for (handled_errors) |*h| {
- h.* = false;
- }
- var all_errors = try comp.getAllErrorsAlloc();
- defer all_errors.deinit(allocator);
- for (all_errors.list) |a| {
- for (e) |ex, i| {
- if (a.line == ex.line and a.column == ex.column and std.mem.eql(u8, ex.msg, a.msg)) {
- handled_errors[i] = true;
- break;
- }
- } else {
- std.debug.print("{}\nUnexpected error:\n================\n:{}:{}: error: {}\n================\nTest failed.\n", .{ case.name, a.line + 1, a.column + 1, a.msg });
- std.process.exit(1);
- }
- }
-
- for (handled_errors) |h, i| {
- if (!h) {
- const er = e[i];
- std.debug.print("{}\nDid not receive error:\n================\n{}:{}: {}\n================\nTest failed.\n", .{ case.name, er.line, er.column, er.msg });
- std.process.exit(1);
- }
- }
- },
- .Execution => |expected_stdout| {
- std.debug.assert(!case.cbe);
-
- update_node.estimated_total_items = 4;
- var exec_result = x: {
- var exec_node = update_node.start("execute", null);
- exec_node.activate();
- defer exec_node.end();
-
- var argv = std.ArrayList([]const u8).init(allocator);
- defer argv.deinit();
-
- const exe_path = try std.fmt.allocPrint(arena, "." ++ std.fs.path.sep_str ++ "{}", .{bin_name});
-
- switch (case.target.getExternalExecutor()) {
- .native => try argv.append(exe_path),
- .unavailable => {
- try self.runInterpreterIfAvailable(allocator, &exec_node, case, tmp.dir, bin_name);
- return; // Pass test.
- },
-
- .qemu => |qemu_bin_name| if (enable_qemu) {
- // TODO Ability for test cases to specify whether to link libc.
- const need_cross_glibc = false; // target.isGnuLibC() and self.is_linking_libc;
- const glibc_dir_arg = if (need_cross_glibc)
- glibc_multi_install_dir orelse return // glibc dir not available; pass test
- else
- null;
- try argv.append(qemu_bin_name);
- if (glibc_dir_arg) |dir| {
- const linux_triple = try target.linuxTriple(arena);
- const full_dir = try std.fs.path.join(arena, &[_][]const u8{
- dir,
- linux_triple,
- });
-
- try argv.append("-L");
- try argv.append(full_dir);
- }
- try argv.append(exe_path);
- } else {
- return; // QEMU not available; pass test.
- },
-
- .wine => |wine_bin_name| if (enable_wine) {
- try argv.append(wine_bin_name);
- try argv.append(exe_path);
- } else {
- return; // Wine not available; pass test.
- },
-
- .wasmtime => |wasmtime_bin_name| if (enable_wasmtime) {
- try argv.append(wasmtime_bin_name);
- try argv.append("--dir=.");
- try argv.append(exe_path);
- } else {
- return; // wasmtime not available; pass test.
- },
- }
-
- try comp.makeBinFileExecutable();
-
- break :x try std.ChildProcess.exec(.{
- .allocator = allocator,
- .argv = argv.items,
- .cwd_dir = tmp.dir,
- });
- };
- var test_node = update_node.start("test", null);
- test_node.activate();
- defer test_node.end();
- defer allocator.free(exec_result.stdout);
- defer allocator.free(exec_result.stderr);
- switch (exec_result.term) {
- .Exited => |code| {
- if (code != 0) {
- std.debug.print("elf file exited with code {}\n", .{code});
- return error.BinaryBadExitCode;
- }
- },
- else => return error.BinaryCrashed,
- }
- if (!std.mem.eql(u8, expected_stdout, exec_result.stdout)) {
- std.debug.panic(
- "update index {}, mismatched stdout\n====Expected (len={}):====\n{}\n====Actual (len={}):====\n{}\n========\n",
- .{ update_index, expected_stdout.len, expected_stdout, exec_result.stdout.len, exec_result.stdout },
- );
- }
- },
- }
- }
- }
-
- fn runInterpreterIfAvailable(
- self: *TestContext,
- gpa: *Allocator,
- node: *std.Progress.Node,
- case: Case,
- tmp_dir: std.fs.Dir,
- bin_name: []const u8,
- ) !void {
- const arch = case.target.cpu_arch orelse return;
- switch (arch) {
- .spu_2 => return self.runSpu2Interpreter(gpa, node, case, tmp_dir, bin_name),
- else => return,
- }
- }
-
- fn runSpu2Interpreter(
- self: *TestContext,
- gpa: *Allocator,
- update_node: *std.Progress.Node,
- case: Case,
- tmp_dir: std.fs.Dir,
- bin_name: []const u8,
- ) !void {
- const spu = @import("codegen/spu-mk2.zig");
- if (case.target.os_tag) |os| {
- if (os != .freestanding) {
- std.debug.panic("Only freestanding makes sense for SPU-II tests!", .{});
- }
- } else {
- std.debug.panic("SPU_2 has no native OS, check the test!", .{});
- }
-
- var interpreter = spu.Interpreter(struct {
- RAM: [0x10000]u8 = undefined,
-
- pub fn read8(bus: @This(), addr: u16) u8 {
- return bus.RAM[addr];
- }
- pub fn read16(bus: @This(), addr: u16) u16 {
- return std.mem.readIntLittle(u16, bus.RAM[addr..][0..2]);
- }
-
- pub fn write8(bus: *@This(), addr: u16, val: u8) void {
- bus.RAM[addr] = val;
- }
-
- pub fn write16(bus: *@This(), addr: u16, val: u16) void {
- std.mem.writeIntLittle(u16, bus.RAM[addr..][0..2], val);
- }
- }){
- .bus = .{},
- };
-
- {
- var load_node = update_node.start("load", null);
- load_node.activate();
- defer load_node.end();
-
- var file = try tmp_dir.openFile(bin_name, .{ .read = true });
- defer file.close();
-
- const header = try std.elf.readHeader(file);
- var iterator = header.program_header_iterator(file);
-
- var none_loaded = true;
-
- while (try iterator.next()) |phdr| {
- if (phdr.p_type != std.elf.PT_LOAD) {
- std.debug.print("Encountered unexpected ELF program header: type {}\n", .{phdr.p_type});
- std.process.exit(1);
- }
- if (phdr.p_paddr != phdr.p_vaddr) {
- std.debug.print("Physical address does not match virtual address in ELF header!\n", .{});
- std.process.exit(1);
- }
- if (phdr.p_filesz != phdr.p_memsz) {
- std.debug.print("Physical size does not match virtual size in ELF header!\n", .{});
- std.process.exit(1);
- }
- if ((try file.pread(interpreter.bus.RAM[phdr.p_paddr .. phdr.p_paddr + phdr.p_filesz], phdr.p_offset)) != phdr.p_filesz) {
- std.debug.print("Read less than expected from ELF file!", .{});
- std.process.exit(1);
- }
- std.log.scoped(.spu2_test).debug("Loaded 0x{x} bytes to 0x{x:0<4}\n", .{ phdr.p_filesz, phdr.p_paddr });
- none_loaded = false;
- }
- if (none_loaded) {
- std.debug.print("No data found in ELF file!\n", .{});
- std.process.exit(1);
- }
- }
-
- var exec_node = update_node.start("execute", null);
- exec_node.activate();
- defer exec_node.end();
-
- var blocks: u16 = 1000;
- const block_size = 1000;
- while (!interpreter.undefined0) {
- const pre_ip = interpreter.ip;
- if (blocks > 0) {
- blocks -= 1;
- try interpreter.ExecuteBlock(block_size);
- if (pre_ip == interpreter.ip) {
- std.debug.print("Infinite loop detected in SPU II test!\n", .{});
- std.process.exit(1);
- }
- }
- }
- }
-};
diff --git a/src-self-hosted/tracy.zig b/src-self-hosted/tracy.zig
deleted file mode 100644
index 6f56a87ce6fad8484cfc8f37ff5e4e97ca0570f1..0000000000000000000000000000000000000000
--- a/src-self-hosted/tracy.zig
+++ /dev/null
@@ -1,45 +0,0 @@
-pub const std = @import("std");
-
-pub const enable = if (std.builtin.is_test) false else @import("build_options").enable_tracy;
-
-extern fn ___tracy_emit_zone_begin_callstack(
- srcloc: *const ___tracy_source_location_data,
- depth: c_int,
- active: c_int,
-) ___tracy_c_zone_context;
-
-extern fn ___tracy_emit_zone_end(ctx: ___tracy_c_zone_context) void;
-
-pub const ___tracy_source_location_data = extern struct {
- name: ?[*:0]const u8,
- function: [*:0]const u8,
- file: [*:0]const u8,
- line: u32,
- color: u32,
-};
-
-pub const ___tracy_c_zone_context = extern struct {
- id: u32,
- active: c_int,
-
- pub fn end(self: ___tracy_c_zone_context) void {
- ___tracy_emit_zone_end(self);
- }
-};
-
-pub const Ctx = if (enable) ___tracy_c_zone_context else struct {
- pub fn end(self: Ctx) void {}
-};
-
-pub inline fn trace(comptime src: std.builtin.SourceLocation) Ctx {
- if (!enable) return .{};
-
- const loc: ___tracy_source_location_data = .{
- .name = null,
- .function = src.fn_name.ptr,
- .file = src.file.ptr,
- .line = src.line,
- .color = 0,
- };
- return ___tracy_emit_zone_begin_callstack(&loc, 1, 1);
-}
diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig
deleted file mode 100644
index c5c5231ca206b90e7f30c8391afa4f35f2a83c07..0000000000000000000000000000000000000000
--- a/src-self-hosted/translate_c.zig
+++ /dev/null
@@ -1,6470 +0,0 @@
-//! This is the userland implementation of translate-c which is used by both stage1
-//! and stage2.
-
-const std = @import("std");
-const assert = std.debug.assert;
-const ast = std.zig.ast;
-const Token = std.zig.Token;
-usingnamespace @import("clang.zig");
-const ctok = std.c.tokenizer;
-const CToken = std.c.Token;
-const mem = std.mem;
-const math = std.math;
-
-const CallingConvention = std.builtin.CallingConvention;
-
-pub const ClangErrMsg = Stage2ErrorMsg;
-
-pub const Error = error{OutOfMemory};
-const TypeError = Error || error{UnsupportedType};
-const TransError = TypeError || error{UnsupportedTranslation};
-
-const DeclTable = std.AutoArrayHashMap(usize, []const u8);
-
-const SymbolTable = std.StringArrayHashMap(*ast.Node);
-const AliasList = std.ArrayList(struct {
- alias: []const u8,
- name: []const u8,
-});
-
-const Scope = struct {
- id: Id,
- parent: ?*Scope,
-
- const Id = enum {
- Switch,
- Block,
- Root,
- Condition,
- Loop,
- };
-
- /// Represents an in-progress ast.Node.Switch. This struct is stack-allocated.
- /// When it is deinitialized, it produces an ast.Node.Switch which is allocated
- /// into the main arena.
- const Switch = struct {
- base: Scope,
- pending_block: Block,
- cases: []*ast.Node,
- case_index: usize,
- switch_label: ?[]const u8,
- default_label: ?[]const u8,
- };
-
- /// Used for the scope of condition expressions, for example `if (cond)`.
- /// The block is lazily initialised because it is only needed for rare
- /// cases of comma operators being used.
- const Condition = struct {
- base: Scope,
- block: ?Block = null,
-
- fn getBlockScope(self: *Condition, c: *Context) !*Block {
- if (self.block) |*b| return b;
- self.block = try Block.init(c, &self.base, true);
- return &self.block.?;
- }
-
- fn deinit(self: *Condition) void {
- if (self.block) |*b| b.deinit();
- }
- };
-
- /// Represents an in-progress ast.Node.Block. This struct is stack-allocated.
- /// When it is deinitialized, it produces an ast.Node.Block which is allocated
- /// into the main arena.
- const Block = struct {
- base: Scope,
- statements: std.ArrayList(*ast.Node),
- variables: AliasList,
- label: ?ast.TokenIndex,
- mangle_count: u32 = 0,
- lbrace: ast.TokenIndex,
-
- fn init(c: *Context, parent: *Scope, labeled: bool) !Block {
- var blk = Block{
- .base = .{
- .id = .Block,
- .parent = parent,
- },
- .statements = std.ArrayList(*ast.Node).init(c.gpa),
- .variables = AliasList.init(c.gpa),
- .label = null,
- .lbrace = try appendToken(c, .LBrace, "{"),
- };
- if (labeled) {
- blk.label = try appendIdentifier(c, try blk.makeMangledName(c, "blk"));
- _ = try appendToken(c, .Colon, ":");
- }
- return blk;
- }
-
- fn deinit(self: *Block) void {
- self.statements.deinit();
- self.variables.deinit();
- self.* = undefined;
- }
-
- fn complete(self: *Block, c: *Context) !*ast.Node {
- // We reserve 1 extra statement if the parent is a Loop. This is in case of
- // do while, we want to put `if (cond) break;` at the end.
- const alloc_len = self.statements.items.len + @boolToInt(self.base.parent.?.id == .Loop);
- const rbrace = try appendToken(c, .RBrace, "}");
- if (self.label) |label| {
- const node = try ast.Node.LabeledBlock.alloc(c.arena, alloc_len);
- node.* = .{
- .statements_len = self.statements.items.len,
- .lbrace = self.lbrace,
- .rbrace = rbrace,
- .label = label,
- };
- mem.copy(*ast.Node, node.statements(), self.statements.items);
- return &node.base;
- } else {
- const node = try ast.Node.Block.alloc(c.arena, alloc_len);
- node.* = .{
- .statements_len = self.statements.items.len,
- .lbrace = self.lbrace,
- .rbrace = rbrace,
- };
- mem.copy(*ast.Node, node.statements(), self.statements.items);
- return &node.base;
- }
- }
-
- /// Given the desired name, return a name that does not shadow anything from outer scopes.
- /// Inserts the returned name into the scope.
- fn makeMangledName(scope: *Block, c: *Context, name: []const u8) ![]const u8 {
- const name_copy = try c.arena.dupe(u8, name);
- var proposed_name = name_copy;
- while (scope.contains(proposed_name)) {
- scope.mangle_count += 1;
- proposed_name = try std.fmt.allocPrint(c.arena, "{}_{}", .{ name, scope.mangle_count });
- }
- try scope.variables.append(.{ .name = name_copy, .alias = proposed_name });
- return proposed_name;
- }
-
- fn getAlias(scope: *Block, name: []const u8) []const u8 {
- for (scope.variables.items) |p| {
- if (mem.eql(u8, p.name, name))
- return p.alias;
- }
- return scope.base.parent.?.getAlias(name);
- }
-
- fn localContains(scope: *Block, name: []const u8) bool {
- for (scope.variables.items) |p| {
- if (mem.eql(u8, p.alias, name))
- return true;
- }
- return false;
- }
-
- fn contains(scope: *Block, name: []const u8) bool {
- if (scope.localContains(name))
- return true;
- return scope.base.parent.?.contains(name);
- }
- };
-
- const Root = struct {
- base: Scope,
- sym_table: SymbolTable,
- macro_table: SymbolTable,
- context: *Context,
-
- fn init(c: *Context) Root {
- return .{
- .base = .{
- .id = .Root,
- .parent = null,
- },
- .sym_table = SymbolTable.init(c.arena),
- .macro_table = SymbolTable.init(c.arena),
- .context = c,
- };
- }
-
- /// Check if the global scope contains this name, without looking into the "future", e.g.
- /// ignore the preprocessed decl and macro names.
- fn containsNow(scope: *Root, name: []const u8) bool {
- return isZigPrimitiveType(name) or
- scope.sym_table.contains(name) or
- scope.macro_table.contains(name);
- }
-
- /// Check if the global scope contains the name, includes all decls that haven't been translated yet.
- fn contains(scope: *Root, name: []const u8) bool {
- return scope.containsNow(name) or scope.context.global_names.contains(name);
- }
- };
-
- fn findBlockScope(inner: *Scope, c: *Context) !*Scope.Block {
- var scope = inner;
- while (true) {
- switch (scope.id) {
- .Root => unreachable,
- .Block => return @fieldParentPtr(Block, "base", scope),
- .Condition => return @fieldParentPtr(Condition, "base", scope).getBlockScope(c),
- else => scope = scope.parent.?,
- }
- }
- }
-
- fn getAlias(scope: *Scope, name: []const u8) []const u8 {
- return switch (scope.id) {
- .Root => return name,
- .Block => @fieldParentPtr(Block, "base", scope).getAlias(name),
- .Switch, .Loop, .Condition => scope.parent.?.getAlias(name),
- };
- }
-
- fn contains(scope: *Scope, name: []const u8) bool {
- return switch (scope.id) {
- .Root => @fieldParentPtr(Root, "base", scope).contains(name),
- .Block => @fieldParentPtr(Block, "base", scope).contains(name),
- .Switch, .Loop, .Condition => scope.parent.?.contains(name),
- };
- }
-
- fn getBreakableScope(inner: *Scope) *Scope {
- var scope = inner;
- while (true) {
- switch (scope.id) {
- .Root => unreachable,
- .Switch => return scope,
- .Loop => return scope,
- else => scope = scope.parent.?,
- }
- }
- }
-
- fn getSwitch(inner: *Scope) *Scope.Switch {
- var scope = inner;
- while (true) {
- switch (scope.id) {
- .Root => unreachable,
- .Switch => return @fieldParentPtr(Switch, "base", scope),
- else => scope = scope.parent.?,
- }
- }
- }
-};
-
-pub const Context = struct {
- gpa: *mem.Allocator,
- arena: *mem.Allocator,
- token_ids: std.ArrayListUnmanaged(Token.Id),
- token_locs: std.ArrayListUnmanaged(Token.Loc),
- errors: std.ArrayListUnmanaged(ast.Error),
- source_buffer: *std.ArrayList(u8),
- err: Error,
- source_manager: *ZigClangSourceManager,
- decl_table: DeclTable,
- alias_list: AliasList,
- global_scope: *Scope.Root,
- clang_context: *ZigClangASTContext,
- mangle_count: u32 = 0,
- root_decls: std.ArrayListUnmanaged(*ast.Node),
-
- /// This one is different than the root scope's name table. This contains
- /// a list of names that we found by visiting all the top level decls without
- /// translating them. The other maps are updated as we translate; this one is updated
- /// up front in a pre-processing step.
- global_names: std.StringArrayHashMap(void),
-
- fn getMangle(c: *Context) u32 {
- c.mangle_count += 1;
- return c.mangle_count;
- }
-
- /// Convert a null-terminated C string to a slice allocated in the arena
- fn str(c: *Context, s: [*:0]const u8) ![]u8 {
- return mem.dupe(c.arena, u8, mem.spanZ(s));
- }
-
- /// Convert a clang source location to a file:line:column string
- fn locStr(c: *Context, loc: ZigClangSourceLocation) ![]u8 {
- const spelling_loc = ZigClangSourceManager_getSpellingLoc(c.source_manager, loc);
- const filename_c = ZigClangSourceManager_getFilename(c.source_manager, spelling_loc);
- const filename = if (filename_c) |s| try c.str(s) else @as([]const u8, "(no file)");
-
- const line = ZigClangSourceManager_getSpellingLineNumber(c.source_manager, spelling_loc);
- const column = ZigClangSourceManager_getSpellingColumnNumber(c.source_manager, spelling_loc);
- return std.fmt.allocPrint(c.arena, "{}:{}:{}", .{ filename, line, column });
- }
-
- fn createCall(c: *Context, fn_expr: *ast.Node, params_len: ast.NodeIndex) !*ast.Node.Call {
- _ = try appendToken(c, .LParen, "(");
- const node = try ast.Node.Call.alloc(c.arena, params_len);
- node.* = .{
- .lhs = fn_expr,
- .params_len = params_len,
- .async_token = null,
- .rtoken = undefined, // set after appending args
- };
- return node;
- }
-
- fn createBuiltinCall(c: *Context, name: []const u8, params_len: ast.NodeIndex) !*ast.Node.BuiltinCall {
- const builtin_token = try appendToken(c, .Builtin, name);
- _ = try appendToken(c, .LParen, "(");
- const node = try ast.Node.BuiltinCall.alloc(c.arena, params_len);
- node.* = .{
- .builtin_token = builtin_token,
- .params_len = params_len,
- .rparen_token = undefined, // set after appending args
- };
- return node;
- }
-
- fn createBlock(c: *Context, statements_len: ast.NodeIndex) !*ast.Node.Block {
- const block_node = try ast.Node.Block.alloc(c.arena, statements_len);
- block_node.* = .{
- .lbrace = try appendToken(c, .LBrace, "{"),
- .statements_len = statements_len,
- .rbrace = undefined,
- };
- return block_node;
- }
-};
-
-pub fn translate(
- gpa: *mem.Allocator,
- args_begin: [*]?[*]const u8,
- args_end: [*]?[*]const u8,
- errors: *[]ClangErrMsg,
- resources_path: [*:0]const u8,
-) !*ast.Tree {
- const ast_unit = ZigClangLoadFromCommandLine(
- args_begin,
- args_end,
- &errors.ptr,
- &errors.len,
- resources_path,
- ) orelse {
- if (errors.len == 0) return error.ASTUnitFailure;
- return error.SemanticAnalyzeFail;
- };
- defer ZigClangASTUnit_delete(ast_unit);
-
- var source_buffer = std.ArrayList(u8).init(gpa);
- defer source_buffer.deinit();
-
- // For memory that has the same lifetime as the Tree that we return
- // from this function.
- var arena = std.heap.ArenaAllocator.init(gpa);
- errdefer arena.deinit();
-
- var context = Context{
- .gpa = gpa,
- .arena = &arena.allocator,
- .source_buffer = &source_buffer,
- .source_manager = ZigClangASTUnit_getSourceManager(ast_unit),
- .err = undefined,
- .decl_table = DeclTable.init(gpa),
- .alias_list = AliasList.init(gpa),
- .global_scope = try arena.allocator.create(Scope.Root),
- .clang_context = ZigClangASTUnit_getASTContext(ast_unit).?,
- .global_names = std.StringArrayHashMap(void).init(gpa),
- .token_ids = .{},
- .token_locs = .{},
- .errors = .{},
- .root_decls = .{},
- };
- context.global_scope.* = Scope.Root.init(&context);
- defer context.decl_table.deinit();
- defer context.alias_list.deinit();
- defer context.token_ids.deinit(gpa);
- defer context.token_locs.deinit(gpa);
- defer context.errors.deinit(gpa);
- defer context.global_names.deinit();
- defer context.root_decls.deinit(gpa);
-
- try prepopulateGlobalNameTable(ast_unit, &context);
-
- if (!ZigClangASTUnit_visitLocalTopLevelDecls(ast_unit, &context, declVisitorC)) {
- return context.err;
- }
-
- try transPreprocessorEntities(&context, ast_unit);
-
- try addMacros(&context);
- for (context.alias_list.items) |alias| {
- if (!context.global_scope.sym_table.contains(alias.alias)) {
- try createAlias(&context, alias);
- }
- }
-
- const eof_token = try appendToken(&context, .Eof, "");
- const root_node = try ast.Node.Root.create(&arena.allocator, context.root_decls.items.len, eof_token);
- mem.copy(*ast.Node, root_node.decls(), context.root_decls.items);
-
- if (false) {
- std.debug.warn("debug source:\n{}\n==EOF==\ntokens:\n", .{source_buffer.items});
- for (context.token_ids.items) |token| {
- std.debug.warn("{}\n", .{token});
- }
- }
-
- const tree = try arena.allocator.create(ast.Tree);
- tree.* = .{
- .gpa = gpa,
- .source = try arena.allocator.dupe(u8, source_buffer.items),
- .token_ids = context.token_ids.toOwnedSlice(gpa),
- .token_locs = context.token_locs.toOwnedSlice(gpa),
- .errors = context.errors.toOwnedSlice(gpa),
- .root_node = root_node,
- .arena = arena.state,
- .generated = true,
- };
- return tree;
-}
-
-fn prepopulateGlobalNameTable(ast_unit: *ZigClangASTUnit, c: *Context) !void {
- if (!ZigClangASTUnit_visitLocalTopLevelDecls(ast_unit, c, declVisitorNamesOnlyC)) {
- return c.err;
- }
-
- // TODO if we see #undef, delete it from the table
- var it = ZigClangASTUnit_getLocalPreprocessingEntities_begin(ast_unit);
- const it_end = ZigClangASTUnit_getLocalPreprocessingEntities_end(ast_unit);
-
- while (it.I != it_end.I) : (it.I += 1) {
- const entity = ZigClangPreprocessingRecord_iterator_deref(it);
- switch (ZigClangPreprocessedEntity_getKind(entity)) {
- .MacroDefinitionKind => {
- const macro = @ptrCast(*ZigClangMacroDefinitionRecord, entity);
- const raw_name = ZigClangMacroDefinitionRecord_getName_getNameStart(macro);
- const name = try c.str(raw_name);
- _ = try c.global_names.put(name, {});
- },
- else => {},
- }
- }
-}
-
-fn declVisitorNamesOnlyC(context: ?*c_void, decl: *const ZigClangDecl) callconv(.C) bool {
- const c = @ptrCast(*Context, @alignCast(@alignOf(Context), context));
- declVisitorNamesOnly(c, decl) catch |err| {
- c.err = err;
- return false;
- };
- return true;
-}
-
-fn declVisitorC(context: ?*c_void, decl: *const ZigClangDecl) callconv(.C) bool {
- const c = @ptrCast(*Context, @alignCast(@alignOf(Context), context));
- declVisitor(c, decl) catch |err| {
- c.err = err;
- return false;
- };
- return true;
-}
-
-fn declVisitorNamesOnly(c: *Context, decl: *const ZigClangDecl) Error!void {
- if (ZigClangDecl_castToNamedDecl(decl)) |named_decl| {
- const decl_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(named_decl));
- _ = try c.global_names.put(decl_name, {});
- }
-}
-
-fn declVisitor(c: *Context, decl: *const ZigClangDecl) Error!void {
- switch (ZigClangDecl_getKind(decl)) {
- .Function => {
- return visitFnDecl(c, @ptrCast(*const ZigClangFunctionDecl, decl));
- },
- .Typedef => {
- _ = try transTypeDef(c, @ptrCast(*const ZigClangTypedefNameDecl, decl), true);
- },
- .Enum => {
- _ = try transEnumDecl(c, @ptrCast(*const ZigClangEnumDecl, decl));
- },
- .Record => {
- _ = try transRecordDecl(c, @ptrCast(*const ZigClangRecordDecl, decl));
- },
- .Var => {
- return visitVarDecl(c, @ptrCast(*const ZigClangVarDecl, decl), null);
- },
- .Empty => {
- // Do nothing
- },
- else => {
- const decl_name = try c.str(ZigClangDecl_getDeclKindName(decl));
- try emitWarning(c, ZigClangDecl_getLocation(decl), "ignoring {} declaration", .{decl_name});
- },
- }
-}
-
-fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {
- const fn_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, fn_decl)));
- if (c.global_scope.sym_table.contains(fn_name))
- return; // Avoid processing this decl twice
-
- // Skip this declaration if a proper definition exists
- if (!ZigClangFunctionDecl_isThisDeclarationADefinition(fn_decl)) {
- if (ZigClangFunctionDecl_getDefinition(fn_decl)) |def|
- return visitFnDecl(c, def);
- }
-
- const rp = makeRestorePoint(c);
- const fn_decl_loc = ZigClangFunctionDecl_getLocation(fn_decl);
- const has_body = ZigClangFunctionDecl_hasBody(fn_decl);
- const storage_class = ZigClangFunctionDecl_getStorageClass(fn_decl);
- const decl_ctx = FnDeclContext{
- .fn_name = fn_name,
- .has_body = has_body,
- .storage_class = storage_class,
- .is_export = switch (storage_class) {
- .None => has_body and !ZigClangFunctionDecl_isInlineSpecified(fn_decl),
- .Extern, .Static => false,
- .PrivateExtern => return failDecl(c, fn_decl_loc, fn_name, "unsupported storage class: private extern", .{}),
- .Auto => unreachable, // Not legal on functions
- .Register => unreachable, // Not legal on functions
- },
- };
-
- var fn_qt = ZigClangFunctionDecl_getType(fn_decl);
-
- const fn_type = while (true) {
- const fn_type = ZigClangQualType_getTypePtr(fn_qt);
-
- switch (ZigClangType_getTypeClass(fn_type)) {
- .Attributed => {
- const attr_type = @ptrCast(*const ZigClangAttributedType, fn_type);
- fn_qt = ZigClangAttributedType_getEquivalentType(attr_type);
- },
- .Paren => {
- const paren_type = @ptrCast(*const ZigClangParenType, fn_type);
- fn_qt = ZigClangParenType_getInnerType(paren_type);
- },
- else => break fn_type,
- }
- } else unreachable;
-
- const proto_node = switch (ZigClangType_getTypeClass(fn_type)) {
- .FunctionProto => blk: {
- const fn_proto_type = @ptrCast(*const ZigClangFunctionProtoType, fn_type);
- break :blk transFnProto(rp, fn_decl, fn_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) {
- error.UnsupportedType => {
- return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{});
- },
- error.OutOfMemory => |e| return e,
- };
- },
- .FunctionNoProto => blk: {
- const fn_no_proto_type = @ptrCast(*const ZigClangFunctionType, fn_type);
- break :blk transFnNoProto(rp, fn_no_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) {
- error.UnsupportedType => {
- return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{});
- },
- error.OutOfMemory => |e| return e,
- };
- },
- else => return failDecl(c, fn_decl_loc, fn_name, "unable to resolve function type {}", .{ZigClangType_getTypeClass(fn_type)}),
- };
-
- if (!decl_ctx.has_body) {
- const semi_tok = try appendToken(c, .Semicolon, ";");
- return addTopLevelDecl(c, fn_name, &proto_node.base);
- }
-
- // actual function definition with body
- const body_stmt = ZigClangFunctionDecl_getBody(fn_decl);
- var block_scope = try Scope.Block.init(rp.c, &c.global_scope.base, false);
- defer block_scope.deinit();
- var scope = &block_scope.base;
-
- var param_id: c_uint = 0;
- for (proto_node.params()) |*param, i| {
- const param_name = if (param.name_token) |name_tok|
- tokenSlice(c, name_tok)
- else
- return failDecl(c, fn_decl_loc, fn_name, "function {} parameter has no name", .{fn_name});
-
- const c_param = ZigClangFunctionDecl_getParamDecl(fn_decl, param_id);
- const qual_type = ZigClangParmVarDecl_getOriginalType(c_param);
- const is_const = ZigClangQualType_isConstQualified(qual_type);
-
- const mangled_param_name = try block_scope.makeMangledName(c, param_name);
-
- if (!is_const) {
- const bare_arg_name = try std.fmt.allocPrint(c.arena, "arg_{}", .{mangled_param_name});
- const arg_name = try block_scope.makeMangledName(c, bare_arg_name);
-
- const mut_tok = try appendToken(c, .Keyword_var, "var");
- const name_tok = try appendIdentifier(c, mangled_param_name);
- const eq_token = try appendToken(c, .Equal, "=");
- const init_node = try transCreateNodeIdentifier(c, arg_name);
- const semicolon_token = try appendToken(c, .Semicolon, ";");
- const node = try ast.Node.VarDecl.create(c.arena, .{
- .mut_token = mut_tok,
- .name_token = name_tok,
- .semicolon_token = semicolon_token,
- }, .{
- .eq_token = eq_token,
- .init_node = init_node,
- });
- try block_scope.statements.append(&node.base);
- param.name_token = try appendIdentifier(c, arg_name);
- _ = try appendToken(c, .Colon, ":");
- }
-
- param_id += 1;
- }
-
- const casted_body = @ptrCast(*const ZigClangCompoundStmt, body_stmt);
- transCompoundStmtInline(rp, &block_scope.base, casted_body, &block_scope) catch |err| switch (err) {
- error.OutOfMemory => |e| return e,
- error.UnsupportedTranslation,
- error.UnsupportedType,
- => return failDecl(c, fn_decl_loc, fn_name, "unable to translate function", .{}),
- };
- // add return statement if the function didn't have one
- blk: {
- const fn_ty = @ptrCast(*const ZigClangFunctionType, fn_type);
-
- if (ZigClangFunctionType_getNoReturnAttr(fn_ty)) break :blk;
- const return_qt = ZigClangFunctionType_getReturnType(fn_ty);
- if (isCVoid(return_qt)) break :blk;
-
- if (block_scope.statements.items.len > 0) {
- var last = block_scope.statements.items[block_scope.statements.items.len - 1];
- while (true) {
- switch (last.tag) {
- .Block, .LabeledBlock => {
- const stmts = last.blockStatements();
- if (stmts.len == 0) break;
-
- last = stmts[stmts.len - 1];
- },
- // no extra return needed
- .Return => break :blk,
- else => break,
- }
- }
- }
-
- const return_expr = try ast.Node.ControlFlowExpression.create(rp.c.arena, .{
- .ltoken = try appendToken(rp.c, .Keyword_return, "return"),
- .tag = .Return,
- }, .{
- .rhs = transZeroInitExpr(rp, scope, fn_decl_loc, ZigClangQualType_getTypePtr(return_qt)) catch |err| switch (err) {
- error.OutOfMemory => |e| return e,
- error.UnsupportedTranslation,
- error.UnsupportedType,
- => return failDecl(c, fn_decl_loc, fn_name, "unable to create a return value for function", .{}),
- },
- });
- _ = try appendToken(rp.c, .Semicolon, ";");
- try block_scope.statements.append(&return_expr.base);
- }
-
- const body_node = try block_scope.complete(rp.c);
- proto_node.setBodyNode(body_node);
- return addTopLevelDecl(c, fn_name, &proto_node.base);
-}
-
-/// if mangled_name is not null, this var decl was declared in a block scope.
-fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl, mangled_name: ?[]const u8) Error!void {
- const var_name = mangled_name orelse try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, var_decl)));
- if (c.global_scope.sym_table.contains(var_name))
- return; // Avoid processing this decl twice
- const rp = makeRestorePoint(c);
- const visib_tok = if (mangled_name) |_| null else try appendToken(c, .Keyword_pub, "pub");
-
- const thread_local_token = if (ZigClangVarDecl_getTLSKind(var_decl) == .None)
- null
- else
- try appendToken(c, .Keyword_threadlocal, "threadlocal");
-
- const scope = &c.global_scope.base;
-
- // TODO https://github.com/ziglang/zig/issues/3756
- // TODO https://github.com/ziglang/zig/issues/1802
- const checked_name = if (isZigPrimitiveType(var_name)) try std.fmt.allocPrint(c.arena, "{}_{}", .{ var_name, c.getMangle() }) else var_name;
- const var_decl_loc = ZigClangVarDecl_getLocation(var_decl);
-
- const qual_type = ZigClangVarDecl_getTypeSourceInfo_getType(var_decl);
- const storage_class = ZigClangVarDecl_getStorageClass(var_decl);
- const is_const = ZigClangQualType_isConstQualified(qual_type);
- const has_init = ZigClangVarDecl_hasInit(var_decl);
-
- // In C extern variables with initializers behave like Zig exports.
- // extern int foo = 2;
- // does the same as:
- // extern int foo;
- // int foo = 2;
- const extern_tok = if (storage_class == .Extern and !has_init)
- try appendToken(c, .Keyword_extern, "extern")
- else if (storage_class != .Static)
- try appendToken(c, .Keyword_export, "export")
- else
- null;
-
- const mut_tok = if (is_const)
- try appendToken(c, .Keyword_const, "const")
- else
- try appendToken(c, .Keyword_var, "var");
-
- const name_tok = try appendIdentifier(c, checked_name);
-
- _ = try appendToken(c, .Colon, ":");
- const type_node = transQualType(rp, qual_type, var_decl_loc) catch |err| switch (err) {
- error.UnsupportedType => {
- return failDecl(c, var_decl_loc, checked_name, "unable to resolve variable type", .{});
- },
- error.OutOfMemory => |e| return e,
- };
-
- var eq_tok: ast.TokenIndex = undefined;
- var init_node: ?*ast.Node = null;
-
- // If the initialization expression is not present, initialize with undefined.
- // If it is an integer literal, we can skip the @as since it will be redundant
- // with the variable type.
- if (has_init) {
- eq_tok = try appendToken(c, .Equal, "=");
- init_node = if (ZigClangVarDecl_getInit(var_decl)) |expr|
- transExprCoercing(rp, &c.global_scope.base, expr, .used, .r_value) catch |err| switch (err) {
- error.UnsupportedTranslation,
- error.UnsupportedType,
- => {
- return failDecl(c, var_decl_loc, checked_name, "unable to translate initializer", .{});
- },
- error.OutOfMemory => |e| return e,
- }
- else
- try transCreateNodeUndefinedLiteral(c);
- } else if (storage_class != .Extern) {
- eq_tok = try appendToken(c, .Equal, "=");
- // The C language specification states that variables with static or threadlocal
- // storage without an initializer are initialized to a zero value.
-
- // @import("std").mem.zeroes(T)
- const import_fn_call = try c.createBuiltinCall("@import", 1);
- const std_node = try transCreateNodeStringLiteral(c, "\"std\"");
- import_fn_call.params()[0] = std_node;
- import_fn_call.rparen_token = try appendToken(c, .RParen, ")");
- const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "mem");
- const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "zeroes");
-
- const zero_init_call = try c.createCall(outer_field_access, 1);
- zero_init_call.params()[0] = type_node;
- zero_init_call.rtoken = try appendToken(c, .RParen, ")");
-
- init_node = &zero_init_call.base;
- }
-
- const linksection_expr = blk: {
- var str_len: usize = undefined;
- if (ZigClangVarDecl_getSectionAttribute(var_decl, &str_len)) |str_ptr| {
- _ = try appendToken(rp.c, .Keyword_linksection, "linksection");
- _ = try appendToken(rp.c, .LParen, "(");
- const expr = try transCreateNodeStringLiteral(
- rp.c,
- try std.fmt.allocPrint(rp.c.arena, "\"{}\"", .{str_ptr[0..str_len]}),
- );
- _ = try appendToken(rp.c, .RParen, ")");
-
- break :blk expr;
- }
- break :blk null;
- };
-
- const align_expr = blk: {
- const alignment = ZigClangVarDecl_getAlignedAttribute(var_decl, rp.c.clang_context);
- if (alignment != 0) {
- _ = try appendToken(rp.c, .Keyword_align, "align");
- _ = try appendToken(rp.c, .LParen, "(");
- // Clang reports the alignment in bits
- const expr = try transCreateNodeInt(rp.c, alignment / 8);
- _ = try appendToken(rp.c, .RParen, ")");
-
- break :blk expr;
- }
- break :blk null;
- };
-
- const node = try ast.Node.VarDecl.create(c.arena, .{
- .name_token = name_tok,
- .mut_token = mut_tok,
- .semicolon_token = try appendToken(c, .Semicolon, ";"),
- }, .{
- .visib_token = visib_tok,
- .thread_local_token = thread_local_token,
- .eq_token = eq_tok,
- .extern_export_token = extern_tok,
- .type_node = type_node,
- .align_node = align_expr,
- .section_node = linksection_expr,
- .init_node = init_node,
- });
- return addTopLevelDecl(c, checked_name, &node.base);
-}
-
-fn transTypeDefAsBuiltin(c: *Context, typedef_decl: *const ZigClangTypedefNameDecl, builtin_name: []const u8) !*ast.Node {
- _ = try c.decl_table.put(@ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)), builtin_name);
- return transCreateNodeIdentifier(c, builtin_name);
-}
-
-fn checkForBuiltinTypedef(checked_name: []const u8) ?[]const u8 {
- const table = [_][2][]const u8{
- .{ "uint8_t", "u8" },
- .{ "int8_t", "i8" },
- .{ "uint16_t", "u16" },
- .{ "int16_t", "i16" },
- .{ "uint32_t", "u32" },
- .{ "int32_t", "i32" },
- .{ "uint64_t", "u64" },
- .{ "int64_t", "i64" },
- .{ "intptr_t", "isize" },
- .{ "uintptr_t", "usize" },
- .{ "ssize_t", "isize" },
- .{ "size_t", "usize" },
- };
-
- for (table) |entry| {
- if (mem.eql(u8, checked_name, entry[0])) {
- return entry[1];
- }
- }
-
- return null;
-}
-
-fn transTypeDef(c: *Context, typedef_decl: *const ZigClangTypedefNameDecl, top_level_visit: bool) Error!?*ast.Node {
- if (c.decl_table.get(@ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)))) |name|
- return transCreateNodeIdentifier(c, name); // Avoid processing this decl twice
- const rp = makeRestorePoint(c);
-
- const typedef_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, typedef_decl)));
-
- // TODO https://github.com/ziglang/zig/issues/3756
- // TODO https://github.com/ziglang/zig/issues/1802
- const checked_name = if (isZigPrimitiveType(typedef_name)) try std.fmt.allocPrint(c.arena, "{}_{}", .{ typedef_name, c.getMangle() }) else typedef_name;
- if (checkForBuiltinTypedef(checked_name)) |builtin| {
- return transTypeDefAsBuiltin(c, typedef_decl, builtin);
- }
-
- if (!top_level_visit) {
- return transCreateNodeIdentifier(c, checked_name);
- }
-
- _ = try c.decl_table.put(@ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)), checked_name);
- const node = (try transCreateNodeTypedef(rp, typedef_decl, true, checked_name)) orelse return null;
- try addTopLevelDecl(c, checked_name, node);
- return transCreateNodeIdentifier(c, checked_name);
-}
-
-fn transCreateNodeTypedef(
- rp: RestorePoint,
- typedef_decl: *const ZigClangTypedefNameDecl,
- toplevel: bool,
- checked_name: []const u8,
-) Error!?*ast.Node {
- const visib_tok = if (toplevel) try appendToken(rp.c, .Keyword_pub, "pub") else null;
- const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
- const name_tok = try appendIdentifier(rp.c, checked_name);
- const eq_token = try appendToken(rp.c, .Equal, "=");
- const child_qt = ZigClangTypedefNameDecl_getUnderlyingType(typedef_decl);
- const typedef_loc = ZigClangTypedefNameDecl_getLocation(typedef_decl);
- const init_node = transQualType(rp, child_qt, typedef_loc) catch |err| switch (err) {
- error.UnsupportedType => {
- try failDecl(rp.c, typedef_loc, checked_name, "unable to resolve typedef child type", .{});
- return null;
- },
- error.OutOfMemory => |e| return e,
- };
- const semicolon_token = try appendToken(rp.c, .Semicolon, ";");
-
- const node = try ast.Node.VarDecl.create(rp.c.arena, .{
- .name_token = name_tok,
- .mut_token = mut_tok,
- .semicolon_token = semicolon_token,
- }, .{
- .visib_token = visib_tok,
- .eq_token = eq_token,
- .init_node = init_node,
- });
- return &node.base;
-}
-
-fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?*ast.Node {
- if (c.decl_table.get(@ptrToInt(ZigClangRecordDecl_getCanonicalDecl(record_decl)))) |name|
- return try transCreateNodeIdentifier(c, name); // Avoid processing this decl twice
- const record_loc = ZigClangRecordDecl_getLocation(record_decl);
-
- var bare_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, record_decl)));
- var is_unnamed = false;
- // Record declarations such as `struct {...} x` have no name but they're not
- // anonymous hence here isAnonymousStructOrUnion is not needed
- if (bare_name.len == 0) {
- bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{}", .{c.getMangle()});
- is_unnamed = true;
- }
-
- var container_kind_name: []const u8 = undefined;
- var container_kind: std.zig.Token.Id = undefined;
- if (ZigClangRecordDecl_isUnion(record_decl)) {
- container_kind_name = "union";
- container_kind = .Keyword_union;
- } else if (ZigClangRecordDecl_isStruct(record_decl)) {
- container_kind_name = "struct";
- container_kind = .Keyword_struct;
- } else {
- try emitWarning(c, record_loc, "record {} is not a struct or union", .{bare_name});
- return null;
- }
-
- const name = try std.fmt.allocPrint(c.arena, "{}_{}", .{ container_kind_name, bare_name });
- _ = try c.decl_table.put(@ptrToInt(ZigClangRecordDecl_getCanonicalDecl(record_decl)), name);
-
- const visib_tok = if (!is_unnamed) try appendToken(c, .Keyword_pub, "pub") else null;
- const mut_tok = try appendToken(c, .Keyword_const, "const");
- const name_tok = try appendIdentifier(c, name);
-
- const eq_token = try appendToken(c, .Equal, "=");
-
- var semicolon: ast.TokenIndex = undefined;
- const init_node = blk: {
- const rp = makeRestorePoint(c);
- const record_def = ZigClangRecordDecl_getDefinition(record_decl) orelse {
- const opaque = try transCreateNodeOpaqueType(c);
- semicolon = try appendToken(c, .Semicolon, ";");
- break :blk opaque;
- };
-
- const layout_tok = try if (ZigClangRecordDecl_getPackedAttribute(record_decl))
- appendToken(c, .Keyword_packed, "packed")
- else
- appendToken(c, .Keyword_extern, "extern");
- const container_tok = try appendToken(c, container_kind, container_kind_name);
- const lbrace_token = try appendToken(c, .LBrace, "{");
-
- var fields_and_decls = std.ArrayList(*ast.Node).init(c.gpa);
- defer fields_and_decls.deinit();
-
- var unnamed_field_count: u32 = 0;
- var it = ZigClangRecordDecl_field_begin(record_def);
- const end_it = ZigClangRecordDecl_field_end(record_def);
- while (ZigClangRecordDecl_field_iterator_neq(it, end_it)) : (it = ZigClangRecordDecl_field_iterator_next(it)) {
- const field_decl = ZigClangRecordDecl_field_iterator_deref(it);
- const field_loc = ZigClangFieldDecl_getLocation(field_decl);
- const field_qt = ZigClangFieldDecl_getType(field_decl);
-
- if (ZigClangFieldDecl_isBitField(field_decl)) {
- const opaque = try transCreateNodeOpaqueType(c);
- semicolon = try appendToken(c, .Semicolon, ";");
- try emitWarning(c, field_loc, "{} demoted to opaque type - has bitfield", .{container_kind_name});
- break :blk opaque;
- }
-
- if (ZigClangType_isIncompleteOrZeroLengthArrayType(qualTypeCanon(field_qt), c.clang_context)) {
- const opaque = try transCreateNodeOpaqueType(c);
- semicolon = try appendToken(c, .Semicolon, ";");
- try emitWarning(c, field_loc, "{} demoted to opaque type - has variable length array", .{container_kind_name});
- break :blk opaque;
- }
-
- var is_anon = false;
- var raw_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, field_decl)));
- if (ZigClangFieldDecl_isAnonymousStructOrUnion(field_decl) or raw_name.len == 0) {
- // Context.getMangle() is not used here because doing so causes unpredictable field names for anonymous fields.
- raw_name = try std.fmt.allocPrint(c.arena, "unnamed_{}", .{unnamed_field_count});
- unnamed_field_count += 1;
- is_anon = true;
- }
- const field_name = try appendIdentifier(c, raw_name);
- _ = try appendToken(c, .Colon, ":");
- const field_type = transQualType(rp, field_qt, field_loc) catch |err| switch (err) {
- error.UnsupportedType => {
- const opaque = try transCreateNodeOpaqueType(c);
- semicolon = try appendToken(c, .Semicolon, ";");
- try emitWarning(c, record_loc, "{} demoted to opaque type - unable to translate type of field {}", .{ container_kind_name, raw_name });
- break :blk opaque;
- },
- else => |e| return e,
- };
-
- const align_expr = blk_2: {
- const alignment = ZigClangFieldDecl_getAlignedAttribute(field_decl, rp.c.clang_context);
- if (alignment != 0) {
- _ = try appendToken(rp.c, .Keyword_align, "align");
- _ = try appendToken(rp.c, .LParen, "(");
- // Clang reports the alignment in bits
- const expr = try transCreateNodeInt(rp.c, alignment / 8);
- _ = try appendToken(rp.c, .RParen, ")");
-
- break :blk_2 expr;
- }
- break :blk_2 null;
- };
-
- const field_node = try c.arena.create(ast.Node.ContainerField);
- field_node.* = .{
- .doc_comments = null,
- .comptime_token = null,
- .name_token = field_name,
- .type_expr = field_type,
- .value_expr = null,
- .align_expr = align_expr,
- };
-
- if (is_anon) {
- _ = try c.decl_table.put(
- @ptrToInt(ZigClangFieldDecl_getCanonicalDecl(field_decl)),
- raw_name,
- );
- }
-
- try fields_and_decls.append(&field_node.base);
- _ = try appendToken(c, .Comma, ",");
- }
- const container_node = try ast.Node.ContainerDecl.alloc(c.arena, fields_and_decls.items.len);
- container_node.* = .{
- .layout_token = layout_tok,
- .kind_token = container_tok,
- .init_arg_expr = .None,
- .fields_and_decls_len = fields_and_decls.items.len,
- .lbrace_token = lbrace_token,
- .rbrace_token = try appendToken(c, .RBrace, "}"),
- };
- mem.copy(*ast.Node, container_node.fieldsAndDecls(), fields_and_decls.items);
- semicolon = try appendToken(c, .Semicolon, ";");
- break :blk &container_node.base;
- };
-
- const node = try ast.Node.VarDecl.create(c.arena, .{
- .name_token = name_tok,
- .mut_token = mut_tok,
- .semicolon_token = semicolon,
- }, .{
- .visib_token = visib_tok,
- .eq_token = eq_token,
- .init_node = init_node,
- });
-
- try addTopLevelDecl(c, name, &node.base);
- if (!is_unnamed)
- try c.alias_list.append(.{ .alias = bare_name, .name = name });
- return transCreateNodeIdentifier(c, name);
-}
-
-fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.Node {
- if (c.decl_table.get(@ptrToInt(ZigClangEnumDecl_getCanonicalDecl(enum_decl)))) |name|
- return try transCreateNodeIdentifier(c, name); // Avoid processing this decl twice
- const rp = makeRestorePoint(c);
- const enum_loc = ZigClangEnumDecl_getLocation(enum_decl);
-
- var bare_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, enum_decl)));
- var is_unnamed = false;
- if (bare_name.len == 0) {
- bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{}", .{c.getMangle()});
- is_unnamed = true;
- }
-
- const name = try std.fmt.allocPrint(c.arena, "enum_{}", .{bare_name});
- _ = try c.decl_table.put(@ptrToInt(ZigClangEnumDecl_getCanonicalDecl(enum_decl)), name);
-
- const visib_tok = if (!is_unnamed) try appendToken(c, .Keyword_pub, "pub") else null;
- const mut_tok = try appendToken(c, .Keyword_const, "const");
- const name_tok = try appendIdentifier(c, name);
- const eq_token = try appendToken(c, .Equal, "=");
-
- const init_node = if (ZigClangEnumDecl_getDefinition(enum_decl)) |enum_def| blk: {
- var pure_enum = true;
- var it = ZigClangEnumDecl_enumerator_begin(enum_def);
- var end_it = ZigClangEnumDecl_enumerator_end(enum_def);
- while (ZigClangEnumDecl_enumerator_iterator_neq(it, end_it)) : (it = ZigClangEnumDecl_enumerator_iterator_next(it)) {
- const enum_const = ZigClangEnumDecl_enumerator_iterator_deref(it);
- if (ZigClangEnumConstantDecl_getInitExpr(enum_const)) |_| {
- pure_enum = false;
- break;
- }
- }
-
- const extern_tok = try appendToken(c, .Keyword_extern, "extern");
- const container_tok = try appendToken(c, .Keyword_enum, "enum");
-
- var fields_and_decls = std.ArrayList(*ast.Node).init(c.gpa);
- defer fields_and_decls.deinit();
-
- const int_type = ZigClangEnumDecl_getIntegerType(enum_decl);
- // The underlying type may be null in case of forward-declared enum
- // types, while that's not ISO-C compliant many compilers allow this and
- // default to the usual integer type used for all the enums.
-
- // default to c_int since msvc and gcc default to different types
- _ = try appendToken(c, .LParen, "(");
- const init_arg_expr = ast.Node.ContainerDecl.InitArg{
- .Type = if (int_type.ptr != null and
- !isCBuiltinType(int_type, .UInt) and
- !isCBuiltinType(int_type, .Int))
- transQualType(rp, int_type, enum_loc) catch |err| switch (err) {
- error.UnsupportedType => {
- try failDecl(c, enum_loc, name, "unable to translate enum tag type", .{});
- return null;
- },
- else => |e| return e,
- }
- else
- try transCreateNodeIdentifier(c, "c_int"),
- };
- _ = try appendToken(c, .RParen, ")");
-
- const lbrace_token = try appendToken(c, .LBrace, "{");
-
- it = ZigClangEnumDecl_enumerator_begin(enum_def);
- end_it = ZigClangEnumDecl_enumerator_end(enum_def);
- while (ZigClangEnumDecl_enumerator_iterator_neq(it, end_it)) : (it = ZigClangEnumDecl_enumerator_iterator_next(it)) {
- const enum_const = ZigClangEnumDecl_enumerator_iterator_deref(it);
-
- const enum_val_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, enum_const)));
-
- const field_name = if (!is_unnamed and mem.startsWith(u8, enum_val_name, bare_name))
- enum_val_name[bare_name.len..]
- else
- enum_val_name;
-
- const field_name_tok = try appendIdentifier(c, field_name);
-
- const int_node = if (!pure_enum) blk_2: {
- _ = try appendToken(c, .Colon, "=");
- break :blk_2 try transCreateNodeAPInt(c, ZigClangEnumConstantDecl_getInitVal(enum_const));
- } else
- null;
-
- const field_node = try c.arena.create(ast.Node.ContainerField);
- field_node.* = .{
- .doc_comments = null,
- .comptime_token = null,
- .name_token = field_name_tok,
- .type_expr = null,
- .value_expr = int_node,
- .align_expr = null,
- };
-
- try fields_and_decls.append(&field_node.base);
- _ = try appendToken(c, .Comma, ",");
-
- // In C each enum value is in the global namespace. So we put them there too.
- // At this point we can rely on the enum emitting successfully.
- const tld_visib_tok = try appendToken(c, .Keyword_pub, "pub");
- const tld_mut_tok = try appendToken(c, .Keyword_const, "const");
- const tld_name_tok = try appendIdentifier(c, enum_val_name);
- const tld_eq_token = try appendToken(c, .Equal, "=");
- const cast_node = try rp.c.createBuiltinCall("@enumToInt", 1);
- const enum_ident = try transCreateNodeIdentifier(c, name);
- const period_tok = try appendToken(c, .Period, ".");
- const field_ident = try transCreateNodeIdentifier(c, field_name);
- const field_access_node = try c.arena.create(ast.Node.SimpleInfixOp);
- field_access_node.* = .{
- .base = .{ .tag = .Period },
- .op_token = period_tok,
- .lhs = enum_ident,
- .rhs = field_ident,
- };
- cast_node.params()[0] = &field_access_node.base;
- cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- const tld_init_node = &cast_node.base;
- const tld_semicolon_token = try appendToken(c, .Semicolon, ";");
- const tld_node = try ast.Node.VarDecl.create(c.arena, .{
- .name_token = tld_name_tok,
- .mut_token = tld_mut_tok,
- .semicolon_token = tld_semicolon_token,
- }, .{
- .visib_token = tld_visib_tok,
- .eq_token = tld_eq_token,
- .init_node = tld_init_node,
- });
- try addTopLevelDecl(c, field_name, &tld_node.base);
- }
- // make non exhaustive
- const field_node = try c.arena.create(ast.Node.ContainerField);
- field_node.* = .{
- .doc_comments = null,
- .comptime_token = null,
- .name_token = try appendIdentifier(c, "_"),
- .type_expr = null,
- .value_expr = null,
- .align_expr = null,
- };
-
- try fields_and_decls.append(&field_node.base);
- _ = try appendToken(c, .Comma, ",");
- const container_node = try ast.Node.ContainerDecl.alloc(c.arena, fields_and_decls.items.len);
- container_node.* = .{
- .layout_token = extern_tok,
- .kind_token = container_tok,
- .init_arg_expr = init_arg_expr,
- .fields_and_decls_len = fields_and_decls.items.len,
- .lbrace_token = lbrace_token,
- .rbrace_token = try appendToken(c, .RBrace, "}"),
- };
- mem.copy(*ast.Node, container_node.fieldsAndDecls(), fields_and_decls.items);
- break :blk &container_node.base;
- } else
- try transCreateNodeOpaqueType(c);
-
- const semicolon_token = try appendToken(c, .Semicolon, ";");
- const node = try ast.Node.VarDecl.create(c.arena, .{
- .name_token = name_tok,
- .mut_token = mut_tok,
- .semicolon_token = semicolon_token,
- }, .{
- .visib_token = visib_tok,
- .eq_token = eq_token,
- .init_node = init_node,
- });
-
- try addTopLevelDecl(c, name, &node.base);
- if (!is_unnamed)
- try c.alias_list.append(.{ .alias = bare_name, .name = name });
- return transCreateNodeIdentifier(c, name);
-}
-
-fn createAlias(c: *Context, alias: anytype) !void {
- const visib_tok = try appendToken(c, .Keyword_pub, "pub");
- const mut_tok = try appendToken(c, .Keyword_const, "const");
- const name_tok = try appendIdentifier(c, alias.alias);
- const eq_token = try appendToken(c, .Equal, "=");
- const init_node = try transCreateNodeIdentifier(c, alias.name);
- const semicolon_token = try appendToken(c, .Semicolon, ";");
-
- const node = try ast.Node.VarDecl.create(c.arena, .{
- .name_token = name_tok,
- .mut_token = mut_tok,
- .semicolon_token = semicolon_token,
- }, .{
- .visib_token = visib_tok,
- .eq_token = eq_token,
- .init_node = init_node,
- });
- return addTopLevelDecl(c, alias.alias, &node.base);
-}
-
-const ResultUsed = enum {
- used,
- unused,
-};
-
-const LRValue = enum {
- l_value,
- r_value,
-};
-
-fn transStmt(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangStmt,
- result_used: ResultUsed,
- lrvalue: LRValue,
-) TransError!*ast.Node {
- const sc = ZigClangStmt_getStmtClass(stmt);
- switch (sc) {
- .BinaryOperatorClass => return transBinaryOperator(rp, scope, @ptrCast(*const ZigClangBinaryOperator, stmt), result_used),
- .CompoundStmtClass => return transCompoundStmt(rp, scope, @ptrCast(*const ZigClangCompoundStmt, stmt)),
- .CStyleCastExprClass => return transCStyleCastExprClass(rp, scope, @ptrCast(*const ZigClangCStyleCastExpr, stmt), result_used, lrvalue),
- .DeclStmtClass => return transDeclStmt(rp, scope, @ptrCast(*const ZigClangDeclStmt, stmt)),
- .DeclRefExprClass => return transDeclRefExpr(rp, scope, @ptrCast(*const ZigClangDeclRefExpr, stmt), lrvalue),
- .ImplicitCastExprClass => return transImplicitCastExpr(rp, scope, @ptrCast(*const ZigClangImplicitCastExpr, stmt), result_used),
- .IntegerLiteralClass => return transIntegerLiteral(rp, scope, @ptrCast(*const ZigClangIntegerLiteral, stmt), result_used, .with_as),
- .ReturnStmtClass => return transReturnStmt(rp, scope, @ptrCast(*const ZigClangReturnStmt, stmt)),
- .StringLiteralClass => return transStringLiteral(rp, scope, @ptrCast(*const ZigClangStringLiteral, stmt), result_used),
- .ParenExprClass => {
- const expr = try transExpr(rp, scope, ZigClangParenExpr_getSubExpr(@ptrCast(*const ZigClangParenExpr, stmt)), .used, lrvalue);
- if (expr.tag == .GroupedExpression) return maybeSuppressResult(rp, scope, result_used, expr);
- const node = try rp.c.arena.create(ast.Node.GroupedExpression);
- node.* = .{
- .lparen = try appendToken(rp.c, .LParen, "("),
- .expr = expr,
- .rparen = try appendToken(rp.c, .RParen, ")"),
- };
- return maybeSuppressResult(rp, scope, result_used, &node.base);
- },
- .InitListExprClass => return transInitListExpr(rp, scope, @ptrCast(*const ZigClangInitListExpr, stmt), result_used),
- .ImplicitValueInitExprClass => return transImplicitValueInitExpr(rp, scope, @ptrCast(*const ZigClangExpr, stmt), result_used),
- .IfStmtClass => return transIfStmt(rp, scope, @ptrCast(*const ZigClangIfStmt, stmt)),
- .WhileStmtClass => return transWhileLoop(rp, scope, @ptrCast(*const ZigClangWhileStmt, stmt)),
- .DoStmtClass => return transDoWhileLoop(rp, scope, @ptrCast(*const ZigClangDoStmt, stmt)),
- .NullStmtClass => {
- const block = try rp.c.createBlock(0);
- block.rbrace = try appendToken(rp.c, .RBrace, "}");
- return &block.base;
- },
- .ContinueStmtClass => return try transCreateNodeContinue(rp.c),
- .BreakStmtClass => return transBreak(rp, scope),
- .ForStmtClass => return transForLoop(rp, scope, @ptrCast(*const ZigClangForStmt, stmt)),
- .FloatingLiteralClass => return transFloatingLiteral(rp, scope, @ptrCast(*const ZigClangFloatingLiteral, stmt), result_used),
- .ConditionalOperatorClass => {
- return transConditionalOperator(rp, scope, @ptrCast(*const ZigClangConditionalOperator, stmt), result_used);
- },
- .BinaryConditionalOperatorClass => {
- return transBinaryConditionalOperator(rp, scope, @ptrCast(*const ZigClangBinaryConditionalOperator, stmt), result_used);
- },
- .SwitchStmtClass => return transSwitch(rp, scope, @ptrCast(*const ZigClangSwitchStmt, stmt)),
- .CaseStmtClass => return transCase(rp, scope, @ptrCast(*const ZigClangCaseStmt, stmt)),
- .DefaultStmtClass => return transDefault(rp, scope, @ptrCast(*const ZigClangDefaultStmt, stmt)),
- .ConstantExprClass => return transConstantExpr(rp, scope, @ptrCast(*const ZigClangExpr, stmt), result_used),
- .PredefinedExprClass => return transPredefinedExpr(rp, scope, @ptrCast(*const ZigClangPredefinedExpr, stmt), result_used),
- .CharacterLiteralClass => return transCharLiteral(rp, scope, @ptrCast(*const ZigClangCharacterLiteral, stmt), result_used, .with_as),
- .StmtExprClass => return transStmtExpr(rp, scope, @ptrCast(*const ZigClangStmtExpr, stmt), result_used),
- .MemberExprClass => return transMemberExpr(rp, scope, @ptrCast(*const ZigClangMemberExpr, stmt), result_used),
- .ArraySubscriptExprClass => return transArrayAccess(rp, scope, @ptrCast(*const ZigClangArraySubscriptExpr, stmt), result_used),
- .CallExprClass => return transCallExpr(rp, scope, @ptrCast(*const ZigClangCallExpr, stmt), result_used),
- .UnaryExprOrTypeTraitExprClass => return transUnaryExprOrTypeTraitExpr(rp, scope, @ptrCast(*const ZigClangUnaryExprOrTypeTraitExpr, stmt), result_used),
- .UnaryOperatorClass => return transUnaryOperator(rp, scope, @ptrCast(*const ZigClangUnaryOperator, stmt), result_used),
- .CompoundAssignOperatorClass => return transCompoundAssignOperator(rp, scope, @ptrCast(*const ZigClangCompoundAssignOperator, stmt), result_used),
- .OpaqueValueExprClass => {
- const source_expr = ZigClangOpaqueValueExpr_getSourceExpr(@ptrCast(*const ZigClangOpaqueValueExpr, stmt)).?;
- const expr = try transExpr(rp, scope, source_expr, .used, lrvalue);
- if (expr.tag == .GroupedExpression) return maybeSuppressResult(rp, scope, result_used, expr);
- const node = try rp.c.arena.create(ast.Node.GroupedExpression);
- node.* = .{
- .lparen = try appendToken(rp.c, .LParen, "("),
- .expr = expr,
- .rparen = try appendToken(rp.c, .RParen, ")"),
- };
- return maybeSuppressResult(rp, scope, result_used, &node.base);
- },
- else => {
- return revertAndWarn(
- rp,
- error.UnsupportedTranslation,
- ZigClangStmt_getBeginLoc(stmt),
- "TODO implement translation of stmt class {}",
- .{@tagName(sc)},
- );
- },
- }
-}
-
-fn transBinaryOperator(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangBinaryOperator,
- result_used: ResultUsed,
-) TransError!*ast.Node {
- const op = ZigClangBinaryOperator_getOpcode(stmt);
- const qt = ZigClangBinaryOperator_getType(stmt);
- var op_token: ast.TokenIndex = undefined;
- var op_id: ast.Node.Tag = undefined;
- switch (op) {
- .Assign => return try transCreateNodeAssign(rp, scope, result_used, ZigClangBinaryOperator_getLHS(stmt), ZigClangBinaryOperator_getRHS(stmt)),
- .Comma => {
- const block_scope = try scope.findBlockScope(rp.c);
- const expr = block_scope.base.parent == scope;
- const lparen = if (expr) try appendToken(rp.c, .LParen, "(") else undefined;
-
- const lhs = try transExpr(rp, &block_scope.base, ZigClangBinaryOperator_getLHS(stmt), .unused, .r_value);
- try block_scope.statements.append(lhs);
-
- const rhs = try transExpr(rp, &block_scope.base, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value);
- if (expr) {
- _ = try appendToken(rp.c, .Semicolon, ";");
- const break_node = try transCreateNodeBreak(rp.c, block_scope.label, rhs);
- try block_scope.statements.append(&break_node.base);
- const block_node = try block_scope.complete(rp.c);
- const rparen = try appendToken(rp.c, .RParen, ")");
- const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
- grouped_expr.* = .{
- .lparen = lparen,
- .expr = block_node,
- .rparen = rparen,
- };
- return maybeSuppressResult(rp, scope, result_used, &grouped_expr.base);
- } else {
- return maybeSuppressResult(rp, scope, result_used, rhs);
- }
- },
- .Div => {
- if (cIsSignedInteger(qt)) {
- // signed integer division uses @divTrunc
- const div_trunc_node = try rp.c.createBuiltinCall("@divTrunc", 2);
- div_trunc_node.params()[0] = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value);
- _ = try appendToken(rp.c, .Comma, ",");
- const rhs = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value);
- div_trunc_node.params()[1] = rhs;
- div_trunc_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- return maybeSuppressResult(rp, scope, result_used, &div_trunc_node.base);
- }
- },
- .Rem => {
- if (cIsSignedInteger(qt)) {
- // signed integer division uses @rem
- const rem_node = try rp.c.createBuiltinCall("@rem", 2);
- rem_node.params()[0] = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value);
- _ = try appendToken(rp.c, .Comma, ",");
- const rhs = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value);
- rem_node.params()[1] = rhs;
- rem_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- return maybeSuppressResult(rp, scope, result_used, &rem_node.base);
- }
- },
- .Shl => {
- const node = try transCreateNodeShiftOp(rp, scope, stmt, .BitShiftLeft, .AngleBracketAngleBracketLeft, "<<");
- return maybeSuppressResult(rp, scope, result_used, node);
- },
- .Shr => {
- const node = try transCreateNodeShiftOp(rp, scope, stmt, .BitShiftRight, .AngleBracketAngleBracketRight, ">>");
- return maybeSuppressResult(rp, scope, result_used, node);
- },
- .LAnd => {
- const node = try transCreateNodeBoolInfixOp(rp, scope, stmt, .BoolAnd, result_used, true);
- return maybeSuppressResult(rp, scope, result_used, node);
- },
- .LOr => {
- const node = try transCreateNodeBoolInfixOp(rp, scope, stmt, .BoolOr, result_used, true);
- return maybeSuppressResult(rp, scope, result_used, node);
- },
- else => {},
- }
- const lhs_node = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value);
- switch (op) {
- .Add => {
- if (cIsUnsignedInteger(qt)) {
- op_token = try appendToken(rp.c, .PlusPercent, "+%");
- op_id = .AddWrap;
- } else {
- op_token = try appendToken(rp.c, .Plus, "+");
- op_id = .Add;
- }
- },
- .Sub => {
- if (cIsUnsignedInteger(qt)) {
- op_token = try appendToken(rp.c, .MinusPercent, "-%");
- op_id = .SubWrap;
- } else {
- op_token = try appendToken(rp.c, .Minus, "-");
- op_id = .Sub;
- }
- },
- .Mul => {
- if (cIsUnsignedInteger(qt)) {
- op_token = try appendToken(rp.c, .AsteriskPercent, "*%");
- op_id = .MulWrap;
- } else {
- op_token = try appendToken(rp.c, .Asterisk, "*");
- op_id = .Mul;
- }
- },
- .Div => {
- // unsigned/float division uses the operator
- op_id = .Div;
- op_token = try appendToken(rp.c, .Slash, "/");
- },
- .Rem => {
- // unsigned/float division uses the operator
- op_id = .Mod;
- op_token = try appendToken(rp.c, .Percent, "%");
- },
- .LT => {
- op_id = .LessThan;
- op_token = try appendToken(rp.c, .AngleBracketLeft, "<");
- },
- .GT => {
- op_id = .GreaterThan;
- op_token = try appendToken(rp.c, .AngleBracketRight, ">");
- },
- .LE => {
- op_id = .LessOrEqual;
- op_token = try appendToken(rp.c, .AngleBracketLeftEqual, "<=");
- },
- .GE => {
- op_id = .GreaterOrEqual;
- op_token = try appendToken(rp.c, .AngleBracketRightEqual, ">=");
- },
- .EQ => {
- op_id = .EqualEqual;
- op_token = try appendToken(rp.c, .EqualEqual, "==");
- },
- .NE => {
- op_id = .BangEqual;
- op_token = try appendToken(rp.c, .BangEqual, "!=");
- },
- .And => {
- op_id = .BitAnd;
- op_token = try appendToken(rp.c, .Ampersand, "&");
- },
- .Xor => {
- op_id = .BitXor;
- op_token = try appendToken(rp.c, .Caret, "^");
- },
- .Or => {
- op_id = .BitOr;
- op_token = try appendToken(rp.c, .Pipe, "|");
- },
- else => unreachable,
- }
-
- const rhs_node = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value);
-
- const lhs = if (isBoolRes(lhs_node)) init: {
- const cast_node = try rp.c.createBuiltinCall("@boolToInt", 1);
- cast_node.params()[0] = lhs_node;
- cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- break :init &cast_node.base;
- } else lhs_node;
-
- const rhs = if (isBoolRes(rhs_node)) init: {
- const cast_node = try rp.c.createBuiltinCall("@boolToInt", 1);
- cast_node.params()[0] = rhs_node;
- cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- break :init &cast_node.base;
- } else rhs_node;
-
- return transCreateNodeInfixOp(rp, scope, lhs, op_id, op_token, rhs, result_used, true);
-}
-
-fn transCompoundStmtInline(
- rp: RestorePoint,
- parent_scope: *Scope,
- stmt: *const ZigClangCompoundStmt,
- block: *Scope.Block,
-) TransError!void {
- var it = ZigClangCompoundStmt_body_begin(stmt);
- const end_it = ZigClangCompoundStmt_body_end(stmt);
- while (it != end_it) : (it += 1) {
- const result = try transStmt(rp, parent_scope, it[0], .unused, .r_value);
- try block.statements.append(result);
- }
-}
-
-fn transCompoundStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCompoundStmt) TransError!*ast.Node {
- var block_scope = try Scope.Block.init(rp.c, scope, false);
- defer block_scope.deinit();
- try transCompoundStmtInline(rp, &block_scope.base, stmt, &block_scope);
- return try block_scope.complete(rp.c);
-}
-
-fn transCStyleCastExprClass(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangCStyleCastExpr,
- result_used: ResultUsed,
- lrvalue: LRValue,
-) TransError!*ast.Node {
- const sub_expr = ZigClangCStyleCastExpr_getSubExpr(stmt);
- const cast_node = (try transCCast(
- rp,
- scope,
- ZigClangCStyleCastExpr_getBeginLoc(stmt),
- ZigClangCStyleCastExpr_getType(stmt),
- ZigClangExpr_getType(sub_expr),
- try transExpr(rp, scope, sub_expr, .used, lrvalue),
- ));
- return maybeSuppressResult(rp, scope, result_used, cast_node);
-}
-
-fn transDeclStmtOne(
- rp: RestorePoint,
- scope: *Scope,
- decl: *const ZigClangDecl,
- block_scope: *Scope.Block,
-) TransError!*ast.Node {
- const c = rp.c;
-
- switch (ZigClangDecl_getKind(decl)) {
- .Var => {
- const var_decl = @ptrCast(*const ZigClangVarDecl, decl);
-
- const qual_type = ZigClangVarDecl_getTypeSourceInfo_getType(var_decl);
- const name = try c.str(ZigClangNamedDecl_getName_bytes_begin(
- @ptrCast(*const ZigClangNamedDecl, var_decl),
- ));
- const mangled_name = try block_scope.makeMangledName(c, name);
-
- switch (ZigClangVarDecl_getStorageClass(var_decl)) {
- .Extern, .Static => {
- // This is actually a global variable, put it in the global scope and reference it.
- // `_ = mangled_name;`
- try visitVarDecl(rp.c, var_decl, mangled_name);
- return try maybeSuppressResult(rp, scope, .unused, try transCreateNodeIdentifier(rp.c, mangled_name));
- },
- else => {},
- }
-
- const mut_tok = if (ZigClangQualType_isConstQualified(qual_type))
- try appendToken(c, .Keyword_const, "const")
- else
- try appendToken(c, .Keyword_var, "var");
- const name_tok = try appendIdentifier(c, mangled_name);
-
- _ = try appendToken(c, .Colon, ":");
- const loc = ZigClangDecl_getLocation(decl);
- const type_node = try transQualType(rp, qual_type, loc);
-
- const eq_token = try appendToken(c, .Equal, "=");
- var init_node = if (ZigClangVarDecl_getInit(var_decl)) |expr|
- try transExprCoercing(rp, scope, expr, .used, .r_value)
- else
- try transCreateNodeUndefinedLiteral(c);
- if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node)) {
- const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1);
- builtin_node.params()[0] = init_node;
- builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- init_node = &builtin_node.base;
- }
- const semicolon_token = try appendToken(c, .Semicolon, ";");
- const node = try ast.Node.VarDecl.create(c.arena, .{
- .name_token = name_tok,
- .mut_token = mut_tok,
- .semicolon_token = semicolon_token,
- }, .{
- .eq_token = eq_token,
- .type_node = type_node,
- .init_node = init_node,
- });
- return &node.base;
- },
- .Typedef => {
- const typedef_decl = @ptrCast(*const ZigClangTypedefNameDecl, decl);
- const name = try c.str(ZigClangNamedDecl_getName_bytes_begin(
- @ptrCast(*const ZigClangNamedDecl, typedef_decl),
- ));
-
- const underlying_qual = ZigClangTypedefNameDecl_getUnderlyingType(typedef_decl);
- const underlying_type = ZigClangQualType_getTypePtr(underlying_qual);
-
- const mangled_name = try block_scope.makeMangledName(c, name);
- const node = (try transCreateNodeTypedef(rp, typedef_decl, false, mangled_name)) orelse
- return error.UnsupportedTranslation;
- return node;
- },
- else => |kind| return revertAndWarn(
- rp,
- error.UnsupportedTranslation,
- ZigClangDecl_getLocation(decl),
- "TODO implement translation of DeclStmt kind {}",
- .{@tagName(kind)},
- ),
- }
-}
-
-fn transDeclStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangDeclStmt) TransError!*ast.Node {
- const block_scope = scope.findBlockScope(rp.c) catch unreachable;
-
- var it = ZigClangDeclStmt_decl_begin(stmt);
- const end_it = ZigClangDeclStmt_decl_end(stmt);
- assert(it != end_it);
- while (true) : (it += 1) {
- const node = try transDeclStmtOne(rp, scope, it[0], block_scope);
-
- if (it + 1 == end_it) {
- return node;
- } else {
- try block_scope.statements.append(node);
- }
- }
- unreachable;
-}
-
-fn transDeclRefExpr(
- rp: RestorePoint,
- scope: *Scope,
- expr: *const ZigClangDeclRefExpr,
- lrvalue: LRValue,
-) TransError!*ast.Node {
- const value_decl = ZigClangDeclRefExpr_getDecl(expr);
- const name = try rp.c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, value_decl)));
- const mangled_name = scope.getAlias(name);
- return transCreateNodeIdentifier(rp.c, mangled_name);
-}
-
-fn transImplicitCastExpr(
- rp: RestorePoint,
- scope: *Scope,
- expr: *const ZigClangImplicitCastExpr,
- result_used: ResultUsed,
-) TransError!*ast.Node {
- const c = rp.c;
- const sub_expr = ZigClangImplicitCastExpr_getSubExpr(expr);
- const dest_type = getExprQualType(c, @ptrCast(*const ZigClangExpr, expr));
- const src_type = getExprQualType(c, sub_expr);
- switch (ZigClangImplicitCastExpr_getCastKind(expr)) {
- .BitCast, .FloatingCast, .FloatingToIntegral, .IntegralToFloating, .IntegralCast, .PointerToIntegral, .IntegralToPointer => {
- const sub_expr_node = try transExpr(rp, scope, sub_expr, .used, .r_value);
- return try transCCast(rp, scope, ZigClangImplicitCastExpr_getBeginLoc(expr), dest_type, src_type, sub_expr_node);
- },
- .LValueToRValue, .NoOp, .FunctionToPointerDecay => {
- const sub_expr_node = try transExpr(rp, scope, sub_expr, .used, .r_value);
- return maybeSuppressResult(rp, scope, result_used, sub_expr_node);
- },
- .ArrayToPointerDecay => {
- if (exprIsStringLiteral(sub_expr)) {
- const sub_expr_node = try transExpr(rp, scope, sub_expr, .used, .r_value);
- return maybeSuppressResult(rp, scope, result_used, sub_expr_node);
- }
-
- const prefix_op = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
- prefix_op.rhs = try transExpr(rp, scope, sub_expr, .used, .r_value);
-
- return maybeSuppressResult(rp, scope, result_used, &prefix_op.base);
- },
- .NullToPointer => {
- return try transCreateNodeNullLiteral(rp.c);
- },
- .PointerToBoolean => {
- // @ptrToInt(val) != 0
- const ptr_to_int = try rp.c.createBuiltinCall("@ptrToInt", 1);
- ptr_to_int.params()[0] = try transExpr(rp, scope, sub_expr, .used, .r_value);
- ptr_to_int.rparen_token = try appendToken(rp.c, .RParen, ")");
-
- const op_token = try appendToken(rp.c, .BangEqual, "!=");
- const rhs_node = try transCreateNodeInt(rp.c, 0);
- return transCreateNodeInfixOp(rp, scope, &ptr_to_int.base, .BangEqual, op_token, rhs_node, result_used, false);
- },
- .IntegralToBoolean => {
- const sub_expr_node = try transExpr(rp, scope, sub_expr, .used, .r_value);
-
- // The expression is already a boolean one, return it as-is
- if (isBoolRes(sub_expr_node))
- return sub_expr_node;
-
- // val != 0
- const op_token = try appendToken(rp.c, .BangEqual, "!=");
- const rhs_node = try transCreateNodeInt(rp.c, 0);
- return transCreateNodeInfixOp(rp, scope, sub_expr_node, .BangEqual, op_token, rhs_node, result_used, false);
- },
- else => |kind| return revertAndWarn(
- rp,
- error.UnsupportedTranslation,
- ZigClangStmt_getBeginLoc(@ptrCast(*const ZigClangStmt, expr)),
- "TODO implement translation of CastKind {}",
- .{@tagName(kind)},
- ),
- }
-}
-
-fn transBoolExpr(
- rp: RestorePoint,
- scope: *Scope,
- expr: *const ZigClangExpr,
- used: ResultUsed,
- lrvalue: LRValue,
- grouped: bool,
-) TransError!*ast.Node {
- if (ZigClangStmt_getStmtClass(@ptrCast(*const ZigClangStmt, expr)) == .IntegerLiteralClass) {
- var is_zero: bool = undefined;
- if (!ZigClangIntegerLiteral_isZero(@ptrCast(*const ZigClangIntegerLiteral, expr), &is_zero, rp.c.clang_context)) {
- return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangExpr_getBeginLoc(expr), "invalid integer literal", .{});
- }
- return try transCreateNodeBoolLiteral(rp.c, !is_zero);
- }
-
- const lparen = if (grouped)
- try appendToken(rp.c, .LParen, "(")
- else
- undefined;
- var res = try transExpr(rp, scope, expr, used, lrvalue);
-
- if (isBoolRes(res)) {
- if (!grouped and res.tag == .GroupedExpression) {
- const group = @fieldParentPtr(ast.Node.GroupedExpression, "base", res);
- res = group.expr;
- // get zig fmt to work properly
- tokenSlice(rp.c, group.lparen)[0] = ')';
- }
- return res;
- }
-
- const ty = ZigClangQualType_getTypePtr(getExprQualType(rp.c, expr));
- const node = try finishBoolExpr(rp, scope, ZigClangExpr_getBeginLoc(expr), ty, res, used);
-
- if (grouped) {
- const rparen = try appendToken(rp.c, .RParen, ")");
- const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
- grouped_expr.* = .{
- .lparen = lparen,
- .expr = node,
- .rparen = rparen,
- };
- return maybeSuppressResult(rp, scope, used, &grouped_expr.base);
- } else {
- return maybeSuppressResult(rp, scope, used, node);
- }
-}
-
-fn exprIsBooleanType(expr: *const ZigClangExpr) bool {
- return qualTypeIsBoolean(ZigClangExpr_getType(expr));
-}
-
-fn exprIsStringLiteral(expr: *const ZigClangExpr) bool {
- switch (ZigClangExpr_getStmtClass(expr)) {
- .StringLiteralClass => return true,
- .PredefinedExprClass => return true,
- .UnaryOperatorClass => {
- const op_expr = ZigClangUnaryOperator_getSubExpr(@ptrCast(*const ZigClangUnaryOperator, expr));
- return exprIsStringLiteral(op_expr);
- },
- else => return false,
- }
-}
-
-fn isBoolRes(res: *ast.Node) bool {
- switch (res.tag) {
- .BoolOr,
- .BoolAnd,
- .EqualEqual,
- .BangEqual,
- .LessThan,
- .GreaterThan,
- .LessOrEqual,
- .GreaterOrEqual,
- .BoolNot,
- .BoolLiteral,
- => return true,
-
- .GroupedExpression => return isBoolRes(@fieldParentPtr(ast.Node.GroupedExpression, "base", res).expr),
-
- else => return false,
- }
-}
-
-fn finishBoolExpr(
- rp: RestorePoint,
- scope: *Scope,
- loc: ZigClangSourceLocation,
- ty: *const ZigClangType,
- node: *ast.Node,
- used: ResultUsed,
-) TransError!*ast.Node {
- switch (ZigClangType_getTypeClass(ty)) {
- .Builtin => {
- const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
-
- switch (ZigClangBuiltinType_getKind(builtin_ty)) {
- .Bool => return node,
- .Char_U,
- .UChar,
- .Char_S,
- .SChar,
- .UShort,
- .UInt,
- .ULong,
- .ULongLong,
- .Short,
- .Int,
- .Long,
- .LongLong,
- .UInt128,
- .Int128,
- .Float,
- .Double,
- .Float128,
- .LongDouble,
- .WChar_U,
- .Char8,
- .Char16,
- .Char32,
- .WChar_S,
- .Float16,
- => {
- const op_token = try appendToken(rp.c, .BangEqual, "!=");
- const rhs_node = try transCreateNodeInt(rp.c, 0);
- return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false);
- },
- .NullPtr => {
- const op_token = try appendToken(rp.c, .EqualEqual, "==");
- const rhs_node = try transCreateNodeNullLiteral(rp.c);
- return transCreateNodeInfixOp(rp, scope, node, .EqualEqual, op_token, rhs_node, used, false);
- },
- else => {},
- }
- },
- .Pointer => {
- const op_token = try appendToken(rp.c, .BangEqual, "!=");
- const rhs_node = try transCreateNodeNullLiteral(rp.c);
- return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false);
- },
- .Typedef => {
- const typedef_ty = @ptrCast(*const ZigClangTypedefType, ty);
- const typedef_decl = ZigClangTypedefType_getDecl(typedef_ty);
- const underlying_type = ZigClangTypedefNameDecl_getUnderlyingType(typedef_decl);
- return finishBoolExpr(rp, scope, loc, ZigClangQualType_getTypePtr(underlying_type), node, used);
- },
- .Enum => {
- const op_token = try appendToken(rp.c, .BangEqual, "!=");
- const rhs_node = try transCreateNodeInt(rp.c, 0);
- return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false);
- },
- .Elaborated => {
- const elaborated_ty = @ptrCast(*const ZigClangElaboratedType, ty);
- const named_type = ZigClangElaboratedType_getNamedType(elaborated_ty);
- return finishBoolExpr(rp, scope, loc, ZigClangQualType_getTypePtr(named_type), node, used);
- },
- else => {},
- }
- return revertAndWarn(rp, error.UnsupportedType, loc, "unsupported bool expression type", .{});
-}
-
-const SuppressCast = enum {
- with_as,
- no_as,
-};
-fn transIntegerLiteral(
- rp: RestorePoint,
- scope: *Scope,
- expr: *const ZigClangIntegerLiteral,
- result_used: ResultUsed,
- suppress_as: SuppressCast,
-) TransError!*ast.Node {
- var eval_result: ZigClangExprEvalResult = undefined;
- if (!ZigClangIntegerLiteral_EvaluateAsInt(expr, &eval_result, rp.c.clang_context)) {
- const loc = ZigClangIntegerLiteral_getBeginLoc(expr);
- return revertAndWarn(rp, error.UnsupportedTranslation, loc, "invalid integer literal", .{});
- }
-
- if (suppress_as == .no_as) {
- const int_lit_node = try transCreateNodeAPInt(rp.c, ZigClangAPValue_getInt(&eval_result.Val));
- return maybeSuppressResult(rp, scope, result_used, int_lit_node);
- }
-
- // Integer literals in C have types, and this can matter for several reasons.
- // For example, this is valid C:
- // unsigned char y = 256;
- // How this gets evaluated is the 256 is an integer, which gets truncated to signed char, then bit-casted
- // to unsigned char, resulting in 0. In order for this to work, we have to emit this zig code:
- // var y = @bitCast(u8, @truncate(i8, @as(c_int, 256)));
- // Ideally in translate-c we could flatten this out to simply:
- // var y: u8 = 0;
- // But the first step is to be correct, and the next step is to make the output more elegant.
-
- // @as(T, x)
- const expr_base = @ptrCast(*const ZigClangExpr, expr);
- const as_node = try rp.c.createBuiltinCall("@as", 2);
- const ty_node = try transQualType(rp, ZigClangExpr_getType(expr_base), ZigClangExpr_getBeginLoc(expr_base));
- as_node.params()[0] = ty_node;
- _ = try appendToken(rp.c, .Comma, ",");
- as_node.params()[1] = try transCreateNodeAPInt(rp.c, ZigClangAPValue_getInt(&eval_result.Val));
-
- as_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- return maybeSuppressResult(rp, scope, result_used, &as_node.base);
-}
-
-fn transReturnStmt(
- rp: RestorePoint,
- scope: *Scope,
- expr: *const ZigClangReturnStmt,
-) TransError!*ast.Node {
- const return_kw = try appendToken(rp.c, .Keyword_return, "return");
- const rhs: ?*ast.Node = if (ZigClangReturnStmt_getRetValue(expr)) |val_expr|
- try transExprCoercing(rp, scope, val_expr, .used, .r_value)
- else
- null;
- const return_expr = try ast.Node.ControlFlowExpression.create(rp.c.arena, .{
- .ltoken = return_kw,
- .tag = .Return,
- }, .{
- .rhs = rhs,
- });
- _ = try appendToken(rp.c, .Semicolon, ";");
- return &return_expr.base;
-}
-
-fn transStringLiteral(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangStringLiteral,
- result_used: ResultUsed,
-) TransError!*ast.Node {
- const kind = ZigClangStringLiteral_getKind(stmt);
- switch (kind) {
- .Ascii, .UTF8 => {
- var len: usize = undefined;
- const bytes_ptr = ZigClangStringLiteral_getString_bytes_begin_size(stmt, &len);
- const str = bytes_ptr[0..len];
-
- var char_buf: [4]u8 = undefined;
- len = 0;
- for (str) |c| len += escapeChar(c, &char_buf).len;
-
- const buf = try rp.c.arena.alloc(u8, len + "\"\"".len);
- buf[0] = '"';
- writeEscapedString(buf[1..], str);
- buf[buf.len - 1] = '"';
-
- const token = try appendToken(rp.c, .StringLiteral, buf);
- const node = try rp.c.arena.create(ast.Node.OneToken);
- node.* = .{
- .base = .{ .tag = .StringLiteral },
- .token = token,
- };
- return maybeSuppressResult(rp, scope, result_used, &node.base);
- },
- .UTF16, .UTF32, .Wide => return revertAndWarn(
- rp,
- error.UnsupportedTranslation,
- ZigClangStmt_getBeginLoc(@ptrCast(*const ZigClangStmt, stmt)),
- "TODO: support string literal kind {}",
- .{kind},
- ),
- }
-}
-
-fn escapedStringLen(s: []const u8) usize {
- var len: usize = 0;
- var char_buf: [4]u8 = undefined;
- for (s) |c| len += escapeChar(c, &char_buf).len;
- return len;
-}
-
-fn writeEscapedString(buf: []u8, s: []const u8) void {
- var char_buf: [4]u8 = undefined;
- var i: usize = 0;
- for (s) |c| {
- const escaped = escapeChar(c, &char_buf);
- mem.copy(u8, buf[i..], escaped);
- i += escaped.len;
- }
-}
-
-// Returns either a string literal or a slice of `buf`.
-fn escapeChar(c: u8, char_buf: *[4]u8) []const u8 {
- return switch (c) {
- '\"' => "\\\"",
- '\'' => "\\'",
- '\\' => "\\\\",
- '\n' => "\\n",
- '\r' => "\\r",
- '\t' => "\\t",
- // Handle the remaining escapes Zig doesn't support by turning them
- // into their respective hex representation
- else => if (std.ascii.isCntrl(c))
- std.fmt.bufPrint(char_buf, "\\x{x:0<2}", .{c}) catch unreachable
- else
- std.fmt.bufPrint(char_buf, "{c}", .{c}) catch unreachable,
- };
-}
-
-fn transCCast(
- rp: RestorePoint,
- scope: *Scope,
- loc: ZigClangSourceLocation,
- dst_type: ZigClangQualType,
- src_type: ZigClangQualType,
- expr: *ast.Node,
-) !*ast.Node {
- if (ZigClangType_isVoidType(qualTypeCanon(dst_type))) return expr;
- if (ZigClangQualType_eq(dst_type, src_type)) return expr;
- if (qualTypeIsPtr(dst_type) and qualTypeIsPtr(src_type))
- return transCPtrCast(rp, loc, dst_type, src_type, expr);
- if (cIsInteger(dst_type) and cIsInteger(src_type)) {
- // 1. Extend or truncate without changing signed-ness.
- // 2. Bit-cast to correct signed-ness
-
- // @bitCast(dest_type, intermediate_value)
- const cast_node = try rp.c.createBuiltinCall("@bitCast", 2);
- cast_node.params()[0] = try transQualType(rp, dst_type, loc);
- _ = try appendToken(rp.c, .Comma, ",");
-
- switch (cIntTypeCmp(dst_type, src_type)) {
- .lt => {
- // @truncate(SameSignSmallerInt, src_type)
- const trunc_node = try rp.c.createBuiltinCall("@truncate", 2);
- const ty_node = try transQualTypeIntWidthOf(rp.c, dst_type, cIsSignedInteger(src_type));
- trunc_node.params()[0] = ty_node;
- _ = try appendToken(rp.c, .Comma, ",");
- trunc_node.params()[1] = expr;
- trunc_node.rparen_token = try appendToken(rp.c, .RParen, ")");
-
- cast_node.params()[1] = &trunc_node.base;
- },
- .gt => {
- // @as(SameSignBiggerInt, src_type)
- const as_node = try rp.c.createBuiltinCall("@as", 2);
- const ty_node = try transQualTypeIntWidthOf(rp.c, dst_type, cIsSignedInteger(src_type));
- as_node.params()[0] = ty_node;
- _ = try appendToken(rp.c, .Comma, ",");
- as_node.params()[1] = expr;
- as_node.rparen_token = try appendToken(rp.c, .RParen, ")");
-
- cast_node.params()[1] = &as_node.base;
- },
- .eq => {
- cast_node.params()[1] = expr;
- },
- }
- cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- return &cast_node.base;
- }
- if (cIsInteger(dst_type) and qualTypeIsPtr(src_type)) {
- // @intCast(dest_type, @ptrToInt(val))
- const cast_node = try rp.c.createBuiltinCall("@intCast", 2);
- cast_node.params()[0] = try transQualType(rp, dst_type, loc);
- _ = try appendToken(rp.c, .Comma, ",");
- const builtin_node = try rp.c.createBuiltinCall("@ptrToInt", 1);
- builtin_node.params()[0] = expr;
- builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- cast_node.params()[1] = &builtin_node.base;
- cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- return &cast_node.base;
- }
- if (cIsInteger(src_type) and qualTypeIsPtr(dst_type)) {
- // @intToPtr(dest_type, val)
- const builtin_node = try rp.c.createBuiltinCall("@intToPtr", 2);
- builtin_node.params()[0] = try transQualType(rp, dst_type, loc);
- _ = try appendToken(rp.c, .Comma, ",");
- builtin_node.params()[1] = expr;
- builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- return &builtin_node.base;
- }
- if (cIsFloating(src_type) and cIsFloating(dst_type)) {
- const builtin_node = try rp.c.createBuiltinCall("@floatCast", 2);
- builtin_node.params()[0] = try transQualType(rp, dst_type, loc);
- _ = try appendToken(rp.c, .Comma, ",");
- builtin_node.params()[1] = expr;
- builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- return &builtin_node.base;
- }
- if (cIsFloating(src_type) and !cIsFloating(dst_type)) {
- const builtin_node = try rp.c.createBuiltinCall("@floatToInt", 2);
- builtin_node.params()[0] = try transQualType(rp, dst_type, loc);
- _ = try appendToken(rp.c, .Comma, ",");
- builtin_node.params()[1] = expr;
- builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- return &builtin_node.base;
- }
- if (!cIsFloating(src_type) and cIsFloating(dst_type)) {
- const builtin_node = try rp.c.createBuiltinCall("@intToFloat", 2);
- builtin_node.params()[0] = try transQualType(rp, dst_type, loc);
- _ = try appendToken(rp.c, .Comma, ",");
- builtin_node.params()[1] = expr;
- builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- return &builtin_node.base;
- }
- if (ZigClangType_isBooleanType(qualTypeCanon(src_type)) and
- !ZigClangType_isBooleanType(qualTypeCanon(dst_type)))
- {
- // @boolToInt returns either a comptime_int or a u1
- const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1);
- builtin_node.params()[0] = expr;
- builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
-
- const inner_cast_node = try rp.c.createBuiltinCall("@intCast", 2);
- inner_cast_node.params()[0] = try transCreateNodeIdentifier(rp.c, "u1");
- _ = try appendToken(rp.c, .Comma, ",");
- inner_cast_node.params()[1] = &builtin_node.base;
- inner_cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
-
- const cast_node = try rp.c.createBuiltinCall("@intCast", 2);
- cast_node.params()[0] = try transQualType(rp, dst_type, loc);
- _ = try appendToken(rp.c, .Comma, ",");
-
- if (cIsSignedInteger(dst_type)) {
- const bitcast_node = try rp.c.createBuiltinCall("@bitCast", 2);
- bitcast_node.params()[0] = try transCreateNodeIdentifier(rp.c, "i1");
- _ = try appendToken(rp.c, .Comma, ",");
- bitcast_node.params()[1] = &inner_cast_node.base;
- bitcast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- cast_node.params()[1] = &bitcast_node.base;
- } else {
- cast_node.params()[1] = &inner_cast_node.base;
- }
- cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
-
- return &cast_node.base;
- }
- if (ZigClangQualType_getTypeClass(ZigClangQualType_getCanonicalType(dst_type)) == .Enum) {
- const builtin_node = try rp.c.createBuiltinCall("@intToEnum", 2);
- builtin_node.params()[0] = try transQualType(rp, dst_type, loc);
- _ = try appendToken(rp.c, .Comma, ",");
- builtin_node.params()[1] = expr;
- builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- return &builtin_node.base;
- }
- if (ZigClangQualType_getTypeClass(ZigClangQualType_getCanonicalType(src_type)) == .Enum and
- ZigClangQualType_getTypeClass(ZigClangQualType_getCanonicalType(dst_type)) != .Enum)
- {
- const builtin_node = try rp.c.createBuiltinCall("@enumToInt", 1);
- builtin_node.params()[0] = expr;
- builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- return &builtin_node.base;
- }
- const cast_node = try rp.c.createBuiltinCall("@as", 2);
- cast_node.params()[0] = try transQualType(rp, dst_type, loc);
- _ = try appendToken(rp.c, .Comma, ",");
- cast_node.params()[1] = expr;
- cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- return &cast_node.base;
-}
-
-fn transExpr(
- rp: RestorePoint,
- scope: *Scope,
- expr: *const ZigClangExpr,
- used: ResultUsed,
- lrvalue: LRValue,
-) TransError!*ast.Node {
- return transStmt(rp, scope, @ptrCast(*const ZigClangStmt, expr), used, lrvalue);
-}
-
-/// Same as `transExpr` but with the knowledge that the operand will be type coerced, and therefore
-/// an `@as` would be redundant. This is used to prevent redundant `@as` in integer literals.
-fn transExprCoercing(
- rp: RestorePoint,
- scope: *Scope,
- expr: *const ZigClangExpr,
- used: ResultUsed,
- lrvalue: LRValue,
-) TransError!*ast.Node {
- switch (ZigClangStmt_getStmtClass(@ptrCast(*const ZigClangStmt, expr))) {
- .IntegerLiteralClass => {
- return transIntegerLiteral(rp, scope, @ptrCast(*const ZigClangIntegerLiteral, expr), .used, .no_as);
- },
- .CharacterLiteralClass => {
- return transCharLiteral(rp, scope, @ptrCast(*const ZigClangCharacterLiteral, expr), .used, .no_as);
- },
- .UnaryOperatorClass => {
- const un_expr = @ptrCast(*const ZigClangUnaryOperator, expr);
- if (ZigClangUnaryOperator_getOpcode(un_expr) == .Extension) {
- return transExprCoercing(rp, scope, ZigClangUnaryOperator_getSubExpr(un_expr), used, lrvalue);
- }
- },
- else => {},
- }
- return transExpr(rp, scope, expr, .used, .r_value);
-}
-
-fn transInitListExprRecord(
- rp: RestorePoint,
- scope: *Scope,
- loc: ZigClangSourceLocation,
- expr: *const ZigClangInitListExpr,
- ty: *const ZigClangType,
- used: ResultUsed,
-) TransError!*ast.Node {
- var is_union_type = false;
- // Unions and Structs are both represented as RecordDecl
- const record_ty = ZigClangType_getAsRecordType(ty) orelse
- blk: {
- is_union_type = true;
- break :blk ZigClangType_getAsUnionType(ty);
- } orelse unreachable;
- const record_decl = ZigClangRecordType_getDecl(record_ty);
- const record_def = ZigClangRecordDecl_getDefinition(record_decl) orelse
- unreachable;
-
- const ty_node = try transType(rp, ty, loc);
- const init_count = ZigClangInitListExpr_getNumInits(expr);
- var field_inits = std.ArrayList(*ast.Node).init(rp.c.gpa);
- defer field_inits.deinit();
-
- _ = try appendToken(rp.c, .LBrace, "{");
-
- var init_i: c_uint = 0;
- var it = ZigClangRecordDecl_field_begin(record_def);
- const end_it = ZigClangRecordDecl_field_end(record_def);
- while (ZigClangRecordDecl_field_iterator_neq(it, end_it)) : (it = ZigClangRecordDecl_field_iterator_next(it)) {
- const field_decl = ZigClangRecordDecl_field_iterator_deref(it);
-
- // The initializer for a union type has a single entry only
- if (is_union_type and field_decl != ZigClangInitListExpr_getInitializedFieldInUnion(expr)) {
- continue;
- }
-
- assert(init_i < init_count);
- const elem_expr = ZigClangInitListExpr_getInit(expr, init_i);
- init_i += 1;
-
- // Generate the field assignment expression:
- // .field_name = expr
- const period_tok = try appendToken(rp.c, .Period, ".");
-
- var raw_name = try rp.c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, field_decl)));
- if (ZigClangFieldDecl_isAnonymousStructOrUnion(field_decl)) {
- const name = rp.c.decl_table.get(@ptrToInt(ZigClangFieldDecl_getCanonicalDecl(field_decl))).?;
- raw_name = try mem.dupe(rp.c.arena, u8, name);
- }
- const field_name_tok = try appendIdentifier(rp.c, raw_name);
-
- _ = try appendToken(rp.c, .Equal, "=");
-
- const field_init_node = try rp.c.arena.create(ast.Node.FieldInitializer);
- field_init_node.* = .{
- .period_token = period_tok,
- .name_token = field_name_tok,
- .expr = try transExpr(rp, scope, elem_expr, .used, .r_value),
- };
-
- try field_inits.append(&field_init_node.base);
- _ = try appendToken(rp.c, .Comma, ",");
- }
-
- const node = try ast.Node.StructInitializer.alloc(rp.c.arena, field_inits.items.len);
- node.* = .{
- .lhs = ty_node,
- .rtoken = try appendToken(rp.c, .RBrace, "}"),
- .list_len = field_inits.items.len,
- };
- mem.copy(*ast.Node, node.list(), field_inits.items);
- return &node.base;
-}
-
-fn transCreateNodeArrayType(
- rp: RestorePoint,
- source_loc: ZigClangSourceLocation,
- ty: *const ZigClangType,
- len: anytype,
-) !*ast.Node {
- const node = try rp.c.arena.create(ast.Node.ArrayType);
- const op_token = try appendToken(rp.c, .LBracket, "[");
- const len_expr = try transCreateNodeInt(rp.c, len);
- _ = try appendToken(rp.c, .RBracket, "]");
- node.* = .{
- .op_token = op_token,
- .rhs = try transType(rp, ty, source_loc),
- .len_expr = len_expr,
- };
- return &node.base;
-}
-
-fn transInitListExprArray(
- rp: RestorePoint,
- scope: *Scope,
- loc: ZigClangSourceLocation,
- expr: *const ZigClangInitListExpr,
- ty: *const ZigClangType,
- used: ResultUsed,
-) TransError!*ast.Node {
- const arr_type = ZigClangType_getAsArrayTypeUnsafe(ty);
- const child_qt = ZigClangArrayType_getElementType(arr_type);
- const init_count = ZigClangInitListExpr_getNumInits(expr);
- assert(ZigClangType_isConstantArrayType(@ptrCast(*const ZigClangType, arr_type)));
- const const_arr_ty = @ptrCast(*const ZigClangConstantArrayType, arr_type);
- const size_ap_int = ZigClangConstantArrayType_getSize(const_arr_ty);
- const all_count = ZigClangAPInt_getLimitedValue(size_ap_int, math.maxInt(usize));
- const leftover_count = all_count - init_count;
-
- var init_node: *ast.Node.ArrayInitializer = undefined;
- var cat_tok: ast.TokenIndex = undefined;
- if (init_count != 0) {
- const ty_node = try transCreateNodeArrayType(
- rp,
- loc,
- ZigClangQualType_getTypePtr(child_qt),
- init_count,
- );
- _ = try appendToken(rp.c, .LBrace, "{");
- init_node = try ast.Node.ArrayInitializer.alloc(rp.c.arena, init_count);
- init_node.* = .{
- .lhs = ty_node,
- .rtoken = undefined,
- .list_len = init_count,
- };
- const init_list = init_node.list();
-
- var i: c_uint = 0;
- while (i < init_count) : (i += 1) {
- const elem_expr = ZigClangInitListExpr_getInit(expr, i);
- init_list[i] = try transExpr(rp, scope, elem_expr, .used, .r_value);
- _ = try appendToken(rp.c, .Comma, ",");
- }
- init_node.rtoken = try appendToken(rp.c, .RBrace, "}");
- if (leftover_count == 0) {
- return &init_node.base;
- }
- cat_tok = try appendToken(rp.c, .PlusPlus, "++");
- }
-
- const ty_node = try transCreateNodeArrayType(rp, loc, ZigClangQualType_getTypePtr(child_qt), 1);
- _ = try appendToken(rp.c, .LBrace, "{");
- const filler_init_node = try ast.Node.ArrayInitializer.alloc(rp.c.arena, 1);
- filler_init_node.* = .{
- .lhs = ty_node,
- .rtoken = undefined,
- .list_len = 1,
- };
- const filler_val_expr = ZigClangInitListExpr_getArrayFiller(expr);
- filler_init_node.list()[0] = try transExpr(rp, scope, filler_val_expr, .used, .r_value);
- filler_init_node.rtoken = try appendToken(rp.c, .RBrace, "}");
-
- const rhs_node = if (leftover_count == 1)
- &filler_init_node.base
- else blk: {
- const mul_tok = try appendToken(rp.c, .AsteriskAsterisk, "**");
- const mul_node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
- mul_node.* = .{
- .base = .{ .tag = .ArrayMult },
- .op_token = mul_tok,
- .lhs = &filler_init_node.base,
- .rhs = try transCreateNodeInt(rp.c, leftover_count),
- };
- break :blk &mul_node.base;
- };
-
- if (init_count == 0) {
- return rhs_node;
- }
-
- const cat_node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
- cat_node.* = .{
- .base = .{ .tag = .ArrayCat },
- .op_token = cat_tok,
- .lhs = &init_node.base,
- .rhs = rhs_node,
- };
- return &cat_node.base;
-}
-
-fn transInitListExpr(
- rp: RestorePoint,
- scope: *Scope,
- expr: *const ZigClangInitListExpr,
- used: ResultUsed,
-) TransError!*ast.Node {
- const qt = getExprQualType(rp.c, @ptrCast(*const ZigClangExpr, expr));
- var qual_type = ZigClangQualType_getTypePtr(qt);
- const source_loc = ZigClangExpr_getBeginLoc(@ptrCast(*const ZigClangExpr, expr));
-
- if (ZigClangType_isRecordType(qual_type)) {
- return transInitListExprRecord(
- rp,
- scope,
- source_loc,
- expr,
- qual_type,
- used,
- );
- } else if (ZigClangType_isArrayType(qual_type)) {
- return transInitListExprArray(
- rp,
- scope,
- source_loc,
- expr,
- qual_type,
- used,
- );
- } else {
- const type_name = rp.c.str(ZigClangType_getTypeClassName(qual_type));
- return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported initlist type: '{}'", .{type_name});
- }
-}
-
-fn transZeroInitExpr(
- rp: RestorePoint,
- scope: *Scope,
- source_loc: ZigClangSourceLocation,
- ty: *const ZigClangType,
-) TransError!*ast.Node {
- switch (ZigClangType_getTypeClass(ty)) {
- .Builtin => {
- const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
- switch (ZigClangBuiltinType_getKind(builtin_ty)) {
- .Bool => return try transCreateNodeBoolLiteral(rp.c, false),
- .Char_U,
- .UChar,
- .Char_S,
- .Char8,
- .SChar,
- .UShort,
- .UInt,
- .ULong,
- .ULongLong,
- .Short,
- .Int,
- .Long,
- .LongLong,
- .UInt128,
- .Int128,
- .Float,
- .Double,
- .Float128,
- .Float16,
- .LongDouble,
- => return transCreateNodeInt(rp.c, 0),
- else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported builtin type", .{}),
- }
- },
- .Pointer => return transCreateNodeNullLiteral(rp.c),
- .Typedef => {
- const typedef_ty = @ptrCast(*const ZigClangTypedefType, ty);
- const typedef_decl = ZigClangTypedefType_getDecl(typedef_ty);
- return transZeroInitExpr(
- rp,
- scope,
- source_loc,
- ZigClangQualType_getTypePtr(
- ZigClangTypedefNameDecl_getUnderlyingType(typedef_decl),
- ),
- );
- },
- else => {},
- }
-
- return revertAndWarn(rp, error.UnsupportedType, source_loc, "type does not have an implicit init value", .{});
-}
-
-fn transImplicitValueInitExpr(
- rp: RestorePoint,
- scope: *Scope,
- expr: *const ZigClangExpr,
- used: ResultUsed,
-) TransError!*ast.Node {
- const source_loc = ZigClangExpr_getBeginLoc(expr);
- const qt = getExprQualType(rp.c, expr);
- const ty = ZigClangQualType_getTypePtr(qt);
- return transZeroInitExpr(rp, scope, source_loc, ty);
-}
-
-fn transIfStmt(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangIfStmt,
-) TransError!*ast.Node {
- // if (c) t
- // if (c) t else e
- const if_node = try transCreateNodeIf(rp.c);
-
- var cond_scope = Scope.Condition{
- .base = .{
- .parent = scope,
- .id = .Condition,
- },
- };
- defer cond_scope.deinit();
- const cond_expr = @ptrCast(*const ZigClangExpr, ZigClangIfStmt_getCond(stmt));
- if_node.condition = try transBoolExpr(rp, &cond_scope.base, cond_expr, .used, .r_value, false);
- _ = try appendToken(rp.c, .RParen, ")");
-
- if_node.body = try transStmt(rp, scope, ZigClangIfStmt_getThen(stmt), .unused, .r_value);
-
- if (ZigClangIfStmt_getElse(stmt)) |expr| {
- if_node.@"else" = try transCreateNodeElse(rp.c);
- if_node.@"else".?.body = try transStmt(rp, scope, expr, .unused, .r_value);
- }
- _ = try appendToken(rp.c, .Semicolon, ";");
- return &if_node.base;
-}
-
-fn transWhileLoop(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangWhileStmt,
-) TransError!*ast.Node {
- const while_node = try transCreateNodeWhile(rp.c);
-
- var cond_scope = Scope.Condition{
- .base = .{
- .parent = scope,
- .id = .Condition,
- },
- };
- defer cond_scope.deinit();
- const cond_expr = @ptrCast(*const ZigClangExpr, ZigClangWhileStmt_getCond(stmt));
- while_node.condition = try transBoolExpr(rp, &cond_scope.base, cond_expr, .used, .r_value, false);
- _ = try appendToken(rp.c, .RParen, ")");
-
- var loop_scope = Scope{
- .parent = scope,
- .id = .Loop,
- };
- while_node.body = try transStmt(rp, &loop_scope, ZigClangWhileStmt_getBody(stmt), .unused, .r_value);
- _ = try appendToken(rp.c, .Semicolon, ";");
- return &while_node.base;
-}
-
-fn transDoWhileLoop(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangDoStmt,
-) TransError!*ast.Node {
- const while_node = try transCreateNodeWhile(rp.c);
-
- while_node.condition = try transCreateNodeBoolLiteral(rp.c, true);
- _ = try appendToken(rp.c, .RParen, ")");
- var new = false;
- var loop_scope = Scope{
- .parent = scope,
- .id = .Loop,
- };
-
- // if (!cond) break;
- const if_node = try transCreateNodeIf(rp.c);
- var cond_scope = Scope.Condition{
- .base = .{
- .parent = scope,
- .id = .Condition,
- },
- };
- defer cond_scope.deinit();
- const prefix_op = try transCreateNodeSimplePrefixOp(rp.c, .BoolNot, .Bang, "!");
- prefix_op.rhs = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const ZigClangExpr, ZigClangDoStmt_getCond(stmt)), .used, .r_value, true);
- _ = try appendToken(rp.c, .RParen, ")");
- if_node.condition = &prefix_op.base;
- if_node.body = &(try transCreateNodeBreak(rp.c, null, null)).base;
- _ = try appendToken(rp.c, .Semicolon, ";");
-
- const body_node = if (ZigClangStmt_getStmtClass(ZigClangDoStmt_getBody(stmt)) == .CompoundStmtClass) blk: {
- // there's already a block in C, so we'll append our condition to it.
- // c: do {
- // c: a;
- // c: b;
- // c: } while(c);
- // zig: while (true) {
- // zig: a;
- // zig: b;
- // zig: if (!cond) break;
- // zig: }
- const node = try transStmt(rp, &loop_scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value);
- break :blk node.castTag(.Block).?;
- } else blk: {
- // the C statement is without a block, so we need to create a block to contain it.
- // c: do
- // c: a;
- // c: while(c);
- // zig: while (true) {
- // zig: a;
- // zig: if (!cond) break;
- // zig: }
- new = true;
- const block = try rp.c.createBlock(2);
- block.statements_len = 1; // over-allocated so we can add another below
- block.statements()[0] = try transStmt(rp, &loop_scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value);
- break :blk block;
- };
-
- // In both cases above, we reserved 1 extra statement.
- body_node.statements_len += 1;
- body_node.statements()[body_node.statements_len - 1] = &if_node.base;
- if (new)
- body_node.rbrace = try appendToken(rp.c, .RBrace, "}");
- while_node.body = &body_node.base;
- return &while_node.base;
-}
-
-fn transForLoop(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangForStmt,
-) TransError!*ast.Node {
- var loop_scope = Scope{
- .parent = scope,
- .id = .Loop,
- };
-
- var block_scope: ?Scope.Block = null;
- defer if (block_scope) |*bs| bs.deinit();
-
- if (ZigClangForStmt_getInit(stmt)) |init| {
- block_scope = try Scope.Block.init(rp.c, scope, false);
- loop_scope.parent = &block_scope.?.base;
- const init_node = try transStmt(rp, &block_scope.?.base, init, .unused, .r_value);
- try block_scope.?.statements.append(init_node);
- }
- var cond_scope = Scope.Condition{
- .base = .{
- .parent = &loop_scope,
- .id = .Condition,
- },
- };
- defer cond_scope.deinit();
-
- const while_node = try transCreateNodeWhile(rp.c);
- while_node.condition = if (ZigClangForStmt_getCond(stmt)) |cond|
- try transBoolExpr(rp, &cond_scope.base, cond, .used, .r_value, false)
- else
- try transCreateNodeBoolLiteral(rp.c, true);
- _ = try appendToken(rp.c, .RParen, ")");
-
- if (ZigClangForStmt_getInc(stmt)) |incr| {
- _ = try appendToken(rp.c, .Colon, ":");
- _ = try appendToken(rp.c, .LParen, "(");
- while_node.continue_expr = try transExpr(rp, &cond_scope.base, incr, .unused, .r_value);
- _ = try appendToken(rp.c, .RParen, ")");
- }
-
- while_node.body = try transStmt(rp, &loop_scope, ZigClangForStmt_getBody(stmt), .unused, .r_value);
- if (block_scope) |*bs| {
- try bs.statements.append(&while_node.base);
- return try bs.complete(rp.c);
- } else {
- _ = try appendToken(rp.c, .Semicolon, ";");
- return &while_node.base;
- }
-}
-
-fn getSwitchCaseCount(stmt: *const ZigClangSwitchStmt) usize {
- const body = ZigClangSwitchStmt_getBody(stmt);
- assert(ZigClangStmt_getStmtClass(body) == .CompoundStmtClass);
- const comp = @ptrCast(*const ZigClangCompoundStmt, body);
- // TODO https://github.com/ziglang/zig/issues/1738
- // return ZigClangCompoundStmt_body_end(comp) - ZigClangCompoundStmt_body_begin(comp);
- const start_addr = @ptrToInt(ZigClangCompoundStmt_body_begin(comp));
- const end_addr = @ptrToInt(ZigClangCompoundStmt_body_end(comp));
- return (end_addr - start_addr) / @sizeOf(*ZigClangStmt);
-}
-
-fn transSwitch(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangSwitchStmt,
-) TransError!*ast.Node {
- const switch_tok = try appendToken(rp.c, .Keyword_switch, "switch");
- _ = try appendToken(rp.c, .LParen, "(");
-
- const cases_len = getSwitchCaseCount(stmt);
-
- var cond_scope = Scope.Condition{
- .base = .{
- .parent = scope,
- .id = .Condition,
- },
- };
- defer cond_scope.deinit();
- const switch_expr = try transExpr(rp, &cond_scope.base, ZigClangSwitchStmt_getCond(stmt), .used, .r_value);
- _ = try appendToken(rp.c, .RParen, ")");
- _ = try appendToken(rp.c, .LBrace, "{");
- // reserve +1 case in case there is no default case
- const switch_node = try ast.Node.Switch.alloc(rp.c.arena, cases_len + 1);
- switch_node.* = .{
- .switch_token = switch_tok,
- .expr = switch_expr,
- .cases_len = cases_len + 1,
- .rbrace = try appendToken(rp.c, .RBrace, "}"),
- };
-
- var switch_scope = Scope.Switch{
- .base = .{
- .id = .Switch,
- .parent = scope,
- },
- .cases = switch_node.cases(),
- .case_index = 0,
- .pending_block = undefined,
- .default_label = null,
- .switch_label = null,
- };
-
- // tmp block that all statements will go before being picked up by a case or default
- var block_scope = try Scope.Block.init(rp.c, &switch_scope.base, false);
- defer block_scope.deinit();
-
- // Note that we do not defer a deinit here; the switch_scope.pending_block field
- // has its own memory management. This resource is freed inside `transCase` and
- // then the final pending_block is freed at the bottom of this function with
- // pending_block.deinit().
- switch_scope.pending_block = try Scope.Block.init(rp.c, scope, false);
- try switch_scope.pending_block.statements.append(&switch_node.base);
-
- const last = try transStmt(rp, &block_scope.base, ZigClangSwitchStmt_getBody(stmt), .unused, .r_value);
- _ = try appendToken(rp.c, .Semicolon, ";");
-
- // take all pending statements
- const last_block_stmts = last.cast(ast.Node.Block).?.statements();
- try switch_scope.pending_block.statements.ensureCapacity(
- switch_scope.pending_block.statements.items.len + last_block_stmts.len,
- );
- for (last_block_stmts) |n| {
- switch_scope.pending_block.statements.appendAssumeCapacity(n);
- }
-
- if (switch_scope.default_label == null) {
- switch_scope.switch_label = try block_scope.makeMangledName(rp.c, "switch");
- }
- if (switch_scope.switch_label) |l| {
- switch_scope.pending_block.label = try appendIdentifier(rp.c, l);
- _ = try appendToken(rp.c, .Colon, ":");
- }
- if (switch_scope.default_label == null) {
- const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c));
- else_prong.expr = blk: {
- var br = try CtrlFlow.init(rp.c, .Break, switch_scope.switch_label.?);
- break :blk &(try br.finish(null)).base;
- };
- _ = try appendToken(rp.c, .Comma, ",");
-
- if (switch_scope.case_index >= switch_scope.cases.len)
- return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangStmt_getBeginLoc(@ptrCast(*const ZigClangStmt, stmt)), "TODO complex switch cases", .{});
- switch_scope.cases[switch_scope.case_index] = &else_prong.base;
- switch_scope.case_index += 1;
- }
- // We overallocated in case there was no default, so now we correct
- // the number of cases in the AST node.
- switch_node.cases_len = switch_scope.case_index;
-
- const result_node = try switch_scope.pending_block.complete(rp.c);
- switch_scope.pending_block.deinit();
- return result_node;
-}
-
-fn transCase(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangCaseStmt,
-) TransError!*ast.Node {
- const block_scope = scope.findBlockScope(rp.c) catch unreachable;
- const switch_scope = scope.getSwitch();
- const label = try block_scope.makeMangledName(rp.c, "case");
- _ = try appendToken(rp.c, .Semicolon, ";");
-
- const expr = if (ZigClangCaseStmt_getRHS(stmt)) |rhs| blk: {
- const lhs_node = try transExpr(rp, scope, ZigClangCaseStmt_getLHS(stmt), .used, .r_value);
- const ellips = try appendToken(rp.c, .Ellipsis3, "...");
- const rhs_node = try transExpr(rp, scope, rhs, .used, .r_value);
-
- const node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
- node.* = .{
- .base = .{ .tag = .Range },
- .op_token = ellips,
- .lhs = lhs_node,
- .rhs = rhs_node,
- };
- break :blk &node.base;
- } else
- try transExpr(rp, scope, ZigClangCaseStmt_getLHS(stmt), .used, .r_value);
-
- const switch_prong = try transCreateNodeSwitchCase(rp.c, expr);
- switch_prong.expr = blk: {
- var br = try CtrlFlow.init(rp.c, .Break, label);
- break :blk &(try br.finish(null)).base;
- };
- _ = try appendToken(rp.c, .Comma, ",");
-
- if (switch_scope.case_index >= switch_scope.cases.len)
- return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangStmt_getBeginLoc(@ptrCast(*const ZigClangStmt, stmt)), "TODO complex switch cases", .{});
- switch_scope.cases[switch_scope.case_index] = &switch_prong.base;
- switch_scope.case_index += 1;
-
- switch_scope.pending_block.label = try appendIdentifier(rp.c, label);
- _ = try appendToken(rp.c, .Colon, ":");
-
- // take all pending statements
- try switch_scope.pending_block.statements.appendSlice(block_scope.statements.items);
- block_scope.statements.shrink(0);
-
- const pending_node = try switch_scope.pending_block.complete(rp.c);
- switch_scope.pending_block.deinit();
- switch_scope.pending_block = try Scope.Block.init(rp.c, scope, false);
-
- try switch_scope.pending_block.statements.append(pending_node);
-
- return transStmt(rp, scope, ZigClangCaseStmt_getSubStmt(stmt), .unused, .r_value);
-}
-
-fn transDefault(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangDefaultStmt,
-) TransError!*ast.Node {
- const block_scope = scope.findBlockScope(rp.c) catch unreachable;
- const switch_scope = scope.getSwitch();
- switch_scope.default_label = try block_scope.makeMangledName(rp.c, "default");
- _ = try appendToken(rp.c, .Semicolon, ";");
-
- const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c));
- else_prong.expr = blk: {
- var br = try CtrlFlow.init(rp.c, .Break, switch_scope.default_label.?);
- break :blk &(try br.finish(null)).base;
- };
- _ = try appendToken(rp.c, .Comma, ",");
-
- if (switch_scope.case_index >= switch_scope.cases.len)
- return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangStmt_getBeginLoc(@ptrCast(*const ZigClangStmt, stmt)), "TODO complex switch cases", .{});
- switch_scope.cases[switch_scope.case_index] = &else_prong.base;
- switch_scope.case_index += 1;
-
- switch_scope.pending_block.label = try appendIdentifier(rp.c, switch_scope.default_label.?);
- _ = try appendToken(rp.c, .Colon, ":");
-
- // take all pending statements
- try switch_scope.pending_block.statements.appendSlice(block_scope.statements.items);
- block_scope.statements.shrink(0);
-
- const pending_node = try switch_scope.pending_block.complete(rp.c);
- switch_scope.pending_block.deinit();
- switch_scope.pending_block = try Scope.Block.init(rp.c, scope, false);
- try switch_scope.pending_block.statements.append(pending_node);
-
- return transStmt(rp, scope, ZigClangDefaultStmt_getSubStmt(stmt), .unused, .r_value);
-}
-
-fn transConstantExpr(rp: RestorePoint, scope: *Scope, expr: *const ZigClangExpr, used: ResultUsed) TransError!*ast.Node {
- var result: ZigClangExprEvalResult = undefined;
- if (!ZigClangExpr_EvaluateAsConstantExpr(expr, &result, .EvaluateForCodeGen, rp.c.clang_context))
- return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangExpr_getBeginLoc(expr), "invalid constant expression", .{});
-
- var val_node: ?*ast.Node = null;
- switch (ZigClangAPValue_getKind(&result.Val)) {
- .Int => {
- // See comment in `transIntegerLiteral` for why this code is here.
- // @as(T, x)
- const expr_base = @ptrCast(*const ZigClangExpr, expr);
- const as_node = try rp.c.createBuiltinCall("@as", 2);
- const ty_node = try transQualType(rp, ZigClangExpr_getType(expr_base), ZigClangExpr_getBeginLoc(expr_base));
- as_node.params()[0] = ty_node;
- _ = try appendToken(rp.c, .Comma, ",");
-
- const int_lit_node = try transCreateNodeAPInt(rp.c, ZigClangAPValue_getInt(&result.Val));
- as_node.params()[1] = int_lit_node;
-
- as_node.rparen_token = try appendToken(rp.c, .RParen, ")");
-
- return maybeSuppressResult(rp, scope, used, &as_node.base);
- },
- else => {
- return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangExpr_getBeginLoc(expr), "unsupported constant expression kind", .{});
- },
- }
-}
-
-fn transPredefinedExpr(rp: RestorePoint, scope: *Scope, expr: *const ZigClangPredefinedExpr, used: ResultUsed) TransError!*ast.Node {
- return transStringLiteral(rp, scope, ZigClangPredefinedExpr_getFunctionName(expr), used);
-}
-
-fn transCharLiteral(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangCharacterLiteral,
- result_used: ResultUsed,
- suppress_as: SuppressCast,
-) TransError!*ast.Node {
- const kind = ZigClangCharacterLiteral_getKind(stmt);
- const int_lit_node = switch (kind) {
- .Ascii, .UTF8 => blk: {
- const val = ZigClangCharacterLiteral_getValue(stmt);
- if (kind == .Ascii) {
- // C has a somewhat obscure feature called multi-character character
- // constant
- if (val > 255)
- break :blk try transCreateNodeInt(rp.c, val);
- }
- var char_buf: [4]u8 = undefined;
- const token = try appendTokenFmt(rp.c, .CharLiteral, "'{}'", .{escapeChar(@intCast(u8, val), &char_buf)});
- const node = try rp.c.arena.create(ast.Node.OneToken);
- node.* = .{
- .base = .{ .tag = .CharLiteral },
- .token = token,
- };
- break :blk &node.base;
- },
- .UTF16, .UTF32, .Wide => return revertAndWarn(
- rp,
- error.UnsupportedTranslation,
- ZigClangStmt_getBeginLoc(@ptrCast(*const ZigClangStmt, stmt)),
- "TODO: support character literal kind {}",
- .{kind},
- ),
- };
- if (suppress_as == .no_as) {
- return maybeSuppressResult(rp, scope, result_used, int_lit_node);
- }
- // See comment in `transIntegerLiteral` for why this code is here.
- // @as(T, x)
- const expr_base = @ptrCast(*const ZigClangExpr, stmt);
- const as_node = try rp.c.createBuiltinCall("@as", 2);
- const ty_node = try transQualType(rp, ZigClangExpr_getType(expr_base), ZigClangExpr_getBeginLoc(expr_base));
- as_node.params()[0] = ty_node;
- _ = try appendToken(rp.c, .Comma, ",");
- as_node.params()[1] = int_lit_node;
-
- as_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- return maybeSuppressResult(rp, scope, result_used, &as_node.base);
-}
-
-fn transStmtExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangStmtExpr, used: ResultUsed) TransError!*ast.Node {
- const comp = ZigClangStmtExpr_getSubStmt(stmt);
- if (used == .unused) {
- return transCompoundStmt(rp, scope, comp);
- }
- const lparen = try appendToken(rp.c, .LParen, "(");
- var block_scope = try Scope.Block.init(rp.c, scope, true);
- defer block_scope.deinit();
-
- var it = ZigClangCompoundStmt_body_begin(comp);
- const end_it = ZigClangCompoundStmt_body_end(comp);
- while (it != end_it - 1) : (it += 1) {
- const result = try transStmt(rp, &block_scope.base, it[0], .unused, .r_value);
- try block_scope.statements.append(result);
- }
- const break_node = blk: {
- var tmp = try CtrlFlow.init(rp.c, .Break, "blk");
- const rhs = try transStmt(rp, &block_scope.base, it[0], .used, .r_value);
- break :blk try tmp.finish(rhs);
- };
- _ = try appendToken(rp.c, .Semicolon, ";");
- try block_scope.statements.append(&break_node.base);
- const block_node = try block_scope.complete(rp.c);
- const rparen = try appendToken(rp.c, .RParen, ")");
- const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
- grouped_expr.* = .{
- .lparen = lparen,
- .expr = block_node,
- .rparen = rparen,
- };
- return maybeSuppressResult(rp, scope, used, &grouped_expr.base);
-}
-
-fn transMemberExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangMemberExpr, result_used: ResultUsed) TransError!*ast.Node {
- var container_node = try transExpr(rp, scope, ZigClangMemberExpr_getBase(stmt), .used, .r_value);
-
- if (ZigClangMemberExpr_isArrow(stmt)) {
- container_node = try transCreateNodePtrDeref(rp.c, container_node);
- }
-
- const member_decl = ZigClangMemberExpr_getMemberDecl(stmt);
- const name = blk: {
- const decl_kind = ZigClangDecl_getKind(@ptrCast(*const ZigClangDecl, member_decl));
- // If we're referring to a anonymous struct/enum find the bogus name
- // we've assigned to it during the RecordDecl translation
- if (decl_kind == .Field) {
- const field_decl = @ptrCast(*const struct_ZigClangFieldDecl, member_decl);
- if (ZigClangFieldDecl_isAnonymousStructOrUnion(field_decl)) {
- const name = rp.c.decl_table.get(@ptrToInt(ZigClangFieldDecl_getCanonicalDecl(field_decl))).?;
- break :blk try mem.dupe(rp.c.arena, u8, name);
- }
- }
- const decl = @ptrCast(*const ZigClangNamedDecl, member_decl);
- break :blk try rp.c.str(ZigClangNamedDecl_getName_bytes_begin(decl));
- };
-
- const node = try transCreateNodeFieldAccess(rp.c, container_node, name);
- return maybeSuppressResult(rp, scope, result_used, node);
-}
-
-fn transArrayAccess(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangArraySubscriptExpr, result_used: ResultUsed) TransError!*ast.Node {
- var base_stmt = ZigClangArraySubscriptExpr_getBase(stmt);
-
- // Unwrap the base statement if it's an array decayed to a bare pointer type
- // so that we index the array itself
- if (ZigClangStmt_getStmtClass(@ptrCast(*const ZigClangStmt, base_stmt)) == .ImplicitCastExprClass) {
- const implicit_cast = @ptrCast(*const ZigClangImplicitCastExpr, base_stmt);
-
- if (ZigClangImplicitCastExpr_getCastKind(implicit_cast) == .ArrayToPointerDecay) {
- base_stmt = ZigClangImplicitCastExpr_getSubExpr(implicit_cast);
- }
- }
-
- const container_node = try transExpr(rp, scope, base_stmt, .used, .r_value);
- const node = try transCreateNodeArrayAccess(rp.c, container_node);
-
- // cast if the index is long long or signed
- const subscr_expr = ZigClangArraySubscriptExpr_getIdx(stmt);
- const qt = getExprQualType(rp.c, subscr_expr);
- const is_longlong = cIsLongLongInteger(qt);
- const is_signed = cIsSignedInteger(qt);
-
- if (is_longlong or is_signed) {
- const cast_node = try rp.c.createBuiltinCall("@intCast", 2);
- // check if long long first so that signed long long doesn't just become unsigned long long
- var typeid_node = if (is_longlong) try transCreateNodeIdentifier(rp.c, "usize") else try transQualTypeIntWidthOf(rp.c, qt, false);
- cast_node.params()[0] = typeid_node;
- _ = try appendToken(rp.c, .Comma, ",");
- cast_node.params()[1] = try transExpr(rp, scope, subscr_expr, .used, .r_value);
- cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- node.rtoken = try appendToken(rp.c, .RBrace, "]");
- node.index_expr = &cast_node.base;
- } else {
- node.index_expr = try transExpr(rp, scope, subscr_expr, .used, .r_value);
- node.rtoken = try appendToken(rp.c, .RBrace, "]");
- }
- return maybeSuppressResult(rp, scope, result_used, &node.base);
-}
-
-fn transCallExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCallExpr, result_used: ResultUsed) TransError!*ast.Node {
- const callee = ZigClangCallExpr_getCallee(stmt);
- var raw_fn_expr = try transExpr(rp, scope, callee, .used, .r_value);
-
- var is_ptr = false;
- const fn_ty = qualTypeGetFnProto(ZigClangExpr_getType(callee), &is_ptr);
-
- const fn_expr = if (is_ptr and fn_ty != null) blk: {
- if (ZigClangExpr_getStmtClass(callee) == .ImplicitCastExprClass) {
- const implicit_cast = @ptrCast(*const ZigClangImplicitCastExpr, callee);
-
- if (ZigClangImplicitCastExpr_getCastKind(implicit_cast) == .FunctionToPointerDecay) {
- const subexpr = ZigClangImplicitCastExpr_getSubExpr(implicit_cast);
- if (ZigClangExpr_getStmtClass(subexpr) == .DeclRefExprClass) {
- const decl_ref = @ptrCast(*const ZigClangDeclRefExpr, subexpr);
- const named_decl = ZigClangDeclRefExpr_getFoundDecl(decl_ref);
- if (ZigClangDecl_getKind(@ptrCast(*const ZigClangDecl, named_decl)) == .Function) {
- break :blk raw_fn_expr;
- }
- }
- }
- }
- break :blk try transCreateNodeUnwrapNull(rp.c, raw_fn_expr);
- } else
- raw_fn_expr;
-
- const num_args = ZigClangCallExpr_getNumArgs(stmt);
- const node = try rp.c.createCall(fn_expr, num_args);
- const call_params = node.params();
-
- const args = ZigClangCallExpr_getArgs(stmt);
- var i: usize = 0;
- while (i < num_args) : (i += 1) {
- if (i != 0) {
- _ = try appendToken(rp.c, .Comma, ",");
- }
- call_params[i] = try transExpr(rp, scope, args[i], .used, .r_value);
- }
- node.rtoken = try appendToken(rp.c, .RParen, ")");
-
- if (fn_ty) |ty| {
- const canon = ZigClangQualType_getCanonicalType(ty.getReturnType());
- const ret_ty = ZigClangQualType_getTypePtr(canon);
- if (ZigClangType_isVoidType(ret_ty)) {
- _ = try appendToken(rp.c, .Semicolon, ";");
- return &node.base;
- }
- }
-
- return maybeSuppressResult(rp, scope, result_used, &node.base);
-}
-
-const ClangFunctionType = union(enum) {
- Proto: *const ZigClangFunctionProtoType,
- NoProto: *const ZigClangFunctionType,
-
- fn getReturnType(self: @This()) ZigClangQualType {
- switch (@as(@TagType(@This()), self)) {
- .Proto => return ZigClangFunctionProtoType_getReturnType(self.Proto),
- .NoProto => return ZigClangFunctionType_getReturnType(self.NoProto),
- }
- }
-};
-
-fn qualTypeGetFnProto(qt: ZigClangQualType, is_ptr: *bool) ?ClangFunctionType {
- const canon = ZigClangQualType_getCanonicalType(qt);
- var ty = ZigClangQualType_getTypePtr(canon);
- is_ptr.* = false;
-
- if (ZigClangType_getTypeClass(ty) == .Pointer) {
- is_ptr.* = true;
- const child_qt = ZigClangType_getPointeeType(ty);
- ty = ZigClangQualType_getTypePtr(child_qt);
- }
- if (ZigClangType_getTypeClass(ty) == .FunctionProto) {
- return ClangFunctionType{ .Proto = @ptrCast(*const ZigClangFunctionProtoType, ty) };
- }
- if (ZigClangType_getTypeClass(ty) == .FunctionNoProto) {
- return ClangFunctionType{ .NoProto = @ptrCast(*const ZigClangFunctionType, ty) };
- }
- return null;
-}
-
-fn transUnaryExprOrTypeTraitExpr(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangUnaryExprOrTypeTraitExpr,
- result_used: ResultUsed,
-) TransError!*ast.Node {
- const loc = ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(stmt);
- const type_node = try transQualType(
- rp,
- ZigClangUnaryExprOrTypeTraitExpr_getTypeOfArgument(stmt),
- loc,
- );
-
- const kind = ZigClangUnaryExprOrTypeTraitExpr_getKind(stmt);
- const kind_str = switch (kind) {
- .SizeOf => "@sizeOf",
- .AlignOf => "@alignOf",
- .PreferredAlignOf,
- .VecStep,
- .OpenMPRequiredSimdAlign,
- => return revertAndWarn(
- rp,
- error.UnsupportedTranslation,
- loc,
- "Unsupported type trait kind {}",
- .{kind},
- ),
- };
-
- const builtin_node = try rp.c.createBuiltinCall(kind_str, 1);
- builtin_node.params()[0] = type_node;
- builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- return maybeSuppressResult(rp, scope, result_used, &builtin_node.base);
-}
-
-fn qualTypeHasWrappingOverflow(qt: ZigClangQualType) bool {
- if (cIsUnsignedInteger(qt)) {
- // unsigned integer overflow wraps around.
- return true;
- } else {
- // float, signed integer, and pointer overflow is undefined behavior.
- return false;
- }
-}
-
-fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnaryOperator, used: ResultUsed) TransError!*ast.Node {
- const op_expr = ZigClangUnaryOperator_getSubExpr(stmt);
- switch (ZigClangUnaryOperator_getOpcode(stmt)) {
- .PostInc => if (qualTypeHasWrappingOverflow(ZigClangUnaryOperator_getType(stmt)))
- return transCreatePostCrement(rp, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", used)
- else
- return transCreatePostCrement(rp, scope, stmt, .AssignAdd, .PlusEqual, "+=", used),
- .PostDec => if (qualTypeHasWrappingOverflow(ZigClangUnaryOperator_getType(stmt)))
- return transCreatePostCrement(rp, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", used)
- else
- return transCreatePostCrement(rp, scope, stmt, .AssignSub, .MinusEqual, "-=", used),
- .PreInc => if (qualTypeHasWrappingOverflow(ZigClangUnaryOperator_getType(stmt)))
- return transCreatePreCrement(rp, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", used)
- else
- return transCreatePreCrement(rp, scope, stmt, .AssignAdd, .PlusEqual, "+=", used),
- .PreDec => if (qualTypeHasWrappingOverflow(ZigClangUnaryOperator_getType(stmt)))
- return transCreatePreCrement(rp, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", used)
- else
- return transCreatePreCrement(rp, scope, stmt, .AssignSub, .MinusEqual, "-=", used),
- .AddrOf => {
- const op_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
- op_node.rhs = try transExpr(rp, scope, op_expr, used, .r_value);
- return &op_node.base;
- },
- .Deref => {
- const value_node = try transExpr(rp, scope, op_expr, used, .r_value);
- var is_ptr = false;
- const fn_ty = qualTypeGetFnProto(ZigClangExpr_getType(op_expr), &is_ptr);
- if (fn_ty != null and is_ptr)
- return value_node;
- const unwrapped = try transCreateNodeUnwrapNull(rp.c, value_node);
- return transCreateNodePtrDeref(rp.c, unwrapped);
- },
- .Plus => return transExpr(rp, scope, op_expr, used, .r_value),
- .Minus => {
- if (!qualTypeHasWrappingOverflow(ZigClangExpr_getType(op_expr))) {
- const op_node = try transCreateNodeSimplePrefixOp(rp.c, .Negation, .Minus, "-");
- op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
- return &op_node.base;
- } else if (cIsUnsignedInteger(ZigClangExpr_getType(op_expr))) {
- // we gotta emit 0 -% x
- const zero = try transCreateNodeInt(rp.c, 0);
- const token = try appendToken(rp.c, .MinusPercent, "-%");
- const expr = try transExpr(rp, scope, op_expr, .used, .r_value);
- return transCreateNodeInfixOp(rp, scope, zero, .SubWrap, token, expr, used, true);
- } else
- return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangUnaryOperator_getBeginLoc(stmt), "C negation with non float non integer", .{});
- },
- .Not => {
- const op_node = try transCreateNodeSimplePrefixOp(rp.c, .BitNot, .Tilde, "~");
- op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
- return &op_node.base;
- },
- .LNot => {
- const op_node = try transCreateNodeSimplePrefixOp(rp.c, .BoolNot, .Bang, "!");
- op_node.rhs = try transBoolExpr(rp, scope, op_expr, .used, .r_value, true);
- return &op_node.base;
- },
- .Extension => {
- return transExpr(rp, scope, ZigClangUnaryOperator_getSubExpr(stmt), used, .l_value);
- },
- else => return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangUnaryOperator_getBeginLoc(stmt), "unsupported C translation {}", .{ZigClangUnaryOperator_getOpcode(stmt)}),
- }
-}
-
-fn transCreatePreCrement(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangUnaryOperator,
- op: ast.Node.Tag,
- op_tok_id: std.zig.Token.Id,
- bytes: []const u8,
- used: ResultUsed,
-) TransError!*ast.Node {
- const op_expr = ZigClangUnaryOperator_getSubExpr(stmt);
-
- if (used == .unused) {
- // common case
- // c: ++expr
- // zig: expr += 1
- const expr = try transExpr(rp, scope, op_expr, .used, .r_value);
- const token = try appendToken(rp.c, op_tok_id, bytes);
- const one = try transCreateNodeInt(rp.c, 1);
- if (scope.id != .Condition)
- _ = try appendToken(rp.c, .Semicolon, ";");
- return transCreateNodeInfixOp(rp, scope, expr, op, token, one, .used, false);
- }
- // worst case
- // c: ++expr
- // zig: (blk: {
- // zig: const _ref = &expr;
- // zig: _ref.* += 1;
- // zig: break :blk _ref.*
- // zig: })
- var block_scope = try Scope.Block.init(rp.c, scope, true);
- defer block_scope.deinit();
- const ref = try block_scope.makeMangledName(rp.c, "ref");
-
- const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
- const name_tok = try appendIdentifier(rp.c, ref);
- const eq_token = try appendToken(rp.c, .Equal, "=");
- const rhs_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
- rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
- const init_node = &rhs_node.base;
- const semicolon_token = try appendToken(rp.c, .Semicolon, ";");
- const node = try ast.Node.VarDecl.create(rp.c.arena, .{
- .name_token = name_tok,
- .mut_token = mut_tok,
- .semicolon_token = semicolon_token,
- }, .{
- .eq_token = eq_token,
- .init_node = init_node,
- });
- try block_scope.statements.append(&node.base);
-
- const lhs_node = try transCreateNodeIdentifier(rp.c, ref);
- const ref_node = try transCreateNodePtrDeref(rp.c, lhs_node);
- _ = try appendToken(rp.c, .Semicolon, ";");
- const token = try appendToken(rp.c, op_tok_id, bytes);
- const one = try transCreateNodeInt(rp.c, 1);
- _ = try appendToken(rp.c, .Semicolon, ";");
- const assign = try transCreateNodeInfixOp(rp, scope, ref_node, op, token, one, .used, false);
- try block_scope.statements.append(assign);
-
- const break_node = try transCreateNodeBreak(rp.c, block_scope.label, ref_node);
- try block_scope.statements.append(&break_node.base);
- const block_node = try block_scope.complete(rp.c);
- // semicolon must immediately follow rbrace because it is the last token in a block
- _ = try appendToken(rp.c, .Semicolon, ";");
- const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
- grouped_expr.* = .{
- .lparen = try appendToken(rp.c, .LParen, "("),
- .expr = block_node,
- .rparen = try appendToken(rp.c, .RParen, ")"),
- };
- return &grouped_expr.base;
-}
-
-fn transCreatePostCrement(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangUnaryOperator,
- op: ast.Node.Tag,
- op_tok_id: std.zig.Token.Id,
- bytes: []const u8,
- used: ResultUsed,
-) TransError!*ast.Node {
- const op_expr = ZigClangUnaryOperator_getSubExpr(stmt);
-
- if (used == .unused) {
- // common case
- // c: ++expr
- // zig: expr += 1
- const expr = try transExpr(rp, scope, op_expr, .used, .r_value);
- const token = try appendToken(rp.c, op_tok_id, bytes);
- const one = try transCreateNodeInt(rp.c, 1);
- if (scope.id != .Condition)
- _ = try appendToken(rp.c, .Semicolon, ";");
- return transCreateNodeInfixOp(rp, scope, expr, op, token, one, .used, false);
- }
- // worst case
- // c: expr++
- // zig: (blk: {
- // zig: const _ref = &expr;
- // zig: const _tmp = _ref.*;
- // zig: _ref.* += 1;
- // zig: break :blk _tmp
- // zig: })
- var block_scope = try Scope.Block.init(rp.c, scope, true);
- defer block_scope.deinit();
- const ref = try block_scope.makeMangledName(rp.c, "ref");
-
- const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
- const name_tok = try appendIdentifier(rp.c, ref);
- const eq_token = try appendToken(rp.c, .Equal, "=");
- const rhs_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
- rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
- const init_node = &rhs_node.base;
- const semicolon_token = try appendToken(rp.c, .Semicolon, ";");
- const node = try ast.Node.VarDecl.create(rp.c.arena, .{
- .name_token = name_tok,
- .mut_token = mut_tok,
- .semicolon_token = semicolon_token,
- }, .{
- .eq_token = eq_token,
- .init_node = init_node,
- });
- try block_scope.statements.append(&node.base);
-
- const lhs_node = try transCreateNodeIdentifier(rp.c, ref);
- const ref_node = try transCreateNodePtrDeref(rp.c, lhs_node);
- _ = try appendToken(rp.c, .Semicolon, ";");
-
- const tmp = try block_scope.makeMangledName(rp.c, "tmp");
- const tmp_mut_tok = try appendToken(rp.c, .Keyword_const, "const");
- const tmp_name_tok = try appendIdentifier(rp.c, tmp);
- const tmp_eq_token = try appendToken(rp.c, .Equal, "=");
- const tmp_init_node = ref_node;
- const tmp_semicolon_token = try appendToken(rp.c, .Semicolon, ";");
- const tmp_node = try ast.Node.VarDecl.create(rp.c.arena, .{
- .name_token = tmp_name_tok,
- .mut_token = tmp_mut_tok,
- .semicolon_token = semicolon_token,
- }, .{
- .eq_token = tmp_eq_token,
- .init_node = tmp_init_node,
- });
- try block_scope.statements.append(&tmp_node.base);
-
- const token = try appendToken(rp.c, op_tok_id, bytes);
- const one = try transCreateNodeInt(rp.c, 1);
- _ = try appendToken(rp.c, .Semicolon, ";");
- const assign = try transCreateNodeInfixOp(rp, scope, ref_node, op, token, one, .used, false);
- try block_scope.statements.append(assign);
-
- const break_node = blk: {
- var tmp_ctrl_flow = try CtrlFlow.initToken(rp.c, .Break, block_scope.label);
- const rhs = try transCreateNodeIdentifier(rp.c, tmp);
- break :blk try tmp_ctrl_flow.finish(rhs);
- };
- try block_scope.statements.append(&break_node.base);
- _ = try appendToken(rp.c, .Semicolon, ";");
- const block_node = try block_scope.complete(rp.c);
- const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
- grouped_expr.* = .{
- .lparen = try appendToken(rp.c, .LParen, "("),
- .expr = block_node,
- .rparen = try appendToken(rp.c, .RParen, ")"),
- };
- return &grouped_expr.base;
-}
-
-fn transCompoundAssignOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCompoundAssignOperator, used: ResultUsed) TransError!*ast.Node {
- switch (ZigClangCompoundAssignOperator_getOpcode(stmt)) {
- .MulAssign => if (qualTypeHasWrappingOverflow(ZigClangCompoundAssignOperator_getType(stmt)))
- return transCreateCompoundAssign(rp, scope, stmt, .AssignMulWrap, .AsteriskPercentEqual, "*%=", .MulWrap, .AsteriskPercent, "*%", used)
- else
- return transCreateCompoundAssign(rp, scope, stmt, .AssignMul, .AsteriskEqual, "*=", .Mul, .Asterisk, "*", used),
- .AddAssign => if (qualTypeHasWrappingOverflow(ZigClangCompoundAssignOperator_getType(stmt)))
- return transCreateCompoundAssign(rp, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", .AddWrap, .PlusPercent, "+%", used)
- else
- return transCreateCompoundAssign(rp, scope, stmt, .AssignAdd, .PlusEqual, "+=", .Add, .Plus, "+", used),
- .SubAssign => if (qualTypeHasWrappingOverflow(ZigClangCompoundAssignOperator_getType(stmt)))
- return transCreateCompoundAssign(rp, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", .SubWrap, .MinusPercent, "-%", used)
- else
- return transCreateCompoundAssign(rp, scope, stmt, .AssignSub, .MinusPercentEqual, "-=", .Sub, .Minus, "-", used),
- .DivAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignDiv, .SlashEqual, "/=", .Div, .Slash, "/", used),
- .RemAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignMod, .PercentEqual, "%=", .Mod, .Percent, "%", used),
- .ShlAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitShiftLeft, .AngleBracketAngleBracketLeftEqual, "<<=", .BitShiftLeft, .AngleBracketAngleBracketLeft, "<<", used),
- .ShrAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitShiftRight, .AngleBracketAngleBracketRightEqual, ">>=", .BitShiftRight, .AngleBracketAngleBracketRight, ">>", used),
- .AndAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitAnd, .AmpersandEqual, "&=", .BitAnd, .Ampersand, "&", used),
- .XorAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitXor, .CaretEqual, "^=", .BitXor, .Caret, "^", used),
- .OrAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitOr, .PipeEqual, "|=", .BitOr, .Pipe, "|", used),
- else => return revertAndWarn(
- rp,
- error.UnsupportedTranslation,
- ZigClangCompoundAssignOperator_getBeginLoc(stmt),
- "unsupported C translation {}",
- .{ZigClangCompoundAssignOperator_getOpcode(stmt)},
- ),
- }
-}
-
-fn transCreateCompoundAssign(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangCompoundAssignOperator,
- assign_op: ast.Node.Tag,
- assign_tok_id: std.zig.Token.Id,
- assign_bytes: []const u8,
- bin_op: ast.Node.Tag,
- bin_tok_id: std.zig.Token.Id,
- bin_bytes: []const u8,
- used: ResultUsed,
-) TransError!*ast.Node {
- const is_shift = bin_op == .BitShiftLeft or bin_op == .BitShiftRight;
- const is_div = bin_op == .Div;
- const is_mod = bin_op == .Mod;
- const lhs = ZigClangCompoundAssignOperator_getLHS(stmt);
- const rhs = ZigClangCompoundAssignOperator_getRHS(stmt);
- const loc = ZigClangCompoundAssignOperator_getBeginLoc(stmt);
- const lhs_qt = getExprQualType(rp.c, lhs);
- const rhs_qt = getExprQualType(rp.c, rhs);
- const is_signed = cIsSignedInteger(lhs_qt);
- const requires_int_cast = blk: {
- const are_integers = cIsInteger(lhs_qt) and cIsInteger(rhs_qt);
- const are_same_sign = cIsSignedInteger(lhs_qt) == cIsSignedInteger(rhs_qt);
- break :blk are_integers and !are_same_sign;
- };
- if (used == .unused) {
- // common case
- // c: lhs += rhs
- // zig: lhs += rhs
- if ((is_mod or is_div) and is_signed) {
- const op_token = try appendToken(rp.c, .Equal, "=");
- const op_node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
- const builtin = if (is_mod) "@rem" else "@divTrunc";
- const builtin_node = try rp.c.createBuiltinCall(builtin, 2);
- const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value);
- builtin_node.params()[0] = lhs_node;
- _ = try appendToken(rp.c, .Comma, ",");
- builtin_node.params()[1] = try transExpr(rp, scope, rhs, .used, .r_value);
- builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- op_node.* = .{
- .base = .{ .tag = .Assign },
- .op_token = op_token,
- .lhs = lhs_node,
- .rhs = &builtin_node.base,
- };
- _ = try appendToken(rp.c, .Semicolon, ";");
- return &op_node.base;
- }
-
- const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value);
- const eq_token = try appendToken(rp.c, assign_tok_id, assign_bytes);
- var rhs_node = if (is_shift or requires_int_cast)
- try transExprCoercing(rp, scope, rhs, .used, .r_value)
- else
- try transExpr(rp, scope, rhs, .used, .r_value);
-
- if (is_shift or requires_int_cast) {
- const cast_node = try rp.c.createBuiltinCall("@intCast", 2);
- const cast_to_type = if (is_shift)
- try qualTypeToLog2IntRef(rp, getExprQualType(rp.c, rhs), loc)
- else
- try transQualType(rp, getExprQualType(rp.c, lhs), loc);
- cast_node.params()[0] = cast_to_type;
- _ = try appendToken(rp.c, .Comma, ",");
- cast_node.params()[1] = rhs_node;
- cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- rhs_node = &cast_node.base;
- }
- if (scope.id != .Condition)
- _ = try appendToken(rp.c, .Semicolon, ";");
- return transCreateNodeInfixOp(rp, scope, lhs_node, assign_op, eq_token, rhs_node, .used, false);
- }
- // worst case
- // c: lhs += rhs
- // zig: (blk: {
- // zig: const _ref = &lhs;
- // zig: _ref.* = _ref.* + rhs;
- // zig: break :blk _ref.*
- // zig: })
- var block_scope = try Scope.Block.init(rp.c, scope, true);
- defer block_scope.deinit();
- const ref = try block_scope.makeMangledName(rp.c, "ref");
-
- const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
- const name_tok = try appendIdentifier(rp.c, ref);
- const eq_token = try appendToken(rp.c, .Equal, "=");
- const addr_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
- addr_node.rhs = try transExpr(rp, scope, lhs, .used, .l_value);
- const init_node = &addr_node.base;
- const semicolon_token = try appendToken(rp.c, .Semicolon, ";");
- const node = try ast.Node.VarDecl.create(rp.c.arena, .{
- .name_token = name_tok,
- .mut_token = mut_tok,
- .semicolon_token = semicolon_token,
- }, .{
- .eq_token = eq_token,
- .init_node = init_node,
- });
- try block_scope.statements.append(&node.base);
-
- const lhs_node = try transCreateNodeIdentifier(rp.c, ref);
- const ref_node = try transCreateNodePtrDeref(rp.c, lhs_node);
- _ = try appendToken(rp.c, .Semicolon, ";");
-
- if ((is_mod or is_div) and is_signed) {
- const op_token = try appendToken(rp.c, .Equal, "=");
- const op_node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
- const builtin = if (is_mod) "@rem" else "@divTrunc";
- const builtin_node = try rp.c.createBuiltinCall(builtin, 2);
- builtin_node.params()[0] = try transCreateNodePtrDeref(rp.c, lhs_node);
- _ = try appendToken(rp.c, .Comma, ",");
- builtin_node.params()[1] = try transExpr(rp, scope, rhs, .used, .r_value);
- builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- _ = try appendToken(rp.c, .Semicolon, ";");
- op_node.* = .{
- .base = .{ .tag = .Assign },
- .op_token = op_token,
- .lhs = ref_node,
- .rhs = &builtin_node.base,
- };
- _ = try appendToken(rp.c, .Semicolon, ";");
- try block_scope.statements.append(&op_node.base);
- } else {
- const bin_token = try appendToken(rp.c, bin_tok_id, bin_bytes);
- var rhs_node = try transExpr(rp, scope, rhs, .used, .r_value);
-
- if (is_shift or requires_int_cast) {
- const cast_node = try rp.c.createBuiltinCall("@intCast", 2);
- const cast_to_type = if (is_shift)
- try qualTypeToLog2IntRef(rp, getExprQualType(rp.c, rhs), loc)
- else
- try transQualType(rp, getExprQualType(rp.c, lhs), loc);
- cast_node.params()[0] = cast_to_type;
- _ = try appendToken(rp.c, .Comma, ",");
- cast_node.params()[1] = rhs_node;
- cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- rhs_node = &cast_node.base;
- }
-
- const rhs_bin = try transCreateNodeInfixOp(rp, scope, ref_node, bin_op, bin_token, rhs_node, .used, false);
- _ = try appendToken(rp.c, .Semicolon, ";");
-
- const ass_eq_token = try appendToken(rp.c, .Equal, "=");
- const assign = try transCreateNodeInfixOp(rp, scope, ref_node, .Assign, ass_eq_token, rhs_bin, .used, false);
- try block_scope.statements.append(assign);
- }
-
- const break_node = try transCreateNodeBreak(rp.c, block_scope.label, ref_node);
- try block_scope.statements.append(&break_node.base);
- const block_node = try block_scope.complete(rp.c);
- const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
- grouped_expr.* = .{
- .lparen = try appendToken(rp.c, .LParen, "("),
- .expr = block_node,
- .rparen = try appendToken(rp.c, .RParen, ")"),
- };
- return &grouped_expr.base;
-}
-
-fn transCPtrCast(
- rp: RestorePoint,
- loc: ZigClangSourceLocation,
- dst_type: ZigClangQualType,
- src_type: ZigClangQualType,
- expr: *ast.Node,
-) !*ast.Node {
- const ty = ZigClangQualType_getTypePtr(dst_type);
- const child_type = ZigClangType_getPointeeType(ty);
- const src_ty = ZigClangQualType_getTypePtr(src_type);
- const src_child_type = ZigClangType_getPointeeType(src_ty);
-
- if ((ZigClangQualType_isConstQualified(src_child_type) and
- !ZigClangQualType_isConstQualified(child_type)) or
- (ZigClangQualType_isVolatileQualified(src_child_type) and
- !ZigClangQualType_isVolatileQualified(child_type)))
- {
- // Casting away const or volatile requires us to use @intToPtr
- const inttoptr_node = try rp.c.createBuiltinCall("@intToPtr", 2);
- const dst_type_node = try transType(rp, ty, loc);
- inttoptr_node.params()[0] = dst_type_node;
- _ = try appendToken(rp.c, .Comma, ",");
-
- const ptrtoint_node = try rp.c.createBuiltinCall("@ptrToInt", 1);
- ptrtoint_node.params()[0] = expr;
- ptrtoint_node.rparen_token = try appendToken(rp.c, .RParen, ")");
-
- inttoptr_node.params()[1] = &ptrtoint_node.base;
- inttoptr_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- return &inttoptr_node.base;
- } else {
- // Implicit downcasting from higher to lower alignment values is forbidden,
- // use @alignCast to side-step this problem
- const ptrcast_node = try rp.c.createBuiltinCall("@ptrCast", 2);
- const dst_type_node = try transType(rp, ty, loc);
- ptrcast_node.params()[0] = dst_type_node;
- _ = try appendToken(rp.c, .Comma, ",");
-
- if (ZigClangType_isVoidType(qualTypeCanon(child_type))) {
- // void has 1-byte alignment, so @alignCast is not needed
- ptrcast_node.params()[1] = expr;
- } else if (typeIsOpaque(rp.c, qualTypeCanon(child_type), loc)) {
- // For opaque types a ptrCast is enough
- ptrcast_node.params()[1] = expr;
- } else {
- const aligncast_node = try rp.c.createBuiltinCall("@alignCast", 2);
- const alignof_node = try rp.c.createBuiltinCall("@alignOf", 1);
- const child_type_node = try transQualType(rp, child_type, loc);
- alignof_node.params()[0] = child_type_node;
- alignof_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- aligncast_node.params()[0] = &alignof_node.base;
- _ = try appendToken(rp.c, .Comma, ",");
- aligncast_node.params()[1] = expr;
- aligncast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- ptrcast_node.params()[1] = &aligncast_node.base;
- }
- ptrcast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
-
- return &ptrcast_node.base;
- }
-}
-
-fn transBreak(rp: RestorePoint, scope: *Scope) TransError!*ast.Node {
- const break_scope = scope.getBreakableScope();
- const label_text: ?[]const u8 = if (break_scope.id == .Switch) blk: {
- const swtch = @fieldParentPtr(Scope.Switch, "base", break_scope);
- const block_scope = try scope.findBlockScope(rp.c);
- swtch.switch_label = try block_scope.makeMangledName(rp.c, "switch");
- break :blk swtch.switch_label;
- } else
- null;
-
- var cf = try CtrlFlow.init(rp.c, .Break, label_text);
- const br = try cf.finish(null);
- _ = try appendToken(rp.c, .Semicolon, ";");
- return &br.base;
-}
-
-fn transFloatingLiteral(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangFloatingLiteral, used: ResultUsed) TransError!*ast.Node {
- // TODO use something more accurate
- const dbl = ZigClangAPFloat_getValueAsApproximateDouble(stmt);
- const node = try rp.c.arena.create(ast.Node.OneToken);
- node.* = .{
- .base = .{ .tag = .FloatLiteral },
- .token = try appendTokenFmt(rp.c, .FloatLiteral, "{d}", .{dbl}),
- };
- return maybeSuppressResult(rp, scope, used, &node.base);
-}
-
-fn transBinaryConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangBinaryConditionalOperator, used: ResultUsed) TransError!*ast.Node {
- // GNU extension of the ternary operator where the middle expression is
- // omitted, the conditition itself is returned if it evaluates to true
- const casted_stmt = @ptrCast(*const ZigClangAbstractConditionalOperator, stmt);
- const cond_expr = ZigClangAbstractConditionalOperator_getCond(casted_stmt);
- const true_expr = ZigClangAbstractConditionalOperator_getTrueExpr(casted_stmt);
- const false_expr = ZigClangAbstractConditionalOperator_getFalseExpr(casted_stmt);
-
- // c: (cond_expr)?:(false_expr)
- // zig: (blk: {
- // const _cond_temp = (cond_expr);
- // break :blk if (_cond_temp) _cond_temp else (false_expr);
- // })
- const lparen = try appendToken(rp.c, .LParen, "(");
-
- var block_scope = try Scope.Block.init(rp.c, scope, true);
- defer block_scope.deinit();
-
- const mangled_name = try block_scope.makeMangledName(rp.c, "cond_temp");
- const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
- const name_tok = try appendIdentifier(rp.c, mangled_name);
- const eq_token = try appendToken(rp.c, .Equal, "=");
- const init_node = try transExpr(rp, &block_scope.base, cond_expr, .used, .r_value);
- const semicolon_token = try appendToken(rp.c, .Semicolon, ";");
- const tmp_var = try ast.Node.VarDecl.create(rp.c.arena, .{
- .name_token = name_tok,
- .mut_token = mut_tok,
- .semicolon_token = semicolon_token,
- }, .{
- .eq_token = eq_token,
- .init_node = init_node,
- });
- try block_scope.statements.append(&tmp_var.base);
-
- var break_node_tmp = try CtrlFlow.initToken(rp.c, .Break, block_scope.label);
-
- const if_node = try transCreateNodeIf(rp.c);
- var cond_scope = Scope.Condition{
- .base = .{
- .parent = &block_scope.base,
- .id = .Condition,
- },
- };
- defer cond_scope.deinit();
- const tmp_var_node = try transCreateNodeIdentifier(rp.c, mangled_name);
-
- const ty = ZigClangQualType_getTypePtr(getExprQualType(rp.c, cond_expr));
- const cond_node = try finishBoolExpr(rp, &cond_scope.base, ZigClangExpr_getBeginLoc(cond_expr), ty, tmp_var_node, used);
- if_node.condition = cond_node;
- _ = try appendToken(rp.c, .RParen, ")");
-
- if_node.body = try transCreateNodeIdentifier(rp.c, mangled_name);
- if_node.@"else" = try transCreateNodeElse(rp.c);
- if_node.@"else".?.body = try transExpr(rp, &block_scope.base, false_expr, .used, .r_value);
- _ = try appendToken(rp.c, .Semicolon, ";");
-
- const break_node = try break_node_tmp.finish(&if_node.base);
- _ = try appendToken(rp.c, .Semicolon, ";");
- try block_scope.statements.append(&break_node.base);
- const block_node = try block_scope.complete(rp.c);
-
- const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
- grouped_expr.* = .{
- .lparen = lparen,
- .expr = block_node,
- .rparen = try appendToken(rp.c, .RParen, ")"),
- };
- return maybeSuppressResult(rp, scope, used, &grouped_expr.base);
-}
-
-fn transConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangConditionalOperator, used: ResultUsed) TransError!*ast.Node {
- const grouped = scope.id == .Condition;
- const lparen = if (grouped) try appendToken(rp.c, .LParen, "(") else undefined;
- const if_node = try transCreateNodeIf(rp.c);
- var cond_scope = Scope.Condition{
- .base = .{
- .parent = scope,
- .id = .Condition,
- },
- };
- defer cond_scope.deinit();
-
- const casted_stmt = @ptrCast(*const ZigClangAbstractConditionalOperator, stmt);
- const cond_expr = ZigClangAbstractConditionalOperator_getCond(casted_stmt);
- const true_expr = ZigClangAbstractConditionalOperator_getTrueExpr(casted_stmt);
- const false_expr = ZigClangAbstractConditionalOperator_getFalseExpr(casted_stmt);
-
- if_node.condition = try transBoolExpr(rp, &cond_scope.base, cond_expr, .used, .r_value, false);
- _ = try appendToken(rp.c, .RParen, ")");
-
- if_node.body = try transExpr(rp, scope, true_expr, .used, .r_value);
-
- if_node.@"else" = try transCreateNodeElse(rp.c);
- if_node.@"else".?.body = try transExpr(rp, scope, false_expr, .used, .r_value);
-
- if (grouped) {
- const rparen = try appendToken(rp.c, .RParen, ")");
- const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
- grouped_expr.* = .{
- .lparen = lparen,
- .expr = &if_node.base,
- .rparen = rparen,
- };
- return maybeSuppressResult(rp, scope, used, &grouped_expr.base);
- } else {
- return maybeSuppressResult(rp, scope, used, &if_node.base);
- }
-}
-
-fn maybeSuppressResult(
- rp: RestorePoint,
- scope: *Scope,
- used: ResultUsed,
- result: *ast.Node,
-) TransError!*ast.Node {
- if (used == .used) return result;
- if (scope.id != .Condition) {
- // NOTE: This is backwards, but the semicolon must immediately follow the node.
- _ = try appendToken(rp.c, .Semicolon, ";");
- } else { // TODO is there a way to avoid this hack?
- // this parenthesis must come immediately following the node
- _ = try appendToken(rp.c, .RParen, ")");
- // these need to come before _
- _ = try appendToken(rp.c, .Colon, ":");
- _ = try appendToken(rp.c, .LParen, "(");
- }
- const lhs = try transCreateNodeIdentifier(rp.c, "_");
- const op_token = try appendToken(rp.c, .Equal, "=");
- const op_node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
- op_node.* = .{
- .base = .{ .tag = .Assign },
- .op_token = op_token,
- .lhs = lhs,
- .rhs = result,
- };
- return &op_node.base;
-}
-
-fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: *ast.Node) !void {
- try c.root_decls.append(c.gpa, decl_node);
- _ = try c.global_scope.sym_table.put(name, decl_node);
-}
-
-fn transQualType(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigClangSourceLocation) TypeError!*ast.Node {
- return transType(rp, ZigClangQualType_getTypePtr(qt), source_loc);
-}
-
-/// Produces a Zig AST node by translating a Clang QualType, respecting the width, but modifying the signed-ness.
-/// Asserts the type is an integer.
-fn transQualTypeIntWidthOf(c: *Context, ty: ZigClangQualType, is_signed: bool) TypeError!*ast.Node {
- return transTypeIntWidthOf(c, qualTypeCanon(ty), is_signed);
-}
-
-/// Produces a Zig AST node by translating a Clang Type, respecting the width, but modifying the signed-ness.
-/// Asserts the type is an integer.
-fn transTypeIntWidthOf(c: *Context, ty: *const ZigClangType, is_signed: bool) TypeError!*ast.Node {
- assert(ZigClangType_getTypeClass(ty) == .Builtin);
- const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
- return transCreateNodeIdentifier(c, switch (ZigClangBuiltinType_getKind(builtin_ty)) {
- .Char_U, .Char_S, .UChar, .SChar, .Char8 => if (is_signed) "i8" else "u8",
- .UShort, .Short => if (is_signed) "c_short" else "c_ushort",
- .UInt, .Int => if (is_signed) "c_int" else "c_uint",
- .ULong, .Long => if (is_signed) "c_long" else "c_ulong",
- .ULongLong, .LongLong => if (is_signed) "c_longlong" else "c_ulonglong",
- .UInt128, .Int128 => if (is_signed) "i128" else "u128",
- .Char16 => if (is_signed) "i16" else "u16",
- .Char32 => if (is_signed) "i32" else "u32",
- else => unreachable, // only call this function when it has already been determined the type is int
- });
-}
-
-fn isCBuiltinType(qt: ZigClangQualType, kind: ZigClangBuiltinTypeKind) bool {
- const c_type = qualTypeCanon(qt);
- if (ZigClangType_getTypeClass(c_type) != .Builtin)
- return false;
- const builtin_ty = @ptrCast(*const ZigClangBuiltinType, c_type);
- return ZigClangBuiltinType_getKind(builtin_ty) == kind;
-}
-
-fn qualTypeIsPtr(qt: ZigClangQualType) bool {
- return ZigClangType_getTypeClass(qualTypeCanon(qt)) == .Pointer;
-}
-
-fn qualTypeIsBoolean(qt: ZigClangQualType) bool {
- return ZigClangType_isBooleanType(qualTypeCanon(qt));
-}
-
-fn qualTypeIntBitWidth(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigClangSourceLocation) !u32 {
- const ty = ZigClangQualType_getTypePtr(qt);
-
- switch (ZigClangType_getTypeClass(ty)) {
- .Builtin => {
- const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
-
- switch (ZigClangBuiltinType_getKind(builtin_ty)) {
- .Char_U,
- .UChar,
- .Char_S,
- .SChar,
- => return 8,
- .UInt128,
- .Int128,
- => return 128,
- else => return 0,
- }
-
- unreachable;
- },
- .Typedef => {
- const typedef_ty = @ptrCast(*const ZigClangTypedefType, ty);
- const typedef_decl = ZigClangTypedefType_getDecl(typedef_ty);
- const type_name = try rp.c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, typedef_decl)));
-
- if (mem.eql(u8, type_name, "uint8_t") or mem.eql(u8, type_name, "int8_t")) {
- return 8;
- } else if (mem.eql(u8, type_name, "uint16_t") or mem.eql(u8, type_name, "int16_t")) {
- return 16;
- } else if (mem.eql(u8, type_name, "uint32_t") or mem.eql(u8, type_name, "int32_t")) {
- return 32;
- } else if (mem.eql(u8, type_name, "uint64_t") or mem.eql(u8, type_name, "int64_t")) {
- return 64;
- } else {
- return 0;
- }
- },
- else => return 0,
- }
-
- unreachable;
-}
-
-fn qualTypeToLog2IntRef(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigClangSourceLocation) !*ast.Node {
- const int_bit_width = try qualTypeIntBitWidth(rp, qt, source_loc);
-
- if (int_bit_width != 0) {
- // we can perform the log2 now.
- const cast_bit_width = math.log2_int(u64, int_bit_width);
- const node = try rp.c.arena.create(ast.Node.OneToken);
- node.* = .{
- .base = .{ .tag = .IntegerLiteral },
- .token = try appendTokenFmt(rp.c, .Identifier, "u{}", .{cast_bit_width}),
- };
- return &node.base;
- }
-
- const zig_type_node = try transQualType(rp, qt, source_loc);
-
- // @import("std").math.Log2Int(c_long);
- //
- // FnCall
- // FieldAccess
- // FieldAccess
- // FnCall (.builtin = true)
- // Symbol "import"
- // StringLiteral "std"
- // Symbol "math"
- // Symbol "Log2Int"
- // Symbol (var from above)
-
- const import_fn_call = try rp.c.createBuiltinCall("@import", 1);
- const std_token = try appendToken(rp.c, .StringLiteral, "\"std\"");
- const std_node = try rp.c.arena.create(ast.Node.OneToken);
- std_node.* = .{
- .base = .{ .tag = .StringLiteral },
- .token = std_token,
- };
- import_fn_call.params()[0] = &std_node.base;
- import_fn_call.rparen_token = try appendToken(rp.c, .RParen, ")");
-
- const inner_field_access = try transCreateNodeFieldAccess(rp.c, &import_fn_call.base, "math");
- const outer_field_access = try transCreateNodeFieldAccess(rp.c, inner_field_access, "Log2Int");
- const log2int_fn_call = try rp.c.createCall(outer_field_access, 1);
- log2int_fn_call.params()[0] = zig_type_node;
- log2int_fn_call.rtoken = try appendToken(rp.c, .RParen, ")");
-
- return &log2int_fn_call.base;
-}
-
-fn qualTypeChildIsFnProto(qt: ZigClangQualType) bool {
- const ty = qualTypeCanon(qt);
-
- switch (ZigClangType_getTypeClass(ty)) {
- .FunctionProto, .FunctionNoProto => return true,
- else => return false,
- }
-}
-
-fn qualTypeCanon(qt: ZigClangQualType) *const ZigClangType {
- const canon = ZigClangQualType_getCanonicalType(qt);
- return ZigClangQualType_getTypePtr(canon);
-}
-
-fn getExprQualType(c: *Context, expr: *const ZigClangExpr) ZigClangQualType {
- blk: {
- // If this is a C `char *`, turn it into a `const char *`
- if (ZigClangExpr_getStmtClass(expr) != .ImplicitCastExprClass) break :blk;
- const cast_expr = @ptrCast(*const ZigClangImplicitCastExpr, expr);
- if (ZigClangImplicitCastExpr_getCastKind(cast_expr) != .ArrayToPointerDecay) break :blk;
- const sub_expr = ZigClangImplicitCastExpr_getSubExpr(cast_expr);
- if (ZigClangExpr_getStmtClass(sub_expr) != .StringLiteralClass) break :blk;
- const array_qt = ZigClangExpr_getType(sub_expr);
- const array_type = @ptrCast(*const ZigClangArrayType, ZigClangQualType_getTypePtr(array_qt));
- var pointee_qt = ZigClangArrayType_getElementType(array_type);
- ZigClangQualType_addConst(&pointee_qt);
- return ZigClangASTContext_getPointerType(c.clang_context, pointee_qt);
- }
- return ZigClangExpr_getType(expr);
-}
-
-fn typeIsOpaque(c: *Context, ty: *const ZigClangType, loc: ZigClangSourceLocation) bool {
- switch (ZigClangType_getTypeClass(ty)) {
- .Builtin => {
- const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
- return ZigClangBuiltinType_getKind(builtin_ty) == .Void;
- },
- .Record => {
- const record_ty = @ptrCast(*const ZigClangRecordType, ty);
- const record_decl = ZigClangRecordType_getDecl(record_ty);
- const record_def = ZigClangRecordDecl_getDefinition(record_decl) orelse
- return true;
- var it = ZigClangRecordDecl_field_begin(record_def);
- const end_it = ZigClangRecordDecl_field_end(record_def);
- while (ZigClangRecordDecl_field_iterator_neq(it, end_it)) : (it = ZigClangRecordDecl_field_iterator_next(it)) {
- const field_decl = ZigClangRecordDecl_field_iterator_deref(it);
-
- if (ZigClangFieldDecl_isBitField(field_decl)) {
- return true;
- }
- }
- return false;
- },
- .Elaborated => {
- const elaborated_ty = @ptrCast(*const ZigClangElaboratedType, ty);
- const qt = ZigClangElaboratedType_getNamedType(elaborated_ty);
- return typeIsOpaque(c, ZigClangQualType_getTypePtr(qt), loc);
- },
- .Typedef => {
- const typedef_ty = @ptrCast(*const ZigClangTypedefType, ty);
- const typedef_decl = ZigClangTypedefType_getDecl(typedef_ty);
- const underlying_type = ZigClangTypedefNameDecl_getUnderlyingType(typedef_decl);
- return typeIsOpaque(c, ZigClangQualType_getTypePtr(underlying_type), loc);
- },
- else => return false,
- }
-}
-
-fn cIsInteger(qt: ZigClangQualType) bool {
- return cIsSignedInteger(qt) or cIsUnsignedInteger(qt);
-}
-
-fn cIsUnsignedInteger(qt: ZigClangQualType) bool {
- const c_type = qualTypeCanon(qt);
- if (ZigClangType_getTypeClass(c_type) != .Builtin) return false;
- const builtin_ty = @ptrCast(*const ZigClangBuiltinType, c_type);
- return switch (ZigClangBuiltinType_getKind(builtin_ty)) {
- .Char_U,
- .UChar,
- .Char_S,
- .UShort,
- .UInt,
- .ULong,
- .ULongLong,
- .UInt128,
- .WChar_U,
- => true,
- else => false,
- };
-}
-
-fn cIntTypeToIndex(qt: ZigClangQualType) u8 {
- const c_type = qualTypeCanon(qt);
- assert(ZigClangType_getTypeClass(c_type) == .Builtin);
- const builtin_ty = @ptrCast(*const ZigClangBuiltinType, c_type);
- return switch (ZigClangBuiltinType_getKind(builtin_ty)) {
- .Bool, .Char_U, .Char_S, .UChar, .SChar, .Char8 => 1,
- .WChar_U, .WChar_S => 2,
- .UShort, .Short, .Char16 => 3,
- .UInt, .Int, .Char32 => 4,
- .ULong, .Long => 5,
- .ULongLong, .LongLong => 6,
- .UInt128, .Int128 => 7,
- else => unreachable,
- };
-}
-
-fn cIntTypeCmp(a: ZigClangQualType, b: ZigClangQualType) math.Order {
- const a_index = cIntTypeToIndex(a);
- const b_index = cIntTypeToIndex(b);
- return math.order(a_index, b_index);
-}
-
-fn cIsSignedInteger(qt: ZigClangQualType) bool {
- const c_type = qualTypeCanon(qt);
- if (ZigClangType_getTypeClass(c_type) != .Builtin) return false;
- const builtin_ty = @ptrCast(*const ZigClangBuiltinType, c_type);
- return switch (ZigClangBuiltinType_getKind(builtin_ty)) {
- .SChar,
- .Short,
- .Int,
- .Long,
- .LongLong,
- .Int128,
- .WChar_S,
- => true,
- else => false,
- };
-}
-
-fn cIsFloating(qt: ZigClangQualType) bool {
- const c_type = qualTypeCanon(qt);
- if (ZigClangType_getTypeClass(c_type) != .Builtin) return false;
- const builtin_ty = @ptrCast(*const ZigClangBuiltinType, c_type);
- return switch (ZigClangBuiltinType_getKind(builtin_ty)) {
- .Float,
- .Double,
- .Float128,
- .LongDouble,
- => true,
- else => false,
- };
-}
-
-fn cIsLongLongInteger(qt: ZigClangQualType) bool {
- const c_type = qualTypeCanon(qt);
- if (ZigClangType_getTypeClass(c_type) != .Builtin) return false;
- const builtin_ty = @ptrCast(*const ZigClangBuiltinType, c_type);
- return switch (ZigClangBuiltinType_getKind(builtin_ty)) {
- .LongLong, .ULongLong, .Int128, .UInt128 => true,
- else => false,
- };
-}
-fn transCreateNodeAssign(
- rp: RestorePoint,
- scope: *Scope,
- result_used: ResultUsed,
- lhs: *const ZigClangExpr,
- rhs: *const ZigClangExpr,
-) !*ast.Node {
- // common case
- // c: lhs = rhs
- // zig: lhs = rhs
- if (result_used == .unused) {
- const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value);
- const eq_token = try appendToken(rp.c, .Equal, "=");
- var rhs_node = try transExprCoercing(rp, scope, rhs, .used, .r_value);
- if (!exprIsBooleanType(lhs) and isBoolRes(rhs_node)) {
- const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1);
- builtin_node.params()[0] = rhs_node;
- builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- rhs_node = &builtin_node.base;
- }
- if (scope.id != .Condition)
- _ = try appendToken(rp.c, .Semicolon, ";");
- return transCreateNodeInfixOp(rp, scope, lhs_node, .Assign, eq_token, rhs_node, .used, false);
- }
-
- // worst case
- // c: lhs = rhs
- // zig: (blk: {
- // zig: const _tmp = rhs;
- // zig: lhs = _tmp;
- // zig: break :blk _tmp
- // zig: })
- var block_scope = try Scope.Block.init(rp.c, scope, true);
- defer block_scope.deinit();
-
- const tmp = try block_scope.makeMangledName(rp.c, "tmp");
- const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
- const name_tok = try appendIdentifier(rp.c, tmp);
- const eq_token = try appendToken(rp.c, .Equal, "=");
- var rhs_node = try transExpr(rp, &block_scope.base, rhs, .used, .r_value);
- if (!exprIsBooleanType(lhs) and isBoolRes(rhs_node)) {
- const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1);
- builtin_node.params()[0] = rhs_node;
- builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- rhs_node = &builtin_node.base;
- }
- const init_node = rhs_node;
- const semicolon_token = try appendToken(rp.c, .Semicolon, ";");
- const node = try ast.Node.VarDecl.create(rp.c.arena, .{
- .name_token = name_tok,
- .mut_token = mut_tok,
- .semicolon_token = semicolon_token,
- }, .{
- .eq_token = eq_token,
- .init_node = init_node,
- });
- try block_scope.statements.append(&node.base);
-
- const lhs_node = try transExpr(rp, &block_scope.base, lhs, .used, .l_value);
- const lhs_eq_token = try appendToken(rp.c, .Equal, "=");
- const ident = try transCreateNodeIdentifier(rp.c, tmp);
- _ = try appendToken(rp.c, .Semicolon, ";");
-
- const assign = try transCreateNodeInfixOp(rp, &block_scope.base, lhs_node, .Assign, lhs_eq_token, ident, .used, false);
- try block_scope.statements.append(assign);
-
- const break_node = blk: {
- var tmp_ctrl_flow = try CtrlFlow.init(rp.c, .Break, tokenSlice(rp.c, block_scope.label.?));
- const rhs_expr = try transCreateNodeIdentifier(rp.c, tmp);
- break :blk try tmp_ctrl_flow.finish(rhs_expr);
- };
- _ = try appendToken(rp.c, .Semicolon, ";");
- try block_scope.statements.append(&break_node.base);
- const block_node = try block_scope.complete(rp.c);
- // semicolon must immediately follow rbrace because it is the last token in a block
- _ = try appendToken(rp.c, .Semicolon, ";");
- return block_node;
-}
-
-fn transCreateNodeFieldAccess(c: *Context, container: *ast.Node, field_name: []const u8) !*ast.Node {
- const field_access_node = try c.arena.create(ast.Node.SimpleInfixOp);
- field_access_node.* = .{
- .base = .{ .tag = .Period },
- .op_token = try appendToken(c, .Period, "."),
- .lhs = container,
- .rhs = try transCreateNodeIdentifier(c, field_name),
- };
- return &field_access_node.base;
-}
-
-fn transCreateNodeSimplePrefixOp(
- c: *Context,
- comptime tag: ast.Node.Tag,
- op_tok_id: std.zig.Token.Id,
- bytes: []const u8,
-) !*ast.Node.SimplePrefixOp {
- const node = try c.arena.create(ast.Node.SimplePrefixOp);
- node.* = .{
- .base = .{ .tag = tag },
- .op_token = try appendToken(c, op_tok_id, bytes),
- .rhs = undefined, // translate and set afterward
- };
- return node;
-}
-
-fn transCreateNodeInfixOp(
- rp: RestorePoint,
- scope: *Scope,
- lhs_node: *ast.Node,
- op: ast.Node.Tag,
- op_token: ast.TokenIndex,
- rhs_node: *ast.Node,
- used: ResultUsed,
- grouped: bool,
-) !*ast.Node {
- var lparen = if (grouped)
- try appendToken(rp.c, .LParen, "(")
- else
- null;
- const node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
- node.* = .{
- .base = .{ .tag = op },
- .op_token = op_token,
- .lhs = lhs_node,
- .rhs = rhs_node,
- };
- if (!grouped) return maybeSuppressResult(rp, scope, used, &node.base);
- const rparen = try appendToken(rp.c, .RParen, ")");
- const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
- grouped_expr.* = .{
- .lparen = lparen.?,
- .expr = &node.base,
- .rparen = rparen,
- };
- return maybeSuppressResult(rp, scope, used, &grouped_expr.base);
-}
-
-fn transCreateNodeBoolInfixOp(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangBinaryOperator,
- op: ast.Node.Tag,
- used: ResultUsed,
- grouped: bool,
-) !*ast.Node {
- std.debug.assert(op == .BoolAnd or op == .BoolOr);
-
- const lhs_hode = try transBoolExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value, true);
- const op_token = if (op == .BoolAnd)
- try appendToken(rp.c, .Keyword_and, "and")
- else
- try appendToken(rp.c, .Keyword_or, "or");
- const rhs = try transBoolExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value, true);
-
- return transCreateNodeInfixOp(
- rp,
- scope,
- lhs_hode,
- op,
- op_token,
- rhs,
- used,
- grouped,
- );
-}
-
-fn transCreateNodePtrType(
- c: *Context,
- is_const: bool,
- is_volatile: bool,
- op_tok_id: std.zig.Token.Id,
-) !*ast.Node.PtrType {
- const node = try c.arena.create(ast.Node.PtrType);
- const op_token = switch (op_tok_id) {
- .LBracket => blk: {
- const lbracket = try appendToken(c, .LBracket, "[");
- _ = try appendToken(c, .Asterisk, "*");
- _ = try appendToken(c, .RBracket, "]");
- break :blk lbracket;
- },
- .Identifier => blk: {
- const lbracket = try appendToken(c, .LBracket, "["); // Rendering checks if this token + 2 == .Identifier, so needs to return this token
- _ = try appendToken(c, .Asterisk, "*");
- _ = try appendIdentifier(c, "c");
- _ = try appendToken(c, .RBracket, "]");
- break :blk lbracket;
- },
- .Asterisk => try appendToken(c, .Asterisk, "*"),
- else => unreachable,
- };
- node.* = .{
- .op_token = op_token,
- .ptr_info = .{
- .const_token = if (is_const) try appendToken(c, .Keyword_const, "const") else null,
- .volatile_token = if (is_volatile) try appendToken(c, .Keyword_volatile, "volatile") else null,
- },
- .rhs = undefined, // translate and set afterward
- };
- return node;
-}
-
-fn transCreateNodeAPInt(c: *Context, int: *const ZigClangAPSInt) !*ast.Node {
- const num_limbs = math.cast(usize, ZigClangAPSInt_getNumWords(int)) catch |err| switch (err) {
- error.Overflow => return error.OutOfMemory,
- };
- var aps_int = int;
- const is_negative = ZigClangAPSInt_isSigned(int) and ZigClangAPSInt_isNegative(int);
- if (is_negative) aps_int = ZigClangAPSInt_negate(aps_int);
- defer if (is_negative) {
- ZigClangAPSInt_free(aps_int);
- };
-
- const limbs = try c.arena.alloc(math.big.Limb, num_limbs);
- defer c.arena.free(limbs);
-
- const data = ZigClangAPSInt_getRawData(aps_int);
- switch (@sizeOf(math.big.Limb)) {
- 8 => {
- var i: usize = 0;
- while (i < num_limbs) : (i += 1) {
- limbs[i] = data[i];
- }
- },
- 4 => {
- var limb_i: usize = 0;
- var data_i: usize = 0;
- while (limb_i < num_limbs) : ({
- limb_i += 2;
- data_i += 1;
- }) {
- limbs[limb_i] = @truncate(u32, data[data_i]);
- limbs[limb_i + 1] = @truncate(u32, data[data_i] >> 32);
- }
- },
- else => @compileError("unimplemented"),
- }
-
- const big: math.big.int.Const = .{ .limbs = limbs, .positive = !is_negative };
- const str = big.toStringAlloc(c.arena, 10, false) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- };
- defer c.arena.free(str);
- const token = try appendToken(c, .IntegerLiteral, str);
- const node = try c.arena.create(ast.Node.OneToken);
- node.* = .{
- .base = .{ .tag = .IntegerLiteral },
- .token = token,
- };
- return &node.base;
-}
-
-fn transCreateNodeUndefinedLiteral(c: *Context) !*ast.Node {
- const token = try appendToken(c, .Keyword_undefined, "undefined");
- const node = try c.arena.create(ast.Node.OneToken);
- node.* = .{
- .base = .{ .tag = .UndefinedLiteral },
- .token = token,
- };
- return &node.base;
-}
-
-fn transCreateNodeNullLiteral(c: *Context) !*ast.Node {
- const token = try appendToken(c, .Keyword_null, "null");
- const node = try c.arena.create(ast.Node.OneToken);
- node.* = .{
- .base = .{ .tag = .NullLiteral },
- .token = token,
- };
- return &node.base;
-}
-
-fn transCreateNodeBoolLiteral(c: *Context, value: bool) !*ast.Node {
- const token = if (value)
- try appendToken(c, .Keyword_true, "true")
- else
- try appendToken(c, .Keyword_false, "false");
- const node = try c.arena.create(ast.Node.OneToken);
- node.* = .{
- .base = .{ .tag = .BoolLiteral },
- .token = token,
- };
- return &node.base;
-}
-
-fn transCreateNodeInt(c: *Context, int: anytype) !*ast.Node {
- const token = try appendTokenFmt(c, .IntegerLiteral, "{}", .{int});
- const node = try c.arena.create(ast.Node.OneToken);
- node.* = .{
- .base = .{ .tag = .IntegerLiteral },
- .token = token,
- };
- return &node.base;
-}
-
-fn transCreateNodeFloat(c: *Context, int: anytype) !*ast.Node {
- const token = try appendTokenFmt(c, .FloatLiteral, "{}", .{int});
- const node = try c.arena.create(ast.Node.OneToken);
- node.* = .{
- .base = .{ .tag = .FloatLiteral },
- .token = token,
- };
- return &node.base;
-}
-
-fn transCreateNodeOpaqueType(c: *Context) !*ast.Node {
- const call_node = try c.createBuiltinCall("@Type", 1);
- call_node.params()[0] = try transCreateNodeEnumLiteral(c, "Opaque");
- call_node.rparen_token = try appendToken(c, .RParen, ")");
- return &call_node.base;
-}
-
-fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_alias: *ast.Node.FnProto) !*ast.Node {
- const scope = &c.global_scope.base;
-
- const pub_tok = try appendToken(c, .Keyword_pub, "pub");
- const inline_tok = try appendToken(c, .Keyword_inline, "inline");
- const fn_tok = try appendToken(c, .Keyword_fn, "fn");
- const name_tok = try appendIdentifier(c, name);
- _ = try appendToken(c, .LParen, "(");
-
- var fn_params = std.ArrayList(ast.Node.FnProto.ParamDecl).init(c.gpa);
- defer fn_params.deinit();
-
- for (proto_alias.params()) |param, i| {
- if (i != 0) {
- _ = try appendToken(c, .Comma, ",");
- }
- const param_name_tok = param.name_token orelse
- try appendTokenFmt(c, .Identifier, "arg_{}", .{c.getMangle()});
-
- _ = try appendToken(c, .Colon, ":");
-
- (try fn_params.addOne()).* = .{
- .doc_comments = null,
- .comptime_token = null,
- .noalias_token = param.noalias_token,
- .name_token = param_name_tok,
- .param_type = param.param_type,
- };
- }
-
- _ = try appendToken(c, .RParen, ")");
-
- const block_lbrace = try appendToken(c, .LBrace, "{");
-
- const return_kw = try appendToken(c, .Keyword_return, "return");
- const unwrap_expr = try transCreateNodeUnwrapNull(c, ref.cast(ast.Node.VarDecl).?.getInitNode().?);
-
- const call_expr = try c.createCall(unwrap_expr, fn_params.items.len);
- const call_params = call_expr.params();
-
- for (fn_params.items) |param, i| {
- if (i != 0) {
- _ = try appendToken(c, .Comma, ",");
- }
- call_params[i] = try transCreateNodeIdentifier(c, tokenSlice(c, param.name_token.?));
- }
- call_expr.rtoken = try appendToken(c, .RParen, ")");
-
- const return_expr = try ast.Node.ControlFlowExpression.create(c.arena, .{
- .ltoken = return_kw,
- .tag = .Return,
- }, .{
- .rhs = &call_expr.base,
- });
- _ = try appendToken(c, .Semicolon, ";");
-
- const block = try ast.Node.Block.alloc(c.arena, 1);
- block.* = .{
- .lbrace = block_lbrace,
- .statements_len = 1,
- .rbrace = try appendToken(c, .RBrace, "}"),
- };
- block.statements()[0] = &return_expr.base;
-
- const fn_proto = try ast.Node.FnProto.create(c.arena, .{
- .params_len = fn_params.items.len,
- .fn_token = fn_tok,
- .return_type = proto_alias.return_type,
- }, .{
- .visib_token = pub_tok,
- .name_token = name_tok,
- .extern_export_inline_token = inline_tok,
- .body_node = &block.base,
- });
- mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items);
- return &fn_proto.base;
-}
-
-fn transCreateNodeUnwrapNull(c: *Context, wrapped: *ast.Node) !*ast.Node {
- _ = try appendToken(c, .Period, ".");
- const qm = try appendToken(c, .QuestionMark, "?");
- const node = try c.arena.create(ast.Node.SimpleSuffixOp);
- node.* = .{
- .base = .{ .tag = .UnwrapOptional },
- .lhs = wrapped,
- .rtoken = qm,
- };
- return &node.base;
-}
-
-fn transCreateNodeEnumLiteral(c: *Context, name: []const u8) !*ast.Node {
- const node = try c.arena.create(ast.Node.EnumLiteral);
- node.* = .{
- .dot = try appendToken(c, .Period, "."),
- .name = try appendIdentifier(c, name),
- };
- return &node.base;
-}
-
-fn transCreateNodeStringLiteral(c: *Context, str: []const u8) !*ast.Node {
- const node = try c.arena.create(ast.Node.OneToken);
- node.* = .{
- .base = .{ .tag = .StringLiteral },
- .token = try appendToken(c, .StringLiteral, str),
- };
- return &node.base;
-}
-
-fn transCreateNodeIf(c: *Context) !*ast.Node.If {
- const if_tok = try appendToken(c, .Keyword_if, "if");
- _ = try appendToken(c, .LParen, "(");
- const node = try c.arena.create(ast.Node.If);
- node.* = .{
- .if_token = if_tok,
- .condition = undefined,
- .payload = null,
- .body = undefined,
- .@"else" = null,
- };
- return node;
-}
-
-fn transCreateNodeElse(c: *Context) !*ast.Node.Else {
- const node = try c.arena.create(ast.Node.Else);
- node.* = .{
- .else_token = try appendToken(c, .Keyword_else, "else"),
- .payload = null,
- .body = undefined,
- };
- return node;
-}
-
-fn transCreateNodeBreak(
- c: *Context,
- label: ?ast.TokenIndex,
- rhs: ?*ast.Node,
-) !*ast.Node.ControlFlowExpression {
- var ctrl_flow = try CtrlFlow.init(c, .Break, if (label) |l| tokenSlice(c, l) else null);
- return ctrl_flow.finish(rhs);
-}
-
-const CtrlFlow = struct {
- c: *Context,
- ltoken: ast.TokenIndex,
- label_token: ?ast.TokenIndex,
- tag: ast.Node.Tag,
-
- /// Does everything except the RHS.
- fn init(c: *Context, tag: ast.Node.Tag, label: ?[]const u8) !CtrlFlow {
- const kw: Token.Id = switch (tag) {
- .Break => .Keyword_break,
- .Continue => .Keyword_continue,
- .Return => .Keyword_return,
- else => unreachable,
- };
- const kw_text = switch (tag) {
- .Break => "break",
- .Continue => "continue",
- .Return => "return",
- else => unreachable,
- };
- const ltoken = try appendToken(c, kw, kw_text);
- const label_token = if (label) |l| blk: {
- _ = try appendToken(c, .Colon, ":");
- break :blk try appendIdentifier(c, l);
- } else null;
- return CtrlFlow{
- .c = c,
- .ltoken = ltoken,
- .label_token = label_token,
- .tag = tag,
- };
- }
-
- fn initToken(c: *Context, tag: ast.Node.Tag, label: ?ast.TokenIndex) !CtrlFlow {
- const other_token = label orelse return init(c, tag, null);
- const loc = c.token_locs.items[other_token];
- const label_name = c.source_buffer.items[loc.start..loc.end];
- return init(c, tag, label_name);
- }
-
- fn finish(self: *CtrlFlow, rhs: ?*ast.Node) !*ast.Node.ControlFlowExpression {
- return ast.Node.ControlFlowExpression.create(self.c.arena, .{
- .ltoken = self.ltoken,
- .tag = self.tag,
- }, .{
- .label = self.label_token,
- .rhs = rhs,
- });
- }
-};
-
-fn transCreateNodeWhile(c: *Context) !*ast.Node.While {
- const while_tok = try appendToken(c, .Keyword_while, "while");
- _ = try appendToken(c, .LParen, "(");
-
- const node = try c.arena.create(ast.Node.While);
- node.* = .{
- .label = null,
- .inline_token = null,
- .while_token = while_tok,
- .condition = undefined,
- .payload = null,
- .continue_expr = null,
- .body = undefined,
- .@"else" = null,
- };
- return node;
-}
-
-fn transCreateNodeContinue(c: *Context) !*ast.Node {
- const ltoken = try appendToken(c, .Keyword_continue, "continue");
- const node = try ast.Node.ControlFlowExpression.create(c.arena, .{
- .ltoken = ltoken,
- .tag = .Continue,
- }, .{});
- _ = try appendToken(c, .Semicolon, ";");
- return &node.base;
-}
-
-fn transCreateNodeSwitchCase(c: *Context, lhs: *ast.Node) !*ast.Node.SwitchCase {
- const arrow_tok = try appendToken(c, .EqualAngleBracketRight, "=>");
-
- const node = try ast.Node.SwitchCase.alloc(c.arena, 1);
- node.* = .{
- .items_len = 1,
- .arrow_token = arrow_tok,
- .payload = null,
- .expr = undefined,
- };
- node.items()[0] = lhs;
- return node;
-}
-
-fn transCreateNodeSwitchElse(c: *Context) !*ast.Node {
- const node = try c.arena.create(ast.Node.SwitchElse);
- node.* = .{
- .token = try appendToken(c, .Keyword_else, "else"),
- };
- return &node.base;
-}
-
-fn transCreateNodeShiftOp(
- rp: RestorePoint,
- scope: *Scope,
- stmt: *const ZigClangBinaryOperator,
- op: ast.Node.Tag,
- op_tok_id: std.zig.Token.Id,
- bytes: []const u8,
-) !*ast.Node {
- std.debug.assert(op == .BitShiftLeft or op == .BitShiftRight);
-
- const lhs_expr = ZigClangBinaryOperator_getLHS(stmt);
- const rhs_expr = ZigClangBinaryOperator_getRHS(stmt);
- const rhs_location = ZigClangExpr_getBeginLoc(rhs_expr);
- // lhs >> @as(u5, rh)
-
- const lhs = try transExpr(rp, scope, lhs_expr, .used, .l_value);
- const op_token = try appendToken(rp.c, op_tok_id, bytes);
-
- const cast_node = try rp.c.createBuiltinCall("@intCast", 2);
- const rhs_type = try qualTypeToLog2IntRef(rp, ZigClangBinaryOperator_getType(stmt), rhs_location);
- cast_node.params()[0] = rhs_type;
- _ = try appendToken(rp.c, .Comma, ",");
- const rhs = try transExprCoercing(rp, scope, rhs_expr, .used, .r_value);
- cast_node.params()[1] = rhs;
- cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
-
- const node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
- node.* = .{
- .base = .{ .tag = op },
- .op_token = op_token,
- .lhs = lhs,
- .rhs = &cast_node.base,
- };
-
- return &node.base;
-}
-
-fn transCreateNodePtrDeref(c: *Context, lhs: *ast.Node) !*ast.Node {
- const node = try c.arena.create(ast.Node.SimpleSuffixOp);
- node.* = .{
- .base = .{ .tag = .Deref },
- .lhs = lhs,
- .rtoken = try appendToken(c, .PeriodAsterisk, ".*"),
- };
- return &node.base;
-}
-
-fn transCreateNodeArrayAccess(c: *Context, lhs: *ast.Node) !*ast.Node.ArrayAccess {
- _ = try appendToken(c, .LBrace, "[");
- const node = try c.arena.create(ast.Node.ArrayAccess);
- node.* = .{
- .lhs = lhs,
- .index_expr = undefined,
- .rtoken = undefined,
- };
- return node;
-}
-
-const RestorePoint = struct {
- c: *Context,
- token_index: ast.TokenIndex,
- src_buf_index: usize,
-
- fn activate(self: RestorePoint) void {
- self.c.token_ids.shrink(self.c.gpa, self.token_index);
- self.c.token_locs.shrink(self.c.gpa, self.token_index);
- self.c.source_buffer.shrink(self.src_buf_index);
- }
-};
-
-fn makeRestorePoint(c: *Context) RestorePoint {
- return RestorePoint{
- .c = c,
- .token_index = c.token_ids.items.len,
- .src_buf_index = c.source_buffer.items.len,
- };
-}
-
-fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSourceLocation) TypeError!*ast.Node {
- switch (ZigClangType_getTypeClass(ty)) {
- .Builtin => {
- const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
- return transCreateNodeIdentifier(rp.c, switch (ZigClangBuiltinType_getKind(builtin_ty)) {
- .Void => "c_void",
- .Bool => "bool",
- .Char_U, .UChar, .Char_S, .Char8 => "u8",
- .SChar => "i8",
- .UShort => "c_ushort",
- .UInt => "c_uint",
- .ULong => "c_ulong",
- .ULongLong => "c_ulonglong",
- .Short => "c_short",
- .Int => "c_int",
- .Long => "c_long",
- .LongLong => "c_longlong",
- .UInt128 => "u128",
- .Int128 => "i128",
- .Float => "f32",
- .Double => "f64",
- .Float128 => "f128",
- .Float16 => "f16",
- .LongDouble => "c_longdouble",
- else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported builtin type", .{}),
- });
- },
- .FunctionProto => {
- const fn_proto_ty = @ptrCast(*const ZigClangFunctionProtoType, ty);
- const fn_proto = try transFnProto(rp, null, fn_proto_ty, source_loc, null, false);
- return &fn_proto.base;
- },
- .FunctionNoProto => {
- const fn_no_proto_ty = @ptrCast(*const ZigClangFunctionType, ty);
- const fn_proto = try transFnNoProto(rp, fn_no_proto_ty, source_loc, null, false);
- return &fn_proto.base;
- },
- .Paren => {
- const paren_ty = @ptrCast(*const ZigClangParenType, ty);
- return transQualType(rp, ZigClangParenType_getInnerType(paren_ty), source_loc);
- },
- .Pointer => {
- const child_qt = ZigClangType_getPointeeType(ty);
- if (qualTypeChildIsFnProto(child_qt)) {
- const optional_node = try transCreateNodeSimplePrefixOp(rp.c, .OptionalType, .QuestionMark, "?");
- optional_node.rhs = try transQualType(rp, child_qt, source_loc);
- return &optional_node.base;
- }
- if (typeIsOpaque(rp.c, ZigClangQualType_getTypePtr(child_qt), source_loc)) {
- const optional_node = try transCreateNodeSimplePrefixOp(rp.c, .OptionalType, .QuestionMark, "?");
- const pointer_node = try transCreateNodePtrType(
- rp.c,
- ZigClangQualType_isConstQualified(child_qt),
- ZigClangQualType_isVolatileQualified(child_qt),
- .Asterisk,
- );
- optional_node.rhs = &pointer_node.base;
- pointer_node.rhs = try transQualType(rp, child_qt, source_loc);
- return &optional_node.base;
- }
- const pointer_node = try transCreateNodePtrType(
- rp.c,
- ZigClangQualType_isConstQualified(child_qt),
- ZigClangQualType_isVolatileQualified(child_qt),
- .Identifier,
- );
- pointer_node.rhs = try transQualType(rp, child_qt, source_loc);
- return &pointer_node.base;
- },
- .ConstantArray => {
- const const_arr_ty = @ptrCast(*const ZigClangConstantArrayType, ty);
-
- const size_ap_int = ZigClangConstantArrayType_getSize(const_arr_ty);
- const size = ZigClangAPInt_getLimitedValue(size_ap_int, math.maxInt(usize));
- const elem_ty = ZigClangQualType_getTypePtr(ZigClangConstantArrayType_getElementType(const_arr_ty));
- return try transCreateNodeArrayType(rp, source_loc, elem_ty, size);
- },
- .IncompleteArray => {
- const incomplete_array_ty = @ptrCast(*const ZigClangIncompleteArrayType, ty);
-
- const child_qt = ZigClangIncompleteArrayType_getElementType(incomplete_array_ty);
- var node = try transCreateNodePtrType(
- rp.c,
- ZigClangQualType_isConstQualified(child_qt),
- ZigClangQualType_isVolatileQualified(child_qt),
- .Identifier,
- );
- node.rhs = try transQualType(rp, child_qt, source_loc);
- return &node.base;
- },
- .Typedef => {
- const typedef_ty = @ptrCast(*const ZigClangTypedefType, ty);
-
- const typedef_decl = ZigClangTypedefType_getDecl(typedef_ty);
- return (try transTypeDef(rp.c, typedef_decl, false)) orelse
- revertAndWarn(rp, error.UnsupportedType, source_loc, "unable to translate typedef declaration", .{});
- },
- .Record => {
- const record_ty = @ptrCast(*const ZigClangRecordType, ty);
-
- const record_decl = ZigClangRecordType_getDecl(record_ty);
- return (try transRecordDecl(rp.c, record_decl)) orelse
- revertAndWarn(rp, error.UnsupportedType, source_loc, "unable to resolve record declaration", .{});
- },
- .Enum => {
- const enum_ty = @ptrCast(*const ZigClangEnumType, ty);
-
- const enum_decl = ZigClangEnumType_getDecl(enum_ty);
- return (try transEnumDecl(rp.c, enum_decl)) orelse
- revertAndWarn(rp, error.UnsupportedType, source_loc, "unable to translate enum declaration", .{});
- },
- .Elaborated => {
- const elaborated_ty = @ptrCast(*const ZigClangElaboratedType, ty);
- return transQualType(rp, ZigClangElaboratedType_getNamedType(elaborated_ty), source_loc);
- },
- .Decayed => {
- const decayed_ty = @ptrCast(*const ZigClangDecayedType, ty);
- return transQualType(rp, ZigClangDecayedType_getDecayedType(decayed_ty), source_loc);
- },
- .Attributed => {
- const attributed_ty = @ptrCast(*const ZigClangAttributedType, ty);
- return transQualType(rp, ZigClangAttributedType_getEquivalentType(attributed_ty), source_loc);
- },
- .MacroQualified => {
- const macroqualified_ty = @ptrCast(*const ZigClangMacroQualifiedType, ty);
- return transQualType(rp, ZigClangMacroQualifiedType_getModifiedType(macroqualified_ty), source_loc);
- },
- else => {
- const type_name = rp.c.str(ZigClangType_getTypeClassName(ty));
- return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported type: '{}'", .{type_name});
- },
- }
-}
-
-fn isCVoid(qt: ZigClangQualType) bool {
- const ty = ZigClangQualType_getTypePtr(qt);
- if (ZigClangType_getTypeClass(ty) == .Builtin) {
- const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
- return ZigClangBuiltinType_getKind(builtin_ty) == .Void;
- }
- return false;
-}
-
-const FnDeclContext = struct {
- fn_name: []const u8,
- has_body: bool,
- storage_class: ZigClangStorageClass,
- is_export: bool,
-};
-
-fn transCC(
- rp: RestorePoint,
- fn_ty: *const ZigClangFunctionType,
- source_loc: ZigClangSourceLocation,
-) !CallingConvention {
- const clang_cc = ZigClangFunctionType_getCallConv(fn_ty);
- switch (clang_cc) {
- .C => return CallingConvention.C,
- .X86StdCall => return CallingConvention.Stdcall,
- .X86FastCall => return CallingConvention.Fastcall,
- .X86VectorCall, .AArch64VectorCall => return CallingConvention.Vectorcall,
- .X86ThisCall => return CallingConvention.Thiscall,
- .AAPCS => return CallingConvention.AAPCS,
- .AAPCS_VFP => return CallingConvention.AAPCSVFP,
- else => return revertAndWarn(
- rp,
- error.UnsupportedType,
- source_loc,
- "unsupported calling convention: {}",
- .{@tagName(clang_cc)},
- ),
- }
-}
-
-fn transFnProto(
- rp: RestorePoint,
- fn_decl: ?*const ZigClangFunctionDecl,
- fn_proto_ty: *const ZigClangFunctionProtoType,
- source_loc: ZigClangSourceLocation,
- fn_decl_context: ?FnDeclContext,
- is_pub: bool,
-) !*ast.Node.FnProto {
- const fn_ty = @ptrCast(*const ZigClangFunctionType, fn_proto_ty);
- const cc = try transCC(rp, fn_ty, source_loc);
- const is_var_args = ZigClangFunctionProtoType_isVariadic(fn_proto_ty);
- return finishTransFnProto(rp, fn_decl, fn_proto_ty, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub);
-}
-
-fn transFnNoProto(
- rp: RestorePoint,
- fn_ty: *const ZigClangFunctionType,
- source_loc: ZigClangSourceLocation,
- fn_decl_context: ?FnDeclContext,
- is_pub: bool,
-) !*ast.Node.FnProto {
- const cc = try transCC(rp, fn_ty, source_loc);
- const is_var_args = if (fn_decl_context) |ctx| !ctx.is_export else true;
- return finishTransFnProto(rp, null, null, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub);
-}
-
-fn finishTransFnProto(
- rp: RestorePoint,
- fn_decl: ?*const ZigClangFunctionDecl,
- fn_proto_ty: ?*const ZigClangFunctionProtoType,
- fn_ty: *const ZigClangFunctionType,
- source_loc: ZigClangSourceLocation,
- fn_decl_context: ?FnDeclContext,
- is_var_args: bool,
- cc: CallingConvention,
- is_pub: bool,
-) !*ast.Node.FnProto {
- const is_export = if (fn_decl_context) |ctx| ctx.is_export else false;
- const is_extern = if (fn_decl_context) |ctx| !ctx.has_body else false;
-
- // TODO check for always_inline attribute
- // TODO check for align attribute
-
- // pub extern fn name(...) T
- const pub_tok = if (is_pub) try appendToken(rp.c, .Keyword_pub, "pub") else null;
- const extern_export_inline_tok = if (is_export)
- try appendToken(rp.c, .Keyword_export, "export")
- else if (is_extern)
- try appendToken(rp.c, .Keyword_extern, "extern")
- else
- null;
- const fn_tok = try appendToken(rp.c, .Keyword_fn, "fn");
- const name_tok = if (fn_decl_context) |ctx| try appendIdentifier(rp.c, ctx.fn_name) else null;
- const lparen_tok = try appendToken(rp.c, .LParen, "(");
-
- var fn_params = std.ArrayList(ast.Node.FnProto.ParamDecl).init(rp.c.gpa);
- defer fn_params.deinit();
- const param_count: usize = if (fn_proto_ty != null) ZigClangFunctionProtoType_getNumParams(fn_proto_ty.?) else 0;
- try fn_params.ensureCapacity(param_count + 1); // +1 for possible var args node
-
- var i: usize = 0;
- while (i < param_count) : (i += 1) {
- const param_qt = ZigClangFunctionProtoType_getParamType(fn_proto_ty.?, @intCast(c_uint, i));
-
- const noalias_tok = if (ZigClangQualType_isRestrictQualified(param_qt)) try appendToken(rp.c, .Keyword_noalias, "noalias") else null;
-
- const param_name_tok: ?ast.TokenIndex = blk: {
- if (fn_decl) |decl| {
- const param = ZigClangFunctionDecl_getParamDecl(decl, @intCast(c_uint, i));
- const param_name: []const u8 = try rp.c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, param)));
- if (param_name.len < 1)
- break :blk null;
-
- const result = try appendIdentifier(rp.c, param_name);
- _ = try appendToken(rp.c, .Colon, ":");
- break :blk result;
- }
- break :blk null;
- };
-
- const type_node = try transQualType(rp, param_qt, source_loc);
-
- fn_params.addOneAssumeCapacity().* = .{
- .doc_comments = null,
- .comptime_token = null,
- .noalias_token = noalias_tok,
- .name_token = param_name_tok,
- .param_type = .{ .type_expr = type_node },
- };
-
- if (i + 1 < param_count) {
- _ = try appendToken(rp.c, .Comma, ",");
- }
- }
-
- const var_args_token: ?ast.TokenIndex = if (is_var_args) blk: {
- if (param_count > 0) {
- _ = try appendToken(rp.c, .Comma, ",");
- }
- break :blk try appendToken(rp.c, .Ellipsis3, "...");
- } else null;
-
- const rparen_tok = try appendToken(rp.c, .RParen, ")");
-
- const linksection_expr = blk: {
- if (fn_decl) |decl| {
- var str_len: usize = undefined;
- if (ZigClangFunctionDecl_getSectionAttribute(decl, &str_len)) |str_ptr| {
- _ = try appendToken(rp.c, .Keyword_linksection, "linksection");
- _ = try appendToken(rp.c, .LParen, "(");
- const expr = try transCreateNodeStringLiteral(
- rp.c,
- try std.fmt.allocPrint(rp.c.arena, "\"{}\"", .{str_ptr[0..str_len]}),
- );
- _ = try appendToken(rp.c, .RParen, ")");
-
- break :blk expr;
- }
- }
- break :blk null;
- };
-
- const align_expr = blk: {
- if (fn_decl) |decl| {
- const alignment = ZigClangFunctionDecl_getAlignedAttribute(decl, rp.c.clang_context);
- if (alignment != 0) {
- _ = try appendToken(rp.c, .Keyword_align, "align");
- _ = try appendToken(rp.c, .LParen, "(");
- // Clang reports the alignment in bits
- const expr = try transCreateNodeInt(rp.c, alignment / 8);
- _ = try appendToken(rp.c, .RParen, ")");
-
- break :blk expr;
- }
- }
- break :blk null;
- };
-
- const callconv_expr = if ((is_export or is_extern) and cc == .C) null else blk: {
- _ = try appendToken(rp.c, .Keyword_callconv, "callconv");
- _ = try appendToken(rp.c, .LParen, "(");
- const expr = try transCreateNodeEnumLiteral(rp.c, @tagName(cc));
- _ = try appendToken(rp.c, .RParen, ")");
- break :blk expr;
- };
-
- const return_type_node = blk: {
- if (ZigClangFunctionType_getNoReturnAttr(fn_ty)) {
- break :blk try transCreateNodeIdentifier(rp.c, "noreturn");
- } else {
- const return_qt = ZigClangFunctionType_getReturnType(fn_ty);
- if (isCVoid(return_qt)) {
- // convert primitive c_void to actual void (only for return type)
- break :blk try transCreateNodeIdentifier(rp.c, "void");
- } else {
- break :blk transQualType(rp, return_qt, source_loc) catch |err| switch (err) {
- error.UnsupportedType => {
- try emitWarning(rp.c, source_loc, "unsupported function proto return type", .{});
- return err;
- },
- error.OutOfMemory => |e| return e,
- };
- }
- }
- };
-
- // We need to reserve an undefined (but non-null) body node to set later.
- var body_node: ?*ast.Node = null;
- if (fn_decl_context) |ctx| {
- if (ctx.has_body) {
- // TODO: we should be able to use undefined here but
- // it causes a bug. This is undefined without zig language
- // being aware of it.
- body_node = @intToPtr(*ast.Node, 0x08);
- }
- }
-
- const fn_proto = try ast.Node.FnProto.create(rp.c.arena, .{
- .params_len = fn_params.items.len,
- .return_type = .{ .Explicit = return_type_node },
- .fn_token = fn_tok,
- }, .{
- .visib_token = pub_tok,
- .name_token = name_tok,
- .extern_export_inline_token = extern_export_inline_tok,
- .align_expr = align_expr,
- .section_expr = linksection_expr,
- .callconv_expr = callconv_expr,
- .body_node = body_node,
- .var_args_token = var_args_token,
- });
- mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items);
- return fn_proto;
-}
-
-fn revertAndWarn(
- rp: RestorePoint,
- err: anytype,
- source_loc: ZigClangSourceLocation,
- comptime format: []const u8,
- args: anytype,
-) (@TypeOf(err) || error{OutOfMemory}) {
- rp.activate();
- try emitWarning(rp.c, source_loc, format, args);
- return err;
-}
-
-fn emitWarning(c: *Context, loc: ZigClangSourceLocation, comptime format: []const u8, args: anytype) !void {
- const args_prefix = .{c.locStr(loc)};
- _ = try appendTokenFmt(c, .LineComment, "// {}: warning: " ++ format, args_prefix ++ args);
-}
-
-pub fn failDecl(c: *Context, loc: ZigClangSourceLocation, name: []const u8, comptime format: []const u8, args: anytype) !void {
- // pub const name = @compileError(msg);
- const pub_tok = try appendToken(c, .Keyword_pub, "pub");
- const const_tok = try appendToken(c, .Keyword_const, "const");
- const name_tok = try appendIdentifier(c, name);
- const eq_tok = try appendToken(c, .Equal, "=");
- const builtin_tok = try appendToken(c, .Builtin, "@compileError");
- const lparen_tok = try appendToken(c, .LParen, "(");
- const msg_tok = try appendTokenFmt(c, .StringLiteral, "\"" ++ format ++ "\"", args);
- const rparen_tok = try appendToken(c, .RParen, ")");
- const semi_tok = try appendToken(c, .Semicolon, ";");
- _ = try appendTokenFmt(c, .LineComment, "// {}", .{c.locStr(loc)});
-
- const msg_node = try c.arena.create(ast.Node.OneToken);
- msg_node.* = .{
- .base = .{ .tag = .StringLiteral },
- .token = msg_tok,
- };
-
- const call_node = try ast.Node.BuiltinCall.alloc(c.arena, 1);
- call_node.* = .{
- .builtin_token = builtin_tok,
- .params_len = 1,
- .rparen_token = rparen_tok,
- };
- call_node.params()[0] = &msg_node.base;
-
- const var_decl_node = try ast.Node.VarDecl.create(c.arena, .{
- .name_token = name_tok,
- .mut_token = const_tok,
- .semicolon_token = semi_tok,
- }, .{
- .visib_token = pub_tok,
- .eq_token = eq_tok,
- .init_node = &call_node.base,
- });
- try addTopLevelDecl(c, name, &var_decl_node.base);
-}
-
-fn appendToken(c: *Context, token_id: Token.Id, bytes: []const u8) !ast.TokenIndex {
- std.debug.assert(token_id != .Identifier); // use appendIdentifier
- return appendTokenFmt(c, token_id, "{}", .{bytes});
-}
-
-fn appendTokenFmt(c: *Context, token_id: Token.Id, comptime format: []const u8, args: anytype) !ast.TokenIndex {
- assert(token_id != .Invalid);
-
- try c.token_ids.ensureCapacity(c.gpa, c.token_ids.items.len + 1);
- try c.token_locs.ensureCapacity(c.gpa, c.token_locs.items.len + 1);
-
- const start_index = c.source_buffer.items.len;
- try c.source_buffer.outStream().print(format ++ " ", args);
-
- c.token_ids.appendAssumeCapacity(token_id);
- c.token_locs.appendAssumeCapacity(.{
- .start = start_index,
- .end = c.source_buffer.items.len - 1, // back up before the space
- });
-
- return c.token_ids.items.len - 1;
-}
-
-// TODO hook up with codegen
-fn isZigPrimitiveType(name: []const u8) bool {
- if (name.len > 1 and (name[0] == 'u' or name[0] == 'i')) {
- for (name[1..]) |c| {
- switch (c) {
- '0'...'9' => {},
- else => return false,
- }
- }
- return true;
- }
- // void is invalid in c so it doesn't need to be checked.
- return mem.eql(u8, name, "comptime_float") or
- mem.eql(u8, name, "comptime_int") or
- mem.eql(u8, name, "bool") or
- mem.eql(u8, name, "isize") or
- mem.eql(u8, name, "usize") or
- mem.eql(u8, name, "f16") or
- mem.eql(u8, name, "f32") or
- mem.eql(u8, name, "f64") or
- mem.eql(u8, name, "f128") or
- mem.eql(u8, name, "c_longdouble") or
- mem.eql(u8, name, "noreturn") or
- mem.eql(u8, name, "type") or
- mem.eql(u8, name, "anyerror") or
- mem.eql(u8, name, "c_short") or
- mem.eql(u8, name, "c_ushort") or
- mem.eql(u8, name, "c_int") or
- mem.eql(u8, name, "c_uint") or
- mem.eql(u8, name, "c_long") or
- mem.eql(u8, name, "c_ulong") or
- mem.eql(u8, name, "c_longlong") or
- mem.eql(u8, name, "c_ulonglong");
-}
-
-fn isValidZigIdentifier(name: []const u8) bool {
- for (name) |c, i| {
- switch (c) {
- '_', 'a'...'z', 'A'...'Z' => {},
- '0'...'9' => if (i == 0) return false,
- else => return false,
- }
- }
- return true;
-}
-
-fn appendIdentifier(c: *Context, name: []const u8) !ast.TokenIndex {
- if (!isValidZigIdentifier(name) or std.zig.Token.getKeyword(name) != null) {
- return appendTokenFmt(c, .Identifier, "@\"{}\"", .{name});
- } else {
- return appendTokenFmt(c, .Identifier, "{}", .{name});
- }
-}
-
-fn transCreateNodeIdentifier(c: *Context, name: []const u8) !*ast.Node {
- const token_index = try appendIdentifier(c, name);
- const identifier = try c.arena.create(ast.Node.OneToken);
- identifier.* = .{
- .base = .{ .tag = .Identifier },
- .token = token_index,
- };
- return &identifier.base;
-}
-
-fn transCreateNodeIdentifierUnchecked(c: *Context, name: []const u8) !*ast.Node {
- const token_index = try appendTokenFmt(c, .Identifier, "{}", .{name});
- const identifier = try c.arena.create(ast.Node.OneToken);
- identifier.* = .{
- .base = .{ .tag = .Identifier },
- .token = token_index,
- };
- return &identifier.base;
-}
-
-pub fn freeErrors(errors: []ClangErrMsg) void {
- ZigClangErrorMsg_delete(errors.ptr, errors.len);
-}
-
-const MacroCtx = struct {
- source: []const u8,
- list: []const CToken,
- i: usize = 0,
- loc: ZigClangSourceLocation,
- name: []const u8,
-
- fn peek(self: *MacroCtx) ?CToken.Id {
- if (self.i >= self.list.len) return null;
- return self.list[self.i + 1].id;
- }
-
- fn next(self: *MacroCtx) ?CToken.Id {
- if (self.i >= self.list.len) return null;
- self.i += 1;
- return self.list[self.i].id;
- }
-
- fn slice(self: *MacroCtx) []const u8 {
- const tok = self.list[self.i];
- return self.source[tok.start..tok.end];
- }
-
- fn fail(self: *MacroCtx, c: *Context, comptime fmt: []const u8, args: anytype) !void {
- return failDecl(c, self.loc, self.name, fmt, args);
- }
-};
-
-fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
- // TODO if we see #undef, delete it from the table
- var it = ZigClangASTUnit_getLocalPreprocessingEntities_begin(unit);
- const it_end = ZigClangASTUnit_getLocalPreprocessingEntities_end(unit);
- var tok_list = std.ArrayList(CToken).init(c.gpa);
- defer tok_list.deinit();
- const scope = c.global_scope;
-
- while (it.I != it_end.I) : (it.I += 1) {
- const entity = ZigClangPreprocessingRecord_iterator_deref(it);
- tok_list.items.len = 0;
- switch (ZigClangPreprocessedEntity_getKind(entity)) {
- .MacroDefinitionKind => {
- const macro = @ptrCast(*ZigClangMacroDefinitionRecord, entity);
- const raw_name = ZigClangMacroDefinitionRecord_getName_getNameStart(macro);
- const begin_loc = ZigClangMacroDefinitionRecord_getSourceRange_getBegin(macro);
-
- const name = try c.str(raw_name);
- // TODO https://github.com/ziglang/zig/issues/3756
- // TODO https://github.com/ziglang/zig/issues/1802
- const mangled_name = if (isZigPrimitiveType(name)) try std.fmt.allocPrint(c.arena, "{}_{}", .{ name, c.getMangle() }) else name;
- if (scope.containsNow(mangled_name)) {
- continue;
- }
-
- const begin_c = ZigClangSourceManager_getCharacterData(c.source_manager, begin_loc);
- const slice = begin_c[0..mem.len(begin_c)];
-
- var tokenizer = std.c.Tokenizer{
- .buffer = slice,
- };
- while (true) {
- const tok = tokenizer.next();
- switch (tok.id) {
- .Nl, .Eof => {
- try tok_list.append(tok);
- break;
- },
- .LineComment, .MultiLineComment => continue,
- else => {},
- }
- try tok_list.append(tok);
- }
-
- var macro_ctx = MacroCtx{
- .source = slice,
- .list = tok_list.items,
- .name = mangled_name,
- .loc = begin_loc,
- };
- assert(mem.eql(u8, macro_ctx.slice(), name));
-
- var macro_fn = false;
- switch (macro_ctx.peek().?) {
- .Identifier => {
- // if it equals itself, ignore. for example, from stdio.h:
- // #define stdin stdin
- const tok = macro_ctx.list[1];
- if (mem.eql(u8, name, slice[tok.start..tok.end])) {
- continue;
- }
- },
- .Nl, .Eof => {
- // this means it is a macro without a value
- // we don't care about such things
- continue;
- },
- .LParen => {
- // if the name is immediately followed by a '(' then it is a function
- macro_fn = macro_ctx.list[0].end == macro_ctx.list[1].start;
- },
- else => {},
- }
-
- (if (macro_fn)
- transMacroFnDefine(c, ¯o_ctx)
- else
- transMacroDefine(c, ¯o_ctx)) catch |err| switch (err) {
- error.ParseError => continue,
- error.OutOfMemory => |e| return e,
- };
- },
- else => {},
- }
- }
-}
-
-fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void {
- const scope = &c.global_scope.base;
-
- const visib_tok = try appendToken(c, .Keyword_pub, "pub");
- const mut_tok = try appendToken(c, .Keyword_const, "const");
- const name_tok = try appendIdentifier(c, m.name);
- const eq_token = try appendToken(c, .Equal, "=");
-
- const init_node = try parseCExpr(c, m, scope);
- const last = m.next().?;
- if (last != .Eof and last != .Nl)
- return m.fail(c, "unable to translate C expr: unexpected token .{}", .{@tagName(last)});
-
- const semicolon_token = try appendToken(c, .Semicolon, ";");
- const node = try ast.Node.VarDecl.create(c.arena, .{
- .name_token = name_tok,
- .mut_token = mut_tok,
- .semicolon_token = semicolon_token,
- }, .{
- .visib_token = visib_tok,
- .eq_token = eq_token,
- .init_node = init_node,
- });
- _ = try c.global_scope.macro_table.put(m.name, &node.base);
-}
-
-fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
- var block_scope = try Scope.Block.init(c, &c.global_scope.base, false);
- defer block_scope.deinit();
- const scope = &block_scope.base;
-
- const pub_tok = try appendToken(c, .Keyword_pub, "pub");
- const inline_tok = try appendToken(c, .Keyword_inline, "inline");
- const fn_tok = try appendToken(c, .Keyword_fn, "fn");
- const name_tok = try appendIdentifier(c, m.name);
- _ = try appendToken(c, .LParen, "(");
-
- if (m.next().? != .LParen) {
- return m.fail(c, "unable to translate C expr: expected '('", .{});
- }
-
- var fn_params = std.ArrayList(ast.Node.FnProto.ParamDecl).init(c.gpa);
- defer fn_params.deinit();
-
- while (true) {
- if (m.next().? != .Identifier) {
- return m.fail(c, "unable to translate C expr: expected identifier", .{});
- }
-
- const mangled_name = try block_scope.makeMangledName(c, m.slice());
- const param_name_tok = try appendIdentifier(c, mangled_name);
- _ = try appendToken(c, .Colon, ":");
-
- const any_type = try c.arena.create(ast.Node.OneToken);
- any_type.* = .{
- .base = .{ .tag = .AnyType },
- .token = try appendToken(c, .Keyword_anytype, "anytype"),
- };
-
- (try fn_params.addOne()).* = .{
- .doc_comments = null,
- .comptime_token = null,
- .noalias_token = null,
- .name_token = param_name_tok,
- .param_type = .{ .any_type = &any_type.base },
- };
-
- if (m.peek().? != .Comma)
- break;
- _ = m.next();
- _ = try appendToken(c, .Comma, ",");
- }
-
- if (m.next().? != .RParen) {
- return m.fail(c, "unable to translate C expr: expected ')'", .{});
- }
-
- _ = try appendToken(c, .RParen, ")");
-
- const type_of = try c.createBuiltinCall("@TypeOf", 1);
-
- const return_kw = try appendToken(c, .Keyword_return, "return");
- const expr = try parseCExpr(c, m, scope);
- const last = m.next().?;
- if (last != .Eof and last != .Nl)
- return m.fail(c, "unable to translate C expr: unexpected token .{}", .{@tagName(last)});
- _ = try appendToken(c, .Semicolon, ";");
- const type_of_arg = if (!expr.tag.isBlock()) expr else blk: {
- const stmts = expr.blockStatements();
- const blk_last = stmts[stmts.len - 1];
- const br = blk_last.cast(ast.Node.ControlFlowExpression).?;
- break :blk br.getRHS().?;
- };
- type_of.params()[0] = type_of_arg;
- type_of.rparen_token = try appendToken(c, .RParen, ")");
- const return_expr = try ast.Node.ControlFlowExpression.create(c.arena, .{
- .ltoken = return_kw,
- .tag = .Return,
- }, .{
- .rhs = expr,
- });
-
- try block_scope.statements.append(&return_expr.base);
- const block_node = try block_scope.complete(c);
- const fn_proto = try ast.Node.FnProto.create(c.arena, .{
- .fn_token = fn_tok,
- .params_len = fn_params.items.len,
- .return_type = .{ .Explicit = &type_of.base },
- }, .{
- .visib_token = pub_tok,
- .extern_export_inline_token = inline_tok,
- .name_token = name_tok,
- .body_node = block_node,
- });
- mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items);
-
- _ = try c.global_scope.macro_table.put(m.name, &fn_proto.base);
-}
-
-const ParseError = Error || error{ParseError};
-
-fn parseCExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
- const node = try parseCPrefixOpExpr(c, m, scope);
- switch (m.next().?) {
- .QuestionMark => {
- // must come immediately after expr
- _ = try appendToken(c, .RParen, ")");
- const if_node = try transCreateNodeIf(c);
- if_node.condition = node;
- if_node.body = try parseCPrimaryExpr(c, m, scope);
- if (m.next().? != .Colon) {
- try m.fail(c, "unable to translate C expr: expected ':'", .{});
- return error.ParseError;
- }
- if_node.@"else" = try transCreateNodeElse(c);
- if_node.@"else".?.body = try parseCPrimaryExpr(c, m, scope);
- return &if_node.base;
- },
- .Comma => {
- _ = try appendToken(c, .Semicolon, ";");
- var block_scope = try Scope.Block.init(c, scope, true);
- defer block_scope.deinit();
-
- var last = node;
- while (true) {
- // suppress result
- const lhs = try transCreateNodeIdentifier(c, "_");
- const op_token = try appendToken(c, .Equal, "=");
- const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
- op_node.* = .{
- .base = .{ .tag = .Assign },
- .op_token = op_token,
- .lhs = lhs,
- .rhs = last,
- };
- try block_scope.statements.append(&op_node.base);
-
- last = try parseCPrefixOpExpr(c, m, scope);
- _ = try appendToken(c, .Semicolon, ";");
- if (m.next().? != .Comma) {
- m.i -= 1;
- break;
- }
- }
-
- const break_node = try transCreateNodeBreak(c, block_scope.label, last);
- try block_scope.statements.append(&break_node.base);
- return try block_scope.complete(c);
- },
- else => {
- m.i -= 1;
- return node;
- },
- }
-}
-
-fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!*ast.Node {
- var lit_bytes = m.slice();
-
- switch (m.list[m.i].id) {
- .IntegerLiteral => |suffix| {
- if (lit_bytes.len > 2 and lit_bytes[0] == '0') {
- switch (lit_bytes[1]) {
- '0'...'7' => {
- // Octal
- lit_bytes = try std.fmt.allocPrint(c.arena, "0o{}", .{lit_bytes});
- },
- 'X' => {
- // Hexadecimal with capital X, valid in C but not in Zig
- lit_bytes = try std.fmt.allocPrint(c.arena, "0x{}", .{lit_bytes[2..]});
- },
- else => {},
- }
- }
-
- if (suffix == .none) {
- return transCreateNodeInt(c, lit_bytes);
- }
-
- const cast_node = try c.createBuiltinCall("@as", 2);
- cast_node.params()[0] = try transCreateNodeIdentifier(c, switch (suffix) {
- .u => "c_uint",
- .l => "c_long",
- .lu => "c_ulong",
- .ll => "c_longlong",
- .llu => "c_ulonglong",
- else => unreachable,
- });
- lit_bytes = lit_bytes[0 .. lit_bytes.len - switch (suffix) {
- .u, .l => @as(u8, 1),
- .lu, .ll => 2,
- .llu => 3,
- else => unreachable,
- }];
- _ = try appendToken(c, .Comma, ",");
- cast_node.params()[1] = try transCreateNodeInt(c, lit_bytes);
- cast_node.rparen_token = try appendToken(c, .RParen, ")");
- return &cast_node.base;
- },
- .FloatLiteral => |suffix| {
- if (lit_bytes[0] == '.')
- lit_bytes = try std.fmt.allocPrint(c.arena, "0{}", .{lit_bytes});
- if (suffix == .none) {
- return transCreateNodeFloat(c, lit_bytes);
- }
- const cast_node = try c.createBuiltinCall("@as", 2);
- cast_node.params()[0] = try transCreateNodeIdentifier(c, switch (suffix) {
- .f => "f32",
- .l => "c_longdouble",
- else => unreachable,
- });
- _ = try appendToken(c, .Comma, ",");
- cast_node.params()[1] = try transCreateNodeFloat(c, lit_bytes[0 .. lit_bytes.len - 1]);
- cast_node.rparen_token = try appendToken(c, .RParen, ")");
- return &cast_node.base;
- },
- else => unreachable,
- }
-}
-
-fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 {
- var source = m.slice();
- for (source) |c, i| {
- if (c == '\"' or c == '\'') {
- source = source[i..];
- break;
- }
- }
- for (source) |c| {
- if (c == '\\') {
- break;
- }
- } else return source;
- var bytes = try ctx.arena.alloc(u8, source.len * 2);
- var state: enum {
- Start,
- Escape,
- Hex,
- Octal,
- } = .Start;
- var i: usize = 0;
- var count: u8 = 0;
- var num: u8 = 0;
- for (source) |c| {
- switch (state) {
- .Escape => {
- switch (c) {
- 'n', 'r', 't', '\\', '\'', '\"' => {
- bytes[i] = c;
- },
- '0'...'7' => {
- count += 1;
- num += c - '0';
- state = .Octal;
- bytes[i] = 'x';
- },
- 'x' => {
- state = .Hex;
- bytes[i] = 'x';
- },
- 'a' => {
- bytes[i] = 'x';
- i += 1;
- bytes[i] = '0';
- i += 1;
- bytes[i] = '7';
- },
- 'b' => {
- bytes[i] = 'x';
- i += 1;
- bytes[i] = '0';
- i += 1;
- bytes[i] = '8';
- },
- 'f' => {
- bytes[i] = 'x';
- i += 1;
- bytes[i] = '0';
- i += 1;
- bytes[i] = 'C';
- },
- 'v' => {
- bytes[i] = 'x';
- i += 1;
- bytes[i] = '0';
- i += 1;
- bytes[i] = 'B';
- },
- '?' => {
- i -= 1;
- bytes[i] = '?';
- },
- 'u', 'U' => {
- try m.fail(ctx, "macro tokenizing failed: TODO unicode escape sequences", .{});
- return error.ParseError;
- },
- else => {
- try m.fail(ctx, "macro tokenizing failed: unknown escape sequence", .{});
- return error.ParseError;
- },
- }
- i += 1;
- if (state == .Escape)
- state = .Start;
- },
- .Start => {
- if (c == '\\') {
- state = .Escape;
- }
- bytes[i] = c;
- i += 1;
- },
- .Hex => {
- switch (c) {
- '0'...'9' => {
- num = std.math.mul(u8, num, 16) catch {
- try m.fail(ctx, "macro tokenizing failed: hex literal overflowed", .{});
- return error.ParseError;
- };
- num += c - '0';
- },
- 'a'...'f' => {
- num = std.math.mul(u8, num, 16) catch {
- try m.fail(ctx, "macro tokenizing failed: hex literal overflowed", .{});
- return error.ParseError;
- };
- num += c - 'a' + 10;
- },
- 'A'...'F' => {
- num = std.math.mul(u8, num, 16) catch {
- try m.fail(ctx, "macro tokenizing failed: hex literal overflowed", .{});
- return error.ParseError;
- };
- num += c - 'A' + 10;
- },
- else => {
- i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{ .fill = '0', .width = 2 });
- num = 0;
- if (c == '\\')
- state = .Escape
- else
- state = .Start;
- bytes[i] = c;
- i += 1;
- },
- }
- },
- .Octal => {
- const accept_digit = switch (c) {
- // The maximum length of a octal literal is 3 digits
- '0'...'7' => count < 3,
- else => false,
- };
-
- if (accept_digit) {
- count += 1;
- num = std.math.mul(u8, num, 8) catch {
- try m.fail(ctx, "macro tokenizing failed: octal literal overflowed", .{});
- return error.ParseError;
- };
- num += c - '0';
- } else {
- i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{ .fill = '0', .width = 2 });
- num = 0;
- count = 0;
- if (c == '\\')
- state = .Escape
- else
- state = .Start;
- bytes[i] = c;
- i += 1;
- }
- },
- }
- }
- if (state == .Hex or state == .Octal)
- i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{ .fill = '0', .width = 2 });
- return bytes[0..i];
-}
-
-fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
- const tok = m.next().?;
- const slice = m.slice();
- switch (tok) {
- .CharLiteral => {
- if (slice[0] != '\'' or slice[1] == '\\' or slice.len == 3) {
- const token = try appendToken(c, .CharLiteral, try zigifyEscapeSequences(c, m));
- const node = try c.arena.create(ast.Node.OneToken);
- node.* = .{
- .base = .{ .tag = .CharLiteral },
- .token = token,
- };
- return &node.base;
- } else {
- const token = try appendTokenFmt(c, .IntegerLiteral, "0x{x}", .{slice[1 .. slice.len - 1]});
- const node = try c.arena.create(ast.Node.OneToken);
- node.* = .{
- .base = .{ .tag = .IntegerLiteral },
- .token = token,
- };
- return &node.base;
- }
- },
- .StringLiteral => {
- const token = try appendToken(c, .StringLiteral, try zigifyEscapeSequences(c, m));
- const node = try c.arena.create(ast.Node.OneToken);
- node.* = .{
- .base = .{ .tag = .StringLiteral },
- .token = token,
- };
- return &node.base;
- },
- .IntegerLiteral, .FloatLiteral => {
- return parseCNumLit(c, m);
- },
- // eventually this will be replaced by std.c.parse which will handle these correctly
- .Keyword_void => return transCreateNodeIdentifierUnchecked(c, "c_void"),
- .Keyword_bool => return transCreateNodeIdentifierUnchecked(c, "bool"),
- .Keyword_double => return transCreateNodeIdentifierUnchecked(c, "f64"),
- .Keyword_long => return transCreateNodeIdentifierUnchecked(c, "c_long"),
- .Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_int"),
- .Keyword_float => return transCreateNodeIdentifierUnchecked(c, "f32"),
- .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_short"),
- .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "u8"),
- .Keyword_unsigned => if (m.next()) |t| switch (t) {
- .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "u8"),
- .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_ushort"),
- .Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_uint"),
- .Keyword_long => if (m.peek() != null and m.peek().? == .Keyword_long) {
- _ = m.next();
- return transCreateNodeIdentifierUnchecked(c, "c_ulonglong");
- } else return transCreateNodeIdentifierUnchecked(c, "c_ulong"),
- else => {
- m.i -= 1;
- return transCreateNodeIdentifierUnchecked(c, "c_uint");
- },
- } else {
- return transCreateNodeIdentifierUnchecked(c, "c_uint");
- },
- .Keyword_signed => if (m.next()) |t| switch (t) {
- .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "i8"),
- .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_short"),
- .Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_int"),
- .Keyword_long => if (m.peek() != null and m.peek().? == .Keyword_long) {
- _ = m.next();
- return transCreateNodeIdentifierUnchecked(c, "c_longlong");
- } else return transCreateNodeIdentifierUnchecked(c, "c_long"),
- else => {
- m.i -= 1;
- return transCreateNodeIdentifierUnchecked(c, "c_int");
- },
- } else {
- return transCreateNodeIdentifierUnchecked(c, "c_int");
- },
- .Identifier => {
- const mangled_name = scope.getAlias(slice);
- return transCreateNodeIdentifier(c, checkForBuiltinTypedef(mangled_name) orelse mangled_name);
- },
- .LParen => {
- const inner_node = try parseCExpr(c, m, scope);
-
- const next_id = m.next().?;
- if (next_id != .RParen) {
- try m.fail(c, "unable to translate C expr: expected ')'' instead got: {}", .{@tagName(next_id)});
- return error.ParseError;
- }
- var saw_l_paren = false;
- var saw_integer_literal = false;
- switch (m.peek().?) {
- // (type)(to_cast)
- .LParen => {
- saw_l_paren = true;
- _ = m.next();
- },
- // (type)sizeof(x)
- .Keyword_sizeof,
- // (type)alignof(x)
- .Keyword_alignof,
- // (type)identifier
- .Identifier => {},
- // (type)integer
- .IntegerLiteral => {
- saw_integer_literal = true;
- },
- else => return inner_node,
- }
-
- // hack to get zig fmt to render a comma in builtin calls
- _ = try appendToken(c, .Comma, ",");
-
- const node_to_cast = try parseCExpr(c, m, scope);
-
- if (saw_l_paren and m.next().? != .RParen) {
- try m.fail(c, "unable to translate C expr: expected ')''", .{});
- return error.ParseError;
- }
-
- const lparen = try appendToken(c, .LParen, "(");
-
- //(@import("std").meta.cast(dest, x))
- const import_fn_call = try c.createBuiltinCall("@import", 1);
- const std_node = try transCreateNodeStringLiteral(c, "\"std\"");
- import_fn_call.params()[0] = std_node;
- import_fn_call.rparen_token = try appendToken(c, .RParen, ")");
- const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "meta");
- const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "cast");
-
- const cast_fn_call = try c.createCall(outer_field_access, 2);
- cast_fn_call.params()[0] = inner_node;
- cast_fn_call.params()[1] = node_to_cast;
- cast_fn_call.rtoken = try appendToken(c, .RParen, ")");
-
- const group_node = try c.arena.create(ast.Node.GroupedExpression);
- group_node.* = .{
- .lparen = lparen,
- .expr = &cast_fn_call.base,
- .rparen = try appendToken(c, .RParen, ")"),
- };
- return &group_node.base;
- },
- else => {
- try m.fail(c, "unable to translate C expr: unexpected token .{}", .{@tagName(tok)});
- return error.ParseError;
- },
- }
-}
-
-fn nodeIsInfixOp(tag: ast.Node.Tag) bool {
- return switch (tag) {
- .Add,
- .AddWrap,
- .ArrayCat,
- .ArrayMult,
- .Assign,
- .AssignBitAnd,
- .AssignBitOr,
- .AssignBitShiftLeft,
- .AssignBitShiftRight,
- .AssignBitXor,
- .AssignDiv,
- .AssignSub,
- .AssignSubWrap,
- .AssignMod,
- .AssignAdd,
- .AssignAddWrap,
- .AssignMul,
- .AssignMulWrap,
- .BangEqual,
- .BitAnd,
- .BitOr,
- .BitShiftLeft,
- .BitShiftRight,
- .BitXor,
- .BoolAnd,
- .BoolOr,
- .Div,
- .EqualEqual,
- .ErrorUnion,
- .GreaterOrEqual,
- .GreaterThan,
- .LessOrEqual,
- .LessThan,
- .MergeErrorSets,
- .Mod,
- .Mul,
- .MulWrap,
- .Period,
- .Range,
- .Sub,
- .SubWrap,
- .UnwrapOptional,
- .Catch,
- => true,
-
- else => false,
- };
-}
-
-fn macroBoolToInt(c: *Context, node: *ast.Node) !*ast.Node {
- if (!isBoolRes(node)) {
- if (!nodeIsInfixOp(node.tag)) return node;
-
- const group_node = try c.arena.create(ast.Node.GroupedExpression);
- group_node.* = .{
- .lparen = try appendToken(c, .LParen, "("),
- .expr = node,
- .rparen = try appendToken(c, .RParen, ")"),
- };
- return &group_node.base;
- }
-
- const builtin_node = try c.createBuiltinCall("@boolToInt", 1);
- builtin_node.params()[0] = node;
- builtin_node.rparen_token = try appendToken(c, .RParen, ")");
- return &builtin_node.base;
-}
-
-fn macroIntToBool(c: *Context, node: *ast.Node) !*ast.Node {
- if (isBoolRes(node)) {
- if (!nodeIsInfixOp(node.tag)) return node;
-
- const group_node = try c.arena.create(ast.Node.GroupedExpression);
- group_node.* = .{
- .lparen = try appendToken(c, .LParen, "("),
- .expr = node,
- .rparen = try appendToken(c, .RParen, ")"),
- };
- return &group_node.base;
- }
-
- const op_token = try appendToken(c, .BangEqual, "!=");
- const zero = try transCreateNodeInt(c, 0);
- const res = try c.arena.create(ast.Node.SimpleInfixOp);
- res.* = .{
- .base = .{ .tag = .BangEqual },
- .op_token = op_token,
- .lhs = node,
- .rhs = zero,
- };
- const group_node = try c.arena.create(ast.Node.GroupedExpression);
- group_node.* = .{
- .lparen = try appendToken(c, .LParen, "("),
- .expr = &res.base,
- .rparen = try appendToken(c, .RParen, ")"),
- };
- return &group_node.base;
-}
-
-fn parseCSuffixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
- var node = try parseCPrimaryExpr(c, m, scope);
- while (true) {
- var op_token: ast.TokenIndex = undefined;
- var op_id: ast.Node.Tag = undefined;
- var bool_op = false;
- switch (m.next().?) {
- .Period => {
- if (m.next().? != .Identifier) {
- try m.fail(c, "unable to translate C expr: expected identifier", .{});
- return error.ParseError;
- }
-
- node = try transCreateNodeFieldAccess(c, node, m.slice());
- continue;
- },
- .Arrow => {
- if (m.next().? != .Identifier) {
- try m.fail(c, "unable to translate C expr: expected identifier", .{});
- return error.ParseError;
- }
- const deref = try transCreateNodePtrDeref(c, node);
- node = try transCreateNodeFieldAccess(c, deref, m.slice());
- continue;
- },
- .Asterisk => {
- if (m.peek().? == .RParen) {
- // type *)
-
- // hack to get zig fmt to render a comma in builtin calls
- _ = try appendToken(c, .Comma, ",");
-
- // last token of `node`
- const prev_id = m.list[m.i - 1].id;
-
- if (prev_id == .Keyword_void) {
- const ptr = try transCreateNodePtrType(c, false, false, .Asterisk);
- ptr.rhs = node;
- const optional_node = try transCreateNodeSimplePrefixOp(c, .OptionalType, .QuestionMark, "?");
- optional_node.rhs = &ptr.base;
- return &optional_node.base;
- } else {
- const ptr = try transCreateNodePtrType(c, false, false, Token.Id.Identifier);
- ptr.rhs = node;
- return &ptr.base;
- }
- } else {
- // expr * expr
- op_token = try appendToken(c, .Asterisk, "*");
- op_id = .BitShiftLeft;
- }
- },
- .AngleBracketAngleBracketLeft => {
- op_token = try appendToken(c, .AngleBracketAngleBracketLeft, "<<");
- op_id = .BitShiftLeft;
- },
- .AngleBracketAngleBracketRight => {
- op_token = try appendToken(c, .AngleBracketAngleBracketRight, ">>");
- op_id = .BitShiftRight;
- },
- .Pipe => {
- op_token = try appendToken(c, .Pipe, "|");
- op_id = .BitOr;
- },
- .Ampersand => {
- op_token = try appendToken(c, .Ampersand, "&");
- op_id = .BitAnd;
- },
- .Plus => {
- op_token = try appendToken(c, .Plus, "+");
- op_id = .Add;
- },
- .Minus => {
- op_token = try appendToken(c, .Minus, "-");
- op_id = .Sub;
- },
- .AmpersandAmpersand => {
- op_token = try appendToken(c, .Keyword_and, "and");
- op_id = .BoolAnd;
- bool_op = true;
- },
- .PipePipe => {
- op_token = try appendToken(c, .Keyword_or, "or");
- op_id = .BoolOr;
- bool_op = true;
- },
- .AngleBracketRight => {
- op_token = try appendToken(c, .AngleBracketRight, ">");
- op_id = .GreaterThan;
- },
- .AngleBracketRightEqual => {
- op_token = try appendToken(c, .AngleBracketRightEqual, ">=");
- op_id = .GreaterOrEqual;
- },
- .AngleBracketLeft => {
- op_token = try appendToken(c, .AngleBracketLeft, "<");
- op_id = .LessThan;
- },
- .AngleBracketLeftEqual => {
- op_token = try appendToken(c, .AngleBracketLeftEqual, "<=");
- op_id = .LessOrEqual;
- },
- .LBracket => {
- const arr_node = try transCreateNodeArrayAccess(c, node);
- arr_node.index_expr = try parseCPrefixOpExpr(c, m, scope);
- arr_node.rtoken = try appendToken(c, .RBracket, "]");
- node = &arr_node.base;
- if (m.next().? != .RBracket) {
- try m.fail(c, "unable to translate C expr: expected ']'", .{});
- return error.ParseError;
- }
- continue;
- },
- .LParen => {
- _ = try appendToken(c, .LParen, "(");
- var call_params = std.ArrayList(*ast.Node).init(c.gpa);
- defer call_params.deinit();
- while (true) {
- const arg = try parseCPrefixOpExpr(c, m, scope);
- try call_params.append(arg);
- switch (m.next().?) {
- .Comma => _ = try appendToken(c, .Comma, ","),
- .RParen => break,
- else => {
- try m.fail(c, "unable to translate C expr: expected ',' or ')'", .{});
- return error.ParseError;
- },
- }
- }
- const call_node = try ast.Node.Call.alloc(c.arena, call_params.items.len);
- call_node.* = .{
- .lhs = node,
- .params_len = call_params.items.len,
- .async_token = null,
- .rtoken = try appendToken(c, .RParen, ")"),
- };
- mem.copy(*ast.Node, call_node.params(), call_params.items);
- node = &call_node.base;
- continue;
- },
- .LBrace => {
- // must come immediately after `node`
- _ = try appendToken(c, .Comma, ",");
-
- const dot = try appendToken(c, .Period, ".");
- _ = try appendToken(c, .LBrace, "{");
-
- var init_vals = std.ArrayList(*ast.Node).init(c.gpa);
- defer init_vals.deinit();
-
- while (true) {
- const val = try parseCPrefixOpExpr(c, m, scope);
- try init_vals.append(val);
- switch (m.next().?) {
- .Comma => _ = try appendToken(c, .Comma, ","),
- .RBrace => break,
- else => {
- try m.fail(c, "unable to translate C expr: expected ',' or '}}'", .{});
- return error.ParseError;
- },
- }
- }
- const tuple_node = try ast.Node.StructInitializerDot.alloc(c.arena, init_vals.items.len);
- tuple_node.* = .{
- .dot = dot,
- .list_len = init_vals.items.len,
- .rtoken = try appendToken(c, .RBrace, "}"),
- };
- mem.copy(*ast.Node, tuple_node.list(), init_vals.items);
-
- //(@import("std").mem.zeroInit(T, .{x}))
- const import_fn_call = try c.createBuiltinCall("@import", 1);
- const std_node = try transCreateNodeStringLiteral(c, "\"std\"");
- import_fn_call.params()[0] = std_node;
- import_fn_call.rparen_token = try appendToken(c, .RParen, ")");
- const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "mem");
- const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "zeroInit");
-
- const zero_init_call = try c.createCall(outer_field_access, 2);
- zero_init_call.params()[0] = node;
- zero_init_call.params()[1] = &tuple_node.base;
- zero_init_call.rtoken = try appendToken(c, .RParen, ")");
-
- node = &zero_init_call.base;
- continue;
- },
- .BangEqual => {
- op_token = try appendToken(c, .BangEqual, "!=");
- op_id = .BangEqual;
- },
- .EqualEqual => {
- op_token = try appendToken(c, .EqualEqual, "==");
- op_id = .EqualEqual;
- },
- .Slash => {
- op_id = .Div;
- op_token = try appendToken(c, .Slash, "/");
- },
- .Percent => {
- op_id = .Mod;
- op_token = try appendToken(c, .Percent, "%");
- },
- .StringLiteral => {
- op_id = .ArrayCat;
- op_token = try appendToken(c, .PlusPlus, "++");
-
- m.i -= 1;
- },
- .Identifier => {
- op_id = .ArrayCat;
- op_token = try appendToken(c, .PlusPlus, "++");
-
- m.i -= 1;
- },
- else => {
- m.i -= 1;
- return node;
- },
- }
- const cast_fn = if (bool_op) macroIntToBool else macroBoolToInt;
- const lhs_node = try cast_fn(c, node);
- const rhs_node = try parseCPrefixOpExpr(c, m, scope);
- const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
- op_node.* = .{
- .base = .{ .tag = op_id },
- .op_token = op_token,
- .lhs = lhs_node,
- .rhs = try cast_fn(c, rhs_node),
- };
- node = &op_node.base;
- }
-}
-
-fn parseCPrefixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
- switch (m.next().?) {
- .Bang => {
- const node = try transCreateNodeSimplePrefixOp(c, .BoolNot, .Bang, "!");
- node.rhs = try parseCPrefixOpExpr(c, m, scope);
- return &node.base;
- },
- .Minus => {
- const node = try transCreateNodeSimplePrefixOp(c, .Negation, .Minus, "-");
- node.rhs = try parseCPrefixOpExpr(c, m, scope);
- return &node.base;
- },
- .Plus => return try parseCPrefixOpExpr(c, m, scope),
- .Tilde => {
- const node = try transCreateNodeSimplePrefixOp(c, .BitNot, .Tilde, "~");
- node.rhs = try parseCPrefixOpExpr(c, m, scope);
- return &node.base;
- },
- .Asterisk => {
- const node = try parseCPrefixOpExpr(c, m, scope);
- return try transCreateNodePtrDeref(c, node);
- },
- .Ampersand => {
- const node = try transCreateNodeSimplePrefixOp(c, .AddressOf, .Ampersand, "&");
- node.rhs = try parseCPrefixOpExpr(c, m, scope);
- return &node.base;
- },
- .Keyword_sizeof => {
- const inner = if (m.peek().? == .LParen) blk: {
- _ = m.next();
- const inner = try parseCExpr(c, m, scope);
- if (m.next().? != .RParen) {
- try m.fail(c, "unable to translate C expr: expected ')'", .{});
- return error.ParseError;
- }
- break :blk inner;
- } else try parseCPrefixOpExpr(c, m, scope);
-
- //(@import("std").meta.sizeof(dest, x))
- const import_fn_call = try c.createBuiltinCall("@import", 1);
- const std_node = try transCreateNodeStringLiteral(c, "\"std\"");
- import_fn_call.params()[0] = std_node;
- import_fn_call.rparen_token = try appendToken(c, .RParen, ")");
- const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "meta");
- const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "sizeof");
-
- const sizeof_call = try c.createCall(outer_field_access, 1);
- sizeof_call.params()[0] = inner;
- sizeof_call.rtoken = try appendToken(c, .RParen, ")");
- return &sizeof_call.base;
- },
- .Keyword_alignof => {
- // TODO this won't work if using 's
- // #define alignof _Alignof
- if (m.next().? != .LParen) {
- try m.fail(c, "unable to translate C expr: expected '('", .{});
- return error.ParseError;
- }
- const inner = try parseCExpr(c, m, scope);
- if (m.next().? != .RParen) {
- try m.fail(c, "unable to translate C expr: expected ')'", .{});
- return error.ParseError;
- }
-
- const builtin_call = try c.createBuiltinCall("@alignOf", 1);
- builtin_call.params()[0] = inner;
- builtin_call.rparen_token = try appendToken(c, .RParen, ")");
- return &builtin_call.base;
- },
- else => {
- m.i -= 1;
- return try parseCSuffixOpExpr(c, m, scope);
- },
- }
-}
-
-fn tokenSlice(c: *Context, token: ast.TokenIndex) []u8 {
- const tok = c.token_locs.items[token];
- const slice = c.source_buffer.span()[tok.start..tok.end];
- return if (mem.startsWith(u8, slice, "@\""))
- slice[2 .. slice.len - 1]
- else
- slice;
-}
-
-fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node {
- switch (node.tag) {
- .ContainerDecl,
- .AddressOf,
- .Await,
- .BitNot,
- .BoolNot,
- .OptionalType,
- .Negation,
- .NegationWrap,
- .Resume,
- .Try,
- .ArrayType,
- .ArrayTypeSentinel,
- .PtrType,
- .SliceType,
- => return node,
-
- .Identifier => {
- const ident = node.castTag(.Identifier).?;
- if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| {
- if (value.cast(ast.Node.VarDecl)) |var_decl|
- return getContainer(c, var_decl.getInitNode().?);
- }
- },
-
- .Period => {
- const infix = node.castTag(.Period).?;
-
- if (getContainerTypeOf(c, infix.lhs)) |ty_node| {
- if (ty_node.cast(ast.Node.ContainerDecl)) |container| {
- for (container.fieldsAndDecls()) |field_ref| {
- const field = field_ref.cast(ast.Node.ContainerField).?;
- const ident = infix.rhs.castTag(.Identifier).?;
- if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) {
- return getContainer(c, field.type_expr.?);
- }
- }
- }
- }
- },
-
- else => {},
- }
- return null;
-}
-
-fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node {
- if (ref.castTag(.Identifier)) |ident| {
- if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| {
- if (value.cast(ast.Node.VarDecl)) |var_decl| {
- if (var_decl.getTypeNode()) |ty|
- return getContainer(c, ty);
- }
- }
- } else if (ref.castTag(.Period)) |infix| {
- if (getContainerTypeOf(c, infix.lhs)) |ty_node| {
- if (ty_node.cast(ast.Node.ContainerDecl)) |container| {
- for (container.fieldsAndDecls()) |field_ref| {
- const field = field_ref.cast(ast.Node.ContainerField).?;
- const ident = infix.rhs.castTag(.Identifier).?;
- if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) {
- return getContainer(c, field.type_expr.?);
- }
- }
- } else
- return ty_node;
- }
- }
- return null;
-}
-
-fn getFnProto(c: *Context, ref: *ast.Node) ?*ast.Node.FnProto {
- const init = if (ref.cast(ast.Node.VarDecl)) |v| v.getInitNode().? else return null;
- if (getContainerTypeOf(c, init)) |ty_node| {
- if (ty_node.castTag(.OptionalType)) |prefix| {
- if (prefix.rhs.cast(ast.Node.FnProto)) |fn_proto| {
- return fn_proto;
- }
- }
- }
- return null;
-}
-
-fn addMacros(c: *Context) !void {
- var it = c.global_scope.macro_table.iterator();
- while (it.next()) |kv| {
- if (getFnProto(c, kv.value)) |proto_node| {
- // If a macro aliases a global variable which is a function pointer, we conclude that
- // the macro is intended to represent a function that assumes the function pointer
- // variable is non-null and calls it.
- try addTopLevelDecl(c, kv.key, try transCreateNodeMacroFn(c, kv.key, kv.value, proto_node));
- } else {
- try addTopLevelDecl(c, kv.key, kv.value);
- }
- }
-}
diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig
deleted file mode 100644
index 49663955124152ab8dc90a20e29fd612edd5841f..0000000000000000000000000000000000000000
--- a/src-self-hosted/type.zig
+++ /dev/null
@@ -1,3075 +0,0 @@
-const std = @import("std");
-const Value = @import("value.zig").Value;
-const assert = std.debug.assert;
-const Allocator = std.mem.Allocator;
-const Target = std.Target;
-const Module = @import("Module.zig");
-
-/// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication.
-/// It's important for this type to be small.
-/// Types are not de-duplicated, which helps with multi-threading since it obviates the requirement
-/// of obtaining a lock on a global type table, as well as making the
-/// garbage collection bookkeeping simpler.
-/// This union takes advantage of the fact that the first page of memory
-/// is unmapped, giving us 4096 possible enum tags that have no payload.
-pub const Type = extern union {
- /// If the tag value is less than Tag.no_payload_count, then no pointer
- /// dereference is needed.
- tag_if_small_enough: usize,
- ptr_otherwise: *Payload,
-
- pub fn zigTypeTag(self: Type) std.builtin.TypeId {
- switch (self.tag()) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .int_signed,
- .int_unsigned,
- => return .Int,
-
- .f16,
- .f32,
- .f64,
- .f128,
- => return .Float,
-
- .c_void => return .Opaque,
- .bool => return .Bool,
- .void => return .Void,
- .type => return .Type,
- .error_set, .error_set_single, .anyerror => return .ErrorSet,
- .comptime_int => return .ComptimeInt,
- .comptime_float => return .ComptimeFloat,
- .noreturn => return .NoReturn,
- .@"null" => return .Null,
- .@"undefined" => return .Undefined,
-
- .fn_noreturn_no_args => return .Fn,
- .fn_void_no_args => return .Fn,
- .fn_naked_noreturn_no_args => return .Fn,
- .fn_ccc_void_no_args => return .Fn,
- .function => return .Fn,
-
- .array, .array_u8_sentinel_0, .array_u8, .array_sentinel => return .Array,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .pointer,
- => return .Pointer,
-
- .optional,
- .optional_single_const_pointer,
- .optional_single_mut_pointer,
- => return .Optional,
- .enum_literal => return .EnumLiteral,
-
- .anyerror_void_error_union, .error_union => return .ErrorUnion,
-
- .anyframe_T, .@"anyframe" => return .AnyFrame,
- }
- }
-
- pub fn initTag(comptime small_tag: Tag) Type {
- comptime assert(@enumToInt(small_tag) < Tag.no_payload_count);
- return .{ .tag_if_small_enough = @enumToInt(small_tag) };
- }
-
- pub fn initPayload(payload: *Payload) Type {
- assert(@enumToInt(payload.tag) >= Tag.no_payload_count);
- return .{ .ptr_otherwise = payload };
- }
-
- pub fn tag(self: Type) Tag {
- if (self.tag_if_small_enough < Tag.no_payload_count) {
- return @intToEnum(Tag, @intCast(@TagType(Tag), self.tag_if_small_enough));
- } else {
- return self.ptr_otherwise.tag;
- }
- }
-
- pub fn cast(self: Type, comptime T: type) ?*T {
- if (self.tag_if_small_enough < Tag.no_payload_count)
- return null;
-
- const expected_tag = std.meta.fieldInfo(T, "base").default_value.?.tag;
- if (self.ptr_otherwise.tag != expected_tag)
- return null;
-
- return @fieldParentPtr(T, "base", self.ptr_otherwise);
- }
-
- pub fn castPointer(self: Type) ?*Payload.PointerSimple {
- return switch (self.tag()) {
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .optional_single_const_pointer,
- .optional_single_mut_pointer,
- => @fieldParentPtr(Payload.PointerSimple, "base", self.ptr_otherwise),
- else => null,
- };
- }
-
- pub fn eql(a: Type, b: Type) bool {
- // As a shortcut, if the small tags / addresses match, we're done.
- if (a.tag_if_small_enough == b.tag_if_small_enough)
- return true;
- const zig_tag_a = a.zigTypeTag();
- const zig_tag_b = b.zigTypeTag();
- if (zig_tag_a != zig_tag_b)
- return false;
- switch (zig_tag_a) {
- .EnumLiteral => return true,
- .Type => return true,
- .Void => return true,
- .Bool => return true,
- .NoReturn => return true,
- .ComptimeFloat => return true,
- .ComptimeInt => return true,
- .Undefined => return true,
- .Null => return true,
- .AnyFrame => {
- return a.elemType().eql(b.elemType());
- },
- .Pointer => {
- // Hot path for common case:
- if (a.castPointer()) |a_payload| {
- if (b.castPointer()) |b_payload| {
- return a.tag() == b.tag() and eql(a_payload.pointee_type, b_payload.pointee_type);
- }
- }
- const is_slice_a = isSlice(a);
- const is_slice_b = isSlice(b);
- if (is_slice_a != is_slice_b)
- return false;
- @panic("TODO implement more pointer Type equality comparison");
- },
- .Int => {
- // Detect that e.g. u64 != usize, even if the bits match on a particular target.
- const a_is_named_int = a.isNamedInt();
- const b_is_named_int = b.isNamedInt();
- if (a_is_named_int != b_is_named_int)
- return false;
- if (a_is_named_int)
- return a.tag() == b.tag();
- // Remaining cases are arbitrary sized integers.
- // The target will not be branched upon, because we handled target-dependent cases above.
- const info_a = a.intInfo(@as(Target, undefined));
- const info_b = b.intInfo(@as(Target, undefined));
- return info_a.signed == info_b.signed and info_a.bits == info_b.bits;
- },
- .Array => {
- if (a.arrayLen() != b.arrayLen())
- return false;
- if (!a.elemType().eql(b.elemType()))
- return false;
- const sentinel_a = a.sentinel();
- const sentinel_b = b.sentinel();
- if (sentinel_a) |sa| {
- if (sentinel_b) |sb| {
- return sa.eql(sb);
- } else {
- return false;
- }
- } else {
- return sentinel_b == null;
- }
- },
- .Fn => {
- if (!a.fnReturnType().eql(b.fnReturnType()))
- return false;
- if (a.fnCallingConvention() != b.fnCallingConvention())
- return false;
- const a_param_len = a.fnParamLen();
- const b_param_len = b.fnParamLen();
- if (a_param_len != b_param_len)
- return false;
- var i: usize = 0;
- while (i < a_param_len) : (i += 1) {
- if (!a.fnParamType(i).eql(b.fnParamType(i)))
- return false;
- }
- return true;
- },
- .Optional => {
- var buf_a: Payload.PointerSimple = undefined;
- var buf_b: Payload.PointerSimple = undefined;
- return a.optionalChild(&buf_a).eql(b.optionalChild(&buf_b));
- },
- .Float,
- .Struct,
- .ErrorUnion,
- .ErrorSet,
- .Enum,
- .Union,
- .BoundFn,
- .Opaque,
- .Frame,
- .Vector,
- => std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ a, b }),
- }
- }
-
- pub fn hash(self: Type) u64 {
- var hasher = std.hash.Wyhash.init(0);
- const zig_type_tag = self.zigTypeTag();
- std.hash.autoHash(&hasher, zig_type_tag);
- switch (zig_type_tag) {
- .Type,
- .Void,
- .Bool,
- .NoReturn,
- .ComptimeFloat,
- .ComptimeInt,
- .Undefined,
- .Null,
- => {}, // The zig type tag is all that is needed to distinguish.
-
- .Pointer => {
- // TODO implement more pointer type hashing
- },
- .Int => {
- // Detect that e.g. u64 != usize, even if the bits match on a particular target.
- if (self.isNamedInt()) {
- std.hash.autoHash(&hasher, self.tag());
- } else {
- // Remaining cases are arbitrary sized integers.
- // The target will not be branched upon, because we handled target-dependent cases above.
- const info = self.intInfo(@as(Target, undefined));
- std.hash.autoHash(&hasher, info.signed);
- std.hash.autoHash(&hasher, info.bits);
- }
- },
- .Array => {
- std.hash.autoHash(&hasher, self.arrayLen());
- std.hash.autoHash(&hasher, self.elemType().hash());
- // TODO hash array sentinel
- },
- .Fn => {
- std.hash.autoHash(&hasher, self.fnReturnType().hash());
- std.hash.autoHash(&hasher, self.fnCallingConvention());
- const params_len = self.fnParamLen();
- std.hash.autoHash(&hasher, params_len);
- var i: usize = 0;
- while (i < params_len) : (i += 1) {
- std.hash.autoHash(&hasher, self.fnParamType(i).hash());
- }
- },
- .Optional => {
- var buf: Payload.PointerSimple = undefined;
- std.hash.autoHash(&hasher, self.optionalChild(&buf).hash());
- },
- .Float,
- .Struct,
- .ErrorUnion,
- .ErrorSet,
- .Enum,
- .Union,
- .BoundFn,
- .Opaque,
- .Frame,
- .AnyFrame,
- .Vector,
- .EnumLiteral,
- => {
- // TODO implement more type hashing
- },
- }
- return hasher.final();
- }
-
- pub fn copy(self: Type, allocator: *Allocator) error{OutOfMemory}!Type {
- if (self.tag_if_small_enough < Tag.no_payload_count) {
- return Type{ .tag_if_small_enough = self.tag_if_small_enough };
- } else switch (self.ptr_otherwise.tag) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .c_void,
- .f16,
- .f32,
- .f64,
- .f128,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .enum_literal,
- .anyerror_void_error_union,
- .@"anyframe",
- => unreachable,
-
- .array_u8_sentinel_0 => return self.copyPayloadShallow(allocator, Payload.Array_u8_Sentinel0),
- .array_u8 => return self.copyPayloadShallow(allocator, Payload.Array_u8),
- .array => {
- const payload = @fieldParentPtr(Payload.Array, "base", self.ptr_otherwise);
- const new_payload = try allocator.create(Payload.Array);
- new_payload.* = .{
- .base = payload.base,
- .len = payload.len,
- .elem_type = try payload.elem_type.copy(allocator),
- };
- return Type{ .ptr_otherwise = &new_payload.base };
- },
- .array_sentinel => {
- const payload = @fieldParentPtr(Payload.ArraySentinel, "base", self.ptr_otherwise);
- const new_payload = try allocator.create(Payload.ArraySentinel);
- new_payload.* = .{
- .base = payload.base,
- .len = payload.len,
- .sentinel = try payload.sentinel.copy(allocator),
- .elem_type = try payload.elem_type.copy(allocator),
- };
- return Type{ .ptr_otherwise = &new_payload.base };
- },
- .int_signed => return self.copyPayloadShallow(allocator, Payload.IntSigned),
- .int_unsigned => return self.copyPayloadShallow(allocator, Payload.IntUnsigned),
- .function => {
- const payload = @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise);
- const new_payload = try allocator.create(Payload.Function);
- const param_types = try allocator.alloc(Type, payload.param_types.len);
- for (payload.param_types) |param_type, i| {
- param_types[i] = try param_type.copy(allocator);
- }
- new_payload.* = .{
- .base = payload.base,
- .return_type = try payload.return_type.copy(allocator),
- .param_types = param_types,
- .cc = payload.cc,
- };
- return Type{ .ptr_otherwise = &new_payload.base };
- },
- .optional => return self.copyPayloadSingleField(allocator, Payload.Optional, "child_type"),
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- => return self.copyPayloadSingleField(allocator, Payload.PointerSimple, "pointee_type"),
- .anyframe_T => return self.copyPayloadSingleField(allocator, Payload.AnyFrame, "return_type"),
-
- .pointer => {
- const payload = @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise);
- const new_payload = try allocator.create(Payload.Pointer);
- new_payload.* = .{
- .base = payload.base,
-
- .pointee_type = try payload.pointee_type.copy(allocator),
- .sentinel = if (payload.sentinel) |some| try some.copy(allocator) else null,
- .@"align" = payload.@"align",
- .bit_offset = payload.bit_offset,
- .host_size = payload.host_size,
- .@"allowzero" = payload.@"allowzero",
- .mutable = payload.mutable,
- .@"volatile" = payload.@"volatile",
- .size = payload.size,
- };
- return Type{ .ptr_otherwise = &new_payload.base };
- },
- .error_union => {
- const payload = @fieldParentPtr(Payload.ErrorUnion, "base", self.ptr_otherwise);
- const new_payload = try allocator.create(Payload.ErrorUnion);
- new_payload.* = .{
- .base = payload.base,
-
- .error_set = try payload.error_set.copy(allocator),
- .payload = try payload.payload.copy(allocator),
- };
- return Type{ .ptr_otherwise = &new_payload.base };
- },
- .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet),
- .error_set_single => return self.copyPayloadShallow(allocator, Payload.ErrorSetSingle),
- }
- }
-
- fn copyPayloadShallow(self: Type, allocator: *Allocator, comptime T: type) error{OutOfMemory}!Type {
- const payload = @fieldParentPtr(T, "base", self.ptr_otherwise);
- const new_payload = try allocator.create(T);
- new_payload.* = payload.*;
- return Type{ .ptr_otherwise = &new_payload.base };
- }
-
- fn copyPayloadSingleField(self: Type, allocator: *Allocator, comptime T: type, comptime field_name: []const u8) error{OutOfMemory}!Type {
- const payload = @fieldParentPtr(T, "base", self.ptr_otherwise);
- const new_payload = try allocator.create(T);
- new_payload.base = payload.base;
- @field(new_payload, field_name) = try @field(payload, field_name).copy(allocator);
- return Type{ .ptr_otherwise = &new_payload.base };
- }
-
- pub fn format(
- self: Type,
- comptime fmt: []const u8,
- options: std.fmt.FormatOptions,
- out_stream: anytype,
- ) @TypeOf(out_stream).Error!void {
- comptime assert(fmt.len == 0);
- var ty = self;
- while (true) {
- const t = ty.tag();
- switch (t) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .c_void,
- .f16,
- .f32,
- .f64,
- .f128,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- => return out_stream.writeAll(@tagName(t)),
-
- .enum_literal => return out_stream.writeAll("@Type(.EnumLiteral)"),
- .@"null" => return out_stream.writeAll("@Type(.Null)"),
- .@"undefined" => return out_stream.writeAll("@Type(.Undefined)"),
-
- .@"anyframe" => return out_stream.writeAll("anyframe"),
- .anyerror_void_error_union => return out_stream.writeAll("anyerror!void"),
- .const_slice_u8 => return out_stream.writeAll("[]const u8"),
- .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"),
- .fn_void_no_args => return out_stream.writeAll("fn() void"),
- .fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
- .fn_ccc_void_no_args => return out_stream.writeAll("fn() callconv(.C) void"),
- .single_const_pointer_to_comptime_int => return out_stream.writeAll("*const comptime_int"),
- .function => {
- const payload = @fieldParentPtr(Payload.Function, "base", ty.ptr_otherwise);
- try out_stream.writeAll("fn(");
- for (payload.param_types) |param_type, i| {
- if (i != 0) try out_stream.writeAll(", ");
- try param_type.format("", .{}, out_stream);
- }
- try out_stream.writeAll(") ");
- ty = payload.return_type;
- continue;
- },
-
- .anyframe_T => {
- const payload = @fieldParentPtr(Payload.AnyFrame, "base", ty.ptr_otherwise);
- try out_stream.print("anyframe->", .{});
- ty = payload.return_type;
- continue;
- },
- .array_u8 => {
- const payload = @fieldParentPtr(Payload.Array_u8, "base", ty.ptr_otherwise);
- return out_stream.print("[{}]u8", .{payload.len});
- },
- .array_u8_sentinel_0 => {
- const payload = @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", ty.ptr_otherwise);
- return out_stream.print("[{}:0]u8", .{payload.len});
- },
- .array => {
- const payload = @fieldParentPtr(Payload.Array, "base", ty.ptr_otherwise);
- try out_stream.print("[{}]", .{payload.len});
- ty = payload.elem_type;
- continue;
- },
- .array_sentinel => {
- const payload = @fieldParentPtr(Payload.ArraySentinel, "base", ty.ptr_otherwise);
- try out_stream.print("[{}:{}]", .{ payload.len, payload.sentinel });
- ty = payload.elem_type;
- continue;
- },
- .single_const_pointer => {
- const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
- try out_stream.writeAll("*const ");
- ty = payload.pointee_type;
- continue;
- },
- .single_mut_pointer => {
- const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
- try out_stream.writeAll("*");
- ty = payload.pointee_type;
- continue;
- },
- .many_const_pointer => {
- const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
- try out_stream.writeAll("[*]const ");
- ty = payload.pointee_type;
- continue;
- },
- .many_mut_pointer => {
- const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
- try out_stream.writeAll("[*]");
- ty = payload.pointee_type;
- continue;
- },
- .c_const_pointer => {
- const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
- try out_stream.writeAll("[*c]const ");
- ty = payload.pointee_type;
- continue;
- },
- .c_mut_pointer => {
- const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
- try out_stream.writeAll("[*c]");
- ty = payload.pointee_type;
- continue;
- },
- .const_slice => {
- const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
- try out_stream.writeAll("[]const ");
- ty = payload.pointee_type;
- continue;
- },
- .mut_slice => {
- const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
- try out_stream.writeAll("[]");
- ty = payload.pointee_type;
- continue;
- },
- .int_signed => {
- const payload = @fieldParentPtr(Payload.IntSigned, "base", ty.ptr_otherwise);
- return out_stream.print("i{}", .{payload.bits});
- },
- .int_unsigned => {
- const payload = @fieldParentPtr(Payload.IntUnsigned, "base", ty.ptr_otherwise);
- return out_stream.print("u{}", .{payload.bits});
- },
- .optional => {
- const payload = @fieldParentPtr(Payload.Optional, "base", ty.ptr_otherwise);
- try out_stream.writeByte('?');
- ty = payload.child_type;
- continue;
- },
- .optional_single_const_pointer => {
- const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
- try out_stream.writeAll("?*const ");
- ty = payload.pointee_type;
- continue;
- },
- .optional_single_mut_pointer => {
- const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
- try out_stream.writeAll("?*");
- ty = payload.pointee_type;
- continue;
- },
-
- .pointer => {
- const payload = @fieldParentPtr(Payload.Pointer, "base", ty.ptr_otherwise);
- if (payload.sentinel) |some| switch (payload.size) {
- .One, .C => unreachable,
- .Many => try out_stream.print("[*:{}]", .{some}),
- .Slice => try out_stream.print("[:{}]", .{some}),
- } else switch (payload.size) {
- .One => try out_stream.writeAll("*"),
- .Many => try out_stream.writeAll("[*]"),
- .C => try out_stream.writeAll("[*c]"),
- .Slice => try out_stream.writeAll("[]"),
- }
- if (payload.@"align" != 0) {
- try out_stream.print("align({}", .{payload.@"align"});
-
- if (payload.bit_offset != 0) {
- try out_stream.print(":{}:{}", .{ payload.bit_offset, payload.host_size });
- }
- try out_stream.writeAll(") ");
- }
- if (!payload.mutable) try out_stream.writeAll("const ");
- if (payload.@"volatile") try out_stream.writeAll("volatile ");
- if (payload.@"allowzero") try out_stream.writeAll("allowzero ");
-
- ty = payload.pointee_type;
- continue;
- },
- .error_union => {
- const payload = @fieldParentPtr(Payload.ErrorUnion, "base", ty.ptr_otherwise);
- try payload.error_set.format("", .{}, out_stream);
- try out_stream.writeAll("!");
- ty = payload.payload;
- continue;
- },
- .error_set => {
- const payload = @fieldParentPtr(Payload.ErrorSet, "base", ty.ptr_otherwise);
- return out_stream.writeAll(std.mem.spanZ(payload.decl.name));
- },
- .error_set_single => {
- const payload = @fieldParentPtr(Payload.ErrorSetSingle, "base", ty.ptr_otherwise);
- return out_stream.print("error{{{}}}", .{payload.name});
- },
- }
- unreachable;
- }
- }
-
- pub fn toValue(self: Type, allocator: *Allocator) Allocator.Error!Value {
- switch (self.tag()) {
- .u8 => return Value.initTag(.u8_type),
- .i8 => return Value.initTag(.i8_type),
- .u16 => return Value.initTag(.u16_type),
- .i16 => return Value.initTag(.i16_type),
- .u32 => return Value.initTag(.u32_type),
- .i32 => return Value.initTag(.i32_type),
- .u64 => return Value.initTag(.u64_type),
- .i64 => return Value.initTag(.i64_type),
- .usize => return Value.initTag(.usize_type),
- .isize => return Value.initTag(.isize_type),
- .c_short => return Value.initTag(.c_short_type),
- .c_ushort => return Value.initTag(.c_ushort_type),
- .c_int => return Value.initTag(.c_int_type),
- .c_uint => return Value.initTag(.c_uint_type),
- .c_long => return Value.initTag(.c_long_type),
- .c_ulong => return Value.initTag(.c_ulong_type),
- .c_longlong => return Value.initTag(.c_longlong_type),
- .c_ulonglong => return Value.initTag(.c_ulonglong_type),
- .c_longdouble => return Value.initTag(.c_longdouble_type),
- .c_void => return Value.initTag(.c_void_type),
- .f16 => return Value.initTag(.f16_type),
- .f32 => return Value.initTag(.f32_type),
- .f64 => return Value.initTag(.f64_type),
- .f128 => return Value.initTag(.f128_type),
- .bool => return Value.initTag(.bool_type),
- .void => return Value.initTag(.void_type),
- .type => return Value.initTag(.type_type),
- .anyerror => return Value.initTag(.anyerror_type),
- .comptime_int => return Value.initTag(.comptime_int_type),
- .comptime_float => return Value.initTag(.comptime_float_type),
- .noreturn => return Value.initTag(.noreturn_type),
- .@"null" => return Value.initTag(.null_type),
- .@"undefined" => return Value.initTag(.undefined_type),
- .fn_noreturn_no_args => return Value.initTag(.fn_noreturn_no_args_type),
- .fn_void_no_args => return Value.initTag(.fn_void_no_args_type),
- .fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type),
- .fn_ccc_void_no_args => return Value.initTag(.fn_ccc_void_no_args_type),
- .single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type),
- .const_slice_u8 => return Value.initTag(.const_slice_u8_type),
- .enum_literal => return Value.initTag(.enum_literal_type),
- else => {
- const ty_payload = try allocator.create(Value.Payload.Ty);
- ty_payload.* = .{ .ty = self };
- return Value.initPayload(&ty_payload.base);
- },
- }
- }
-
- pub fn hasCodeGenBits(self: Type) bool {
- return switch (self.tag()) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .f16,
- .f32,
- .f64,
- .f128,
- .bool,
- .anyerror,
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .array_u8_sentinel_0,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => true,
- // TODO lazy types
- .array => self.elemType().hasCodeGenBits() and self.arrayLen() != 0,
- .array_u8 => self.arrayLen() != 0,
- .array_sentinel, .single_const_pointer, .single_mut_pointer, .many_const_pointer, .many_mut_pointer, .c_const_pointer, .c_mut_pointer, .const_slice, .mut_slice, .pointer => self.elemType().hasCodeGenBits(),
- .int_signed => self.cast(Payload.IntSigned).?.bits != 0,
- .int_unsigned => self.cast(Payload.IntUnsigned).?.bits != 0,
-
- .error_union => {
- const payload = self.cast(Payload.ErrorUnion).?;
- return payload.error_set.hasCodeGenBits() or payload.payload.hasCodeGenBits();
- },
-
- .c_void,
- .void,
- .type,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .enum_literal,
- => false,
- };
- }
-
- pub fn isNoReturn(self: Type) bool {
- return self.zigTypeTag() == .NoReturn;
- }
-
- /// Asserts that hasCodeGenBits() is true.
- pub fn abiAlignment(self: Type, target: Target) u32 {
- return switch (self.tag()) {
- .u8,
- .i8,
- .bool,
- .array_u8_sentinel_0,
- .array_u8,
- => return 1,
-
- .fn_noreturn_no_args, // represents machine code; not a pointer
- .fn_void_no_args, // represents machine code; not a pointer
- .fn_naked_noreturn_no_args, // represents machine code; not a pointer
- .fn_ccc_void_no_args, // represents machine code; not a pointer
- .function, // represents machine code; not a pointer
- => return switch (target.cpu.arch) {
- .arm => 4,
- .riscv64 => 2,
- else => 1,
- },
-
- .i16, .u16 => return 2,
- .i32, .u32 => return 4,
- .i64, .u64 => return 8,
-
- .isize,
- .usize,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .optional_single_const_pointer,
- .optional_single_mut_pointer,
- .@"anyframe",
- .anyframe_T,
- => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
-
- .pointer => {
- const payload = @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise);
-
- if (payload.@"align" != 0) return payload.@"align";
- return @divExact(target.cpu.arch.ptrBitWidth(), 8);
- },
-
- .c_short => return @divExact(CType.short.sizeInBits(target), 8),
- .c_ushort => return @divExact(CType.ushort.sizeInBits(target), 8),
- .c_int => return @divExact(CType.int.sizeInBits(target), 8),
- .c_uint => return @divExact(CType.uint.sizeInBits(target), 8),
- .c_long => return @divExact(CType.long.sizeInBits(target), 8),
- .c_ulong => return @divExact(CType.ulong.sizeInBits(target), 8),
- .c_longlong => return @divExact(CType.longlong.sizeInBits(target), 8),
- .c_ulonglong => return @divExact(CType.ulonglong.sizeInBits(target), 8),
-
- .f16 => return 2,
- .f32 => return 4,
- .f64 => return 8,
- .f128 => return 16,
- .c_longdouble => return 16,
-
- .error_set,
- .error_set_single,
- .anyerror_void_error_union,
- .anyerror,
- => return 2, // TODO revisit this when we have the concept of the error tag type
-
- .array, .array_sentinel => return self.elemType().abiAlignment(target),
-
- .int_signed, .int_unsigned => {
- const bits: u16 = if (self.cast(Payload.IntSigned)) |pl|
- pl.bits
- else if (self.cast(Payload.IntUnsigned)) |pl|
- pl.bits
- else
- unreachable;
-
- return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8);
- },
-
- .optional => {
- var buf: Payload.PointerSimple = undefined;
- const child_type = self.optionalChild(&buf);
- if (!child_type.hasCodeGenBits()) return 1;
-
- if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr())
- return @divExact(target.cpu.arch.ptrBitWidth(), 8);
-
- return child_type.abiAlignment(target);
- },
-
- .error_union => {
- const payload = self.cast(Payload.ErrorUnion).?;
- if (!payload.error_set.hasCodeGenBits()) {
- return payload.payload.abiAlignment(target);
- } else if (!payload.payload.hasCodeGenBits()) {
- return payload.error_set.abiAlignment(target);
- }
- @panic("TODO abiAlignment error union");
- },
-
- .c_void,
- .void,
- .type,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .enum_literal,
- => unreachable,
- };
- }
-
- /// Asserts the type has the ABI size already resolved.
- pub fn abiSize(self: Type, target: Target) u64 {
- return switch (self.tag()) {
- .fn_noreturn_no_args => unreachable, // represents machine code; not a pointer
- .fn_void_no_args => unreachable, // represents machine code; not a pointer
- .fn_naked_noreturn_no_args => unreachable, // represents machine code; not a pointer
- .fn_ccc_void_no_args => unreachable, // represents machine code; not a pointer
- .function => unreachable, // represents machine code; not a pointer
- .c_void => unreachable,
- .void => unreachable,
- .type => unreachable,
- .comptime_int => unreachable,
- .comptime_float => unreachable,
- .noreturn => unreachable,
- .@"null" => unreachable,
- .@"undefined" => unreachable,
- .enum_literal => unreachable,
- .single_const_pointer_to_comptime_int => unreachable,
-
- .u8,
- .i8,
- .bool,
- => return 1,
-
- .array_u8 => @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", self.ptr_otherwise).len,
- .array_u8_sentinel_0 => @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", self.ptr_otherwise).len + 1,
- .array => {
- const payload = @fieldParentPtr(Payload.Array, "base", self.ptr_otherwise);
- const elem_size = std.math.max(payload.elem_type.abiAlignment(target), payload.elem_type.abiSize(target));
- return payload.len * elem_size;
- },
- .array_sentinel => {
- const payload = @fieldParentPtr(Payload.ArraySentinel, "base", self.ptr_otherwise);
- const elem_size = std.math.max(payload.elem_type.abiAlignment(target), payload.elem_type.abiSize(target));
- return (payload.len + 1) * elem_size;
- },
- .i16, .u16 => return 2,
- .i32, .u32 => return 4,
- .i64, .u64 => return 8,
-
- .@"anyframe", .anyframe_T, .isize, .usize => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
-
- .const_slice,
- .mut_slice,
- => {
- if (self.elemType().hasCodeGenBits()) return @divExact(target.cpu.arch.ptrBitWidth(), 8) * 2;
- return @divExact(target.cpu.arch.ptrBitWidth(), 8);
- },
- .const_slice_u8 => return @divExact(target.cpu.arch.ptrBitWidth(), 8) * 2,
-
- .optional_single_const_pointer,
- .optional_single_mut_pointer,
- => {
- if (self.elemType().hasCodeGenBits()) return 1;
- return @divExact(target.cpu.arch.ptrBitWidth(), 8);
- },
-
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .pointer,
- => {
- if (self.elemType().hasCodeGenBits()) return 0;
- return @divExact(target.cpu.arch.ptrBitWidth(), 8);
- },
-
- .c_short => return @divExact(CType.short.sizeInBits(target), 8),
- .c_ushort => return @divExact(CType.ushort.sizeInBits(target), 8),
- .c_int => return @divExact(CType.int.sizeInBits(target), 8),
- .c_uint => return @divExact(CType.uint.sizeInBits(target), 8),
- .c_long => return @divExact(CType.long.sizeInBits(target), 8),
- .c_ulong => return @divExact(CType.ulong.sizeInBits(target), 8),
- .c_longlong => return @divExact(CType.longlong.sizeInBits(target), 8),
- .c_ulonglong => return @divExact(CType.ulonglong.sizeInBits(target), 8),
-
- .f16 => return 2,
- .f32 => return 4,
- .f64 => return 8,
- .f128 => return 16,
- .c_longdouble => return 16,
-
- .error_set,
- .error_set_single,
- .anyerror_void_error_union,
- .anyerror,
- => return 2, // TODO revisit this when we have the concept of the error tag type
-
- .int_signed, .int_unsigned => {
- const bits: u16 = if (self.cast(Payload.IntSigned)) |pl|
- pl.bits
- else if (self.cast(Payload.IntUnsigned)) |pl|
- pl.bits
- else
- unreachable;
-
- return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8);
- },
-
- .optional => {
- var buf: Payload.PointerSimple = undefined;
- const child_type = self.optionalChild(&buf);
- if (!child_type.hasCodeGenBits()) return 1;
-
- if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr())
- return @divExact(target.cpu.arch.ptrBitWidth(), 8);
-
- // Optional types are represented as a struct with the child type as the first
- // field and a boolean as the second. Since the child type's abi alignment is
- // guaranteed to be >= that of bool's (1 byte) the added size is exactly equal
- // to the child type's ABI alignment.
- return child_type.abiAlignment(target) + child_type.abiSize(target);
- },
-
- .error_union => {
- const payload = self.cast(Payload.ErrorUnion).?;
- if (!payload.error_set.hasCodeGenBits() and !payload.payload.hasCodeGenBits()) {
- return 0;
- } else if (!payload.error_set.hasCodeGenBits()) {
- return payload.payload.abiSize(target);
- } else if (!payload.payload.hasCodeGenBits()) {
- return payload.error_set.abiSize(target);
- }
- @panic("TODO abiSize error union");
- },
- };
- }
-
- pub fn isSinglePointer(self: Type) bool {
- return switch (self.tag()) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .f16,
- .f32,
- .f64,
- .f128,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .const_slice_u8,
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => false,
-
- .single_const_pointer,
- .single_mut_pointer,
- .single_const_pointer_to_comptime_int,
- => true,
-
- .pointer => self.cast(Payload.Pointer).?.size == .One,
- };
- }
-
- pub fn isSlice(self: Type) bool {
- return switch (self.tag()) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .f16,
- .f32,
- .f64,
- .f128,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .single_const_pointer_to_comptime_int,
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => false,
-
- .const_slice,
- .mut_slice,
- .const_slice_u8,
- => true,
-
- .pointer => self.cast(Payload.Pointer).?.size == .Slice,
- };
- }
-
- pub fn isConstPtr(self: Type) bool {
- return switch (self.tag()) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .f16,
- .f32,
- .f64,
- .f128,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .int_unsigned,
- .int_signed,
- .single_mut_pointer,
- .many_mut_pointer,
- .c_mut_pointer,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .mut_slice,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => false,
-
- .single_const_pointer,
- .many_const_pointer,
- .c_const_pointer,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .const_slice,
- => true,
-
- .pointer => !self.cast(Payload.Pointer).?.mutable,
- };
- }
-
- pub fn isVolatilePtr(self: Type) bool {
- return switch (self.tag()) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .f16,
- .f32,
- .f64,
- .f128,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .int_unsigned,
- .int_signed,
- .single_mut_pointer,
- .single_const_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => false,
-
- .pointer => {
- const payload = @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise);
- return payload.@"volatile";
- },
- };
- }
-
- pub fn isAllowzeroPtr(self: Type) bool {
- return switch (self.tag()) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .f16,
- .f32,
- .f64,
- .f128,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .int_unsigned,
- .int_signed,
- .single_mut_pointer,
- .single_const_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => false,
-
- .pointer => {
- const payload = @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise);
- return payload.@"allowzero";
- },
- };
- }
-
- /// Asserts that the type is an optional
- pub fn isPtrLikeOptional(self: Type) bool {
- switch (self.tag()) {
- .optional_single_const_pointer, .optional_single_mut_pointer => return true,
- .optional => {
- var buf: Payload.PointerSimple = undefined;
- const child_type = self.optionalChild(&buf);
- // optionals of zero sized pointers behave like bools
- if (!child_type.hasCodeGenBits()) return false;
-
- return child_type.zigTypeTag() == .Pointer and !child_type.isCPtr();
- },
- else => unreachable,
- }
- }
-
- /// Returns if type can be used for a runtime variable
- pub fn isValidVarType(self: Type, is_extern: bool) bool {
- var ty = self;
- while (true) switch (ty.zigTypeTag()) {
- .Bool,
- .Int,
- .Float,
- .ErrorSet,
- .Enum,
- .Frame,
- .AnyFrame,
- .Vector,
- => return true,
-
- .Opaque => return is_extern,
- .BoundFn,
- .ComptimeFloat,
- .ComptimeInt,
- .EnumLiteral,
- .NoReturn,
- .Type,
- .Void,
- .Undefined,
- .Null,
- => return false,
-
- .Optional => {
- var buf: Payload.PointerSimple = undefined;
- return ty.optionalChild(&buf).isValidVarType(is_extern);
- },
- .Pointer, .Array => ty = ty.elemType(),
-
- .ErrorUnion => @panic("TODO fn isValidVarType"),
- .Fn => @panic("TODO fn isValidVarType"),
- .Struct => @panic("TODO struct isValidVarType"),
- .Union => @panic("TODO union isValidVarType"),
- };
- }
-
- /// Asserts the type is a pointer or array type.
- pub fn elemType(self: Type) Type {
- return switch (self.tag()) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .f16,
- .f32,
- .f64,
- .f128,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_const_pointer,
- .optional_single_mut_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => unreachable,
-
- .array => self.cast(Payload.Array).?.elem_type,
- .array_sentinel => self.cast(Payload.ArraySentinel).?.elem_type,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- => self.castPointer().?.pointee_type,
- .array_u8, .array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.u8),
- .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int),
- .pointer => self.cast(Payload.Pointer).?.pointee_type,
- };
- }
-
- /// Asserts that the type is an optional.
- pub fn optionalChild(self: Type, buf: *Payload.PointerSimple) Type {
- return switch (self.tag()) {
- .optional => self.cast(Payload.Optional).?.child_type,
- .optional_single_mut_pointer => {
- buf.* = .{
- .base = .{ .tag = .single_mut_pointer },
- .pointee_type = self.castPointer().?.pointee_type,
- };
- return Type.initPayload(&buf.base);
- },
- .optional_single_const_pointer => {
- buf.* = .{
- .base = .{ .tag = .single_const_pointer },
- .pointee_type = self.castPointer().?.pointee_type,
- };
- return Type.initPayload(&buf.base);
- },
- else => unreachable,
- };
- }
-
- /// Asserts that the type is an optional.
- /// Same as `optionalChild` but allocates the buffer if needed.
- pub fn optionalChildAlloc(self: Type, allocator: *Allocator) !Type {
- return switch (self.tag()) {
- .optional => self.cast(Payload.Optional).?.child_type,
- .optional_single_mut_pointer, .optional_single_const_pointer => {
- const payload = try allocator.create(Payload.PointerSimple);
- payload.* = .{
- .base = .{
- .tag = if (self.tag() == .optional_single_const_pointer)
- .single_const_pointer
- else
- .single_mut_pointer,
- },
- .pointee_type = self.castPointer().?.pointee_type,
- };
- return Type.initPayload(&payload.base);
- },
- else => unreachable,
- };
- }
-
- /// Asserts the type is an array or vector.
- pub fn arrayLen(self: Type) u64 {
- return switch (self.tag()) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .f16,
- .f32,
- .f64,
- .f128,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => unreachable,
-
- .array => self.cast(Payload.Array).?.len,
- .array_sentinel => self.cast(Payload.ArraySentinel).?.len,
- .array_u8 => self.cast(Payload.Array_u8).?.len,
- .array_u8_sentinel_0 => self.cast(Payload.Array_u8_Sentinel0).?.len,
- };
- }
-
- /// Asserts the type is an array, pointer or vector.
- pub fn sentinel(self: Type) ?Value {
- return switch (self.tag()) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .f16,
- .f32,
- .f64,
- .f128,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .const_slice,
- .mut_slice,
- .const_slice_u8,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => unreachable,
-
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .single_const_pointer_to_comptime_int,
- .array,
- .array_u8,
- => return null,
-
- .pointer => return self.cast(Payload.Pointer).?.sentinel,
- .array_sentinel => return self.cast(Payload.ArraySentinel).?.sentinel,
- .array_u8_sentinel_0 => return Value.initTag(.zero),
- };
- }
-
- /// Returns true if and only if the type is a fixed-width integer.
- pub fn isInt(self: Type) bool {
- return self.isSignedInt() or self.isUnsignedInt();
- }
-
- /// Returns true if and only if the type is a fixed-width, signed integer.
- pub fn isSignedInt(self: Type) bool {
- return switch (self.tag()) {
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .int_unsigned,
- .u8,
- .usize,
- .c_ushort,
- .c_uint,
- .c_ulong,
- .c_ulonglong,
- .u16,
- .u32,
- .u64,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => false,
-
- .int_signed,
- .i8,
- .isize,
- .c_short,
- .c_int,
- .c_long,
- .c_longlong,
- .i16,
- .i32,
- .i64,
- => true,
- };
- }
-
- /// Returns true if and only if the type is a fixed-width, unsigned integer.
- pub fn isUnsignedInt(self: Type) bool {
- return switch (self.tag()) {
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .int_signed,
- .i8,
- .isize,
- .c_short,
- .c_int,
- .c_long,
- .c_longlong,
- .i16,
- .i32,
- .i64,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => false,
-
- .int_unsigned,
- .u8,
- .usize,
- .c_ushort,
- .c_uint,
- .c_ulong,
- .c_ulonglong,
- .u16,
- .u32,
- .u64,
- => true,
- };
- }
-
- /// Asserts the type is an integer.
- pub fn intInfo(self: Type, target: Target) struct { signed: bool, bits: u16 } {
- return switch (self.tag()) {
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => unreachable,
-
- .int_unsigned => .{ .signed = false, .bits = self.cast(Payload.IntUnsigned).?.bits },
- .int_signed => .{ .signed = true, .bits = self.cast(Payload.IntSigned).?.bits },
- .u8 => .{ .signed = false, .bits = 8 },
- .i8 => .{ .signed = true, .bits = 8 },
- .u16 => .{ .signed = false, .bits = 16 },
- .i16 => .{ .signed = true, .bits = 16 },
- .u32 => .{ .signed = false, .bits = 32 },
- .i32 => .{ .signed = true, .bits = 32 },
- .u64 => .{ .signed = false, .bits = 64 },
- .i64 => .{ .signed = true, .bits = 64 },
- .usize => .{ .signed = false, .bits = target.cpu.arch.ptrBitWidth() },
- .isize => .{ .signed = true, .bits = target.cpu.arch.ptrBitWidth() },
- .c_short => .{ .signed = true, .bits = CType.short.sizeInBits(target) },
- .c_ushort => .{ .signed = false, .bits = CType.ushort.sizeInBits(target) },
- .c_int => .{ .signed = true, .bits = CType.int.sizeInBits(target) },
- .c_uint => .{ .signed = false, .bits = CType.uint.sizeInBits(target) },
- .c_long => .{ .signed = true, .bits = CType.long.sizeInBits(target) },
- .c_ulong => .{ .signed = false, .bits = CType.ulong.sizeInBits(target) },
- .c_longlong => .{ .signed = true, .bits = CType.longlong.sizeInBits(target) },
- .c_ulonglong => .{ .signed = false, .bits = CType.ulonglong.sizeInBits(target) },
- };
- }
-
- pub fn isNamedInt(self: Type) bool {
- return switch (self.tag()) {
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .int_unsigned,
- .int_signed,
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => false,
-
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- => true,
- };
- }
-
- pub fn isFloat(self: Type) bool {
- return switch (self.tag()) {
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- => true,
-
- else => false,
- };
- }
-
- /// Asserts the type is a fixed-size float.
- pub fn floatBits(self: Type, target: Target) u16 {
- return switch (self.tag()) {
- .f16 => 16,
- .f32 => 32,
- .f64 => 64,
- .f128 => 128,
- .c_longdouble => CType.longdouble.sizeInBits(target),
-
- else => unreachable,
- };
- }
-
- /// Asserts the type is a function.
- pub fn fnParamLen(self: Type) usize {
- return switch (self.tag()) {
- .fn_noreturn_no_args => 0,
- .fn_void_no_args => 0,
- .fn_naked_noreturn_no_args => 0,
- .fn_ccc_void_no_args => 0,
- .function => @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise).param_types.len,
-
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => unreachable,
- };
- }
-
- /// Asserts the type is a function. The length of the slice must be at least the length
- /// given by `fnParamLen`.
- pub fn fnParamTypes(self: Type, types: []Type) void {
- switch (self.tag()) {
- .fn_noreturn_no_args => return,
- .fn_void_no_args => return,
- .fn_naked_noreturn_no_args => return,
- .fn_ccc_void_no_args => return,
- .function => {
- const payload = @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise);
- std.mem.copy(Type, types, payload.param_types);
- },
-
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => unreachable,
- }
- }
-
- /// Asserts the type is a function.
- pub fn fnParamType(self: Type, index: usize) Type {
- switch (self.tag()) {
- .function => {
- const payload = @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise);
- return payload.param_types[index];
- },
-
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => unreachable,
- }
- }
-
- /// Asserts the type is a function.
- pub fn fnReturnType(self: Type) Type {
- return switch (self.tag()) {
- .fn_noreturn_no_args => Type.initTag(.noreturn),
- .fn_naked_noreturn_no_args => Type.initTag(.noreturn),
-
- .fn_void_no_args,
- .fn_ccc_void_no_args,
- => Type.initTag(.void),
-
- .function => @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise).return_type,
-
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => unreachable,
- };
- }
-
- /// Asserts the type is a function.
- pub fn fnCallingConvention(self: Type) std.builtin.CallingConvention {
- return switch (self.tag()) {
- .fn_noreturn_no_args => .Unspecified,
- .fn_void_no_args => .Unspecified,
- .fn_naked_noreturn_no_args => .Naked,
- .fn_ccc_void_no_args => .C,
- .function => @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise).cc,
-
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => unreachable,
- };
- }
-
- /// Asserts the type is a function.
- pub fn fnIsVarArgs(self: Type) bool {
- return switch (self.tag()) {
- .fn_noreturn_no_args => false,
- .fn_void_no_args => false,
- .fn_naked_noreturn_no_args => false,
- .fn_ccc_void_no_args => false,
- .function => false,
-
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => unreachable,
- };
- }
-
- pub fn isNumeric(self: Type) bool {
- return switch (self.tag()) {
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .comptime_int,
- .comptime_float,
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .int_unsigned,
- .int_signed,
- => true,
-
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .noreturn,
- .@"null",
- .@"undefined",
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => false,
- };
- }
-
- pub fn onePossibleValue(self: Type) ?Value {
- var ty = self;
- while (true) switch (ty.tag()) {
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .comptime_int,
- .comptime_float,
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .bool,
- .type,
- .anyerror,
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .single_const_pointer_to_comptime_int,
- .array_sentinel,
- .array_u8_sentinel_0,
- .const_slice_u8,
- .const_slice,
- .mut_slice,
- .c_void,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .anyerror_void_error_union,
- .anyframe_T,
- .@"anyframe",
- .error_union,
- .error_set,
- .error_set_single,
- => return null,
-
- .void => return Value.initTag(.void_value),
- .noreturn => return Value.initTag(.unreachable_value),
- .@"null" => return Value.initTag(.null_value),
- .@"undefined" => return Value.initTag(.undef),
-
- .int_unsigned => {
- if (ty.cast(Payload.IntUnsigned).?.bits == 0) {
- return Value.initTag(.zero);
- } else {
- return null;
- }
- },
- .int_signed => {
- if (ty.cast(Payload.IntSigned).?.bits == 0) {
- return Value.initTag(.zero);
- } else {
- return null;
- }
- },
- .array, .array_u8 => {
- if (ty.arrayLen() == 0)
- return Value.initTag(.empty_array);
- ty = ty.elemType();
- continue;
- },
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .single_const_pointer,
- .single_mut_pointer,
- => {
- const ptr = ty.castPointer().?;
- ty = ptr.pointee_type;
- continue;
- },
- .pointer => {
- ty = ty.cast(Payload.Pointer).?.pointee_type;
- continue;
- },
- };
- }
-
- pub fn isCPtr(self: Type) bool {
- return switch (self.tag()) {
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .comptime_int,
- .comptime_float,
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .bool,
- .type,
- .anyerror,
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .c_void,
- .void,
- .noreturn,
- .@"null",
- .@"undefined",
- .int_unsigned,
- .int_signed,
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .const_slice,
- .mut_slice,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- => return false,
-
- .c_const_pointer,
- .c_mut_pointer,
- => return true,
-
- .pointer => self.cast(Payload.Pointer).?.size == .C,
- };
- }
-
- pub fn isIndexable(self: Type) bool {
- const zig_tag = self.zigTypeTag();
- // TODO tuples are indexable
- return zig_tag == .Array or zig_tag == .Vector or self.isSlice() or
- (self.isSinglePointer() and self.elemType().zigTypeTag() == .Array);
- }
-
- /// This enum does not directly correspond to `std.builtin.TypeId` because
- /// it has extra enum tags in it, as a way of using less memory. For example,
- /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types
- /// but with different alignment values, in this data structure they are represented
- /// with different enum tags, because the the former requires more payload data than the latter.
- /// See `zigTypeTag` for the function that corresponds to `std.builtin.TypeId`.
- pub const Tag = enum {
- // The first section of this enum are tags that require no payload.
- u8,
- i8,
- u16,
- i16,
- u32,
- i32,
- u64,
- i64,
- usize,
- isize,
- c_short,
- c_ushort,
- c_int,
- c_uint,
- c_long,
- c_ulong,
- c_longlong,
- c_ulonglong,
- c_longdouble,
- f16,
- f32,
- f64,
- f128,
- c_void,
- bool,
- void,
- type,
- anyerror,
- comptime_int,
- comptime_float,
- noreturn,
- enum_literal,
- @"null",
- @"undefined",
- fn_noreturn_no_args,
- fn_void_no_args,
- fn_naked_noreturn_no_args,
- fn_ccc_void_no_args,
- single_const_pointer_to_comptime_int,
- anyerror_void_error_union,
- @"anyframe",
- const_slice_u8, // See last_no_payload_tag below.
- // After this, the tag requires a payload.
-
- array_u8,
- array_u8_sentinel_0,
- array,
- array_sentinel,
- pointer,
- single_const_pointer,
- single_mut_pointer,
- many_const_pointer,
- many_mut_pointer,
- c_const_pointer,
- c_mut_pointer,
- const_slice,
- mut_slice,
- int_signed,
- int_unsigned,
- function,
- optional,
- optional_single_mut_pointer,
- optional_single_const_pointer,
- error_union,
- anyframe_T,
- error_set,
- error_set_single,
-
- pub const last_no_payload_tag = Tag.const_slice_u8;
- pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
- };
-
- pub const Payload = struct {
- tag: Tag,
-
- pub const Array_u8_Sentinel0 = struct {
- base: Payload = Payload{ .tag = .array_u8_sentinel_0 },
-
- len: u64,
- };
-
- pub const Array_u8 = struct {
- base: Payload = Payload{ .tag = .array_u8 },
-
- len: u64,
- };
-
- pub const Array = struct {
- base: Payload = Payload{ .tag = .array },
-
- len: u64,
- elem_type: Type,
- };
-
- pub const ArraySentinel = struct {
- base: Payload = Payload{ .tag = .array_sentinel },
-
- len: u64,
- sentinel: Value,
- elem_type: Type,
- };
-
- pub const PointerSimple = struct {
- base: Payload,
-
- pointee_type: Type,
- };
-
- pub const IntSigned = struct {
- base: Payload = Payload{ .tag = .int_signed },
-
- bits: u16,
- };
-
- pub const IntUnsigned = struct {
- base: Payload = Payload{ .tag = .int_unsigned },
-
- bits: u16,
- };
-
- pub const Function = struct {
- base: Payload = Payload{ .tag = .function },
-
- param_types: []Type,
- return_type: Type,
- cc: std.builtin.CallingConvention,
- };
-
- pub const Optional = struct {
- base: Payload = Payload{ .tag = .optional },
-
- child_type: Type,
- };
-
- pub const Pointer = struct {
- base: Payload = .{ .tag = .pointer },
-
- pointee_type: Type,
- sentinel: ?Value,
- /// If zero use pointee_type.AbiAlign()
- @"align": u32,
- bit_offset: u16,
- host_size: u16,
- @"allowzero": bool,
- mutable: bool,
- @"volatile": bool,
- size: std.builtin.TypeInfo.Pointer.Size,
- };
-
- pub const ErrorUnion = struct {
- base: Payload = .{ .tag = .error_union },
-
- error_set: Type,
- payload: Type,
- };
-
- pub const AnyFrame = struct {
- base: Payload = .{ .tag = .anyframe_T },
-
- return_type: Type,
- };
-
- pub const ErrorSet = struct {
- base: Payload = .{ .tag = .error_set },
-
- decl: *Module.Decl,
- };
-
- pub const ErrorSetSingle = struct {
- base: Payload = .{ .tag = .error_set_single },
-
- /// memory is owned by `Module`
- name: []const u8,
- };
- };
-};
-
-pub const CType = enum {
- short,
- ushort,
- int,
- uint,
- long,
- ulong,
- longlong,
- ulonglong,
- longdouble,
-
- pub fn sizeInBits(self: CType, target: Target) u16 {
- const arch = target.cpu.arch;
- switch (target.os.tag) {
- .freestanding, .other => switch (target.cpu.arch) {
- .msp430 => switch (self) {
- .short,
- .ushort,
- .int,
- .uint,
- => return 16,
- .long,
- .ulong,
- => return 32,
- .longlong,
- .ulonglong,
- => return 64,
- .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
- },
- else => switch (self) {
- .short,
- .ushort,
- => return 16,
- .int,
- .uint,
- => return 32,
- .long,
- .ulong,
- => return target.cpu.arch.ptrBitWidth(),
- .longlong,
- .ulonglong,
- => return 64,
- .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
- },
- },
-
- .linux,
- .macosx,
- .freebsd,
- .netbsd,
- .dragonfly,
- .openbsd,
- .wasi,
- .emscripten,
- => switch (self) {
- .short,
- .ushort,
- => return 16,
- .int,
- .uint,
- => return 32,
- .long,
- .ulong,
- => return target.cpu.arch.ptrBitWidth(),
- .longlong,
- .ulonglong,
- => return 64,
- .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
- },
-
- .windows, .uefi => switch (self) {
- .short,
- .ushort,
- => return 16,
- .int,
- .uint,
- .long,
- .ulong,
- => return 32,
- .longlong,
- .ulonglong,
- => return 64,
- .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
- },
-
- .ios => switch (self) {
- .short,
- .ushort,
- => return 16,
- .int,
- .uint,
- => return 32,
- .long,
- .ulong,
- .longlong,
- .ulonglong,
- => return 64,
- .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
- },
-
- .ananas,
- .cloudabi,
- .fuchsia,
- .kfreebsd,
- .lv2,
- .solaris,
- .haiku,
- .minix,
- .rtems,
- .nacl,
- .cnk,
- .aix,
- .cuda,
- .nvcl,
- .amdhsa,
- .ps4,
- .elfiamcu,
- .tvos,
- .watchos,
- .mesa3d,
- .contiki,
- .amdpal,
- .hermit,
- .hurd,
- => @panic("TODO specify the C integer and float type sizes for this OS"),
- }
- }
-};
diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig
deleted file mode 100644
index b65aa06beaa0409ccf199b2cbd805fa5a48548d6..0000000000000000000000000000000000000000
--- a/src-self-hosted/value.zig
+++ /dev/null
@@ -1,1641 +0,0 @@
-const std = @import("std");
-const Type = @import("type.zig").Type;
-const log2 = std.math.log2;
-const assert = std.debug.assert;
-const BigIntConst = std.math.big.int.Const;
-const BigIntMutable = std.math.big.int.Mutable;
-const Target = std.Target;
-const Allocator = std.mem.Allocator;
-const Module = @import("Module.zig");
-
-/// This is the raw data, with no bookkeeping, no memory awareness,
-/// no de-duplication, and no type system awareness.
-/// It's important for this type to be small.
-/// This union takes advantage of the fact that the first page of memory
-/// is unmapped, giving us 4096 possible enum tags that have no payload.
-pub const Value = extern union {
- /// If the tag value is less than Tag.no_payload_count, then no pointer
- /// dereference is needed.
- tag_if_small_enough: usize,
- ptr_otherwise: *Payload,
-
- pub const Tag = enum {
- // The first section of this enum are tags that require no payload.
- u8_type,
- i8_type,
- u16_type,
- i16_type,
- u32_type,
- i32_type,
- u64_type,
- i64_type,
- usize_type,
- isize_type,
- c_short_type,
- c_ushort_type,
- c_int_type,
- c_uint_type,
- c_long_type,
- c_ulong_type,
- c_longlong_type,
- c_ulonglong_type,
- c_longdouble_type,
- f16_type,
- f32_type,
- f64_type,
- f128_type,
- c_void_type,
- bool_type,
- void_type,
- type_type,
- anyerror_type,
- comptime_int_type,
- comptime_float_type,
- noreturn_type,
- null_type,
- undefined_type,
- fn_noreturn_no_args_type,
- fn_void_no_args_type,
- fn_naked_noreturn_no_args_type,
- fn_ccc_void_no_args_type,
- single_const_pointer_to_comptime_int_type,
- const_slice_u8_type,
- enum_literal_type,
- anyframe_type,
-
- undef,
- zero,
- one,
- void_value,
- unreachable_value,
- empty_array,
- null_value,
- bool_true,
- bool_false, // See last_no_payload_tag below.
- // After this, the tag requires a payload.
-
- ty,
- int_type,
- int_u64,
- int_i64,
- int_big_positive,
- int_big_negative,
- function,
- variable,
- ref_val,
- decl_ref,
- elem_ptr,
- bytes,
- repeated, // the value is a value repeated some number of times
- float_16,
- float_32,
- float_64,
- float_128,
- enum_literal,
- error_set,
- @"error",
-
- pub const last_no_payload_tag = Tag.bool_false;
- pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
- };
-
- pub fn initTag(small_tag: Tag) Value {
- assert(@enumToInt(small_tag) < Tag.no_payload_count);
- return .{ .tag_if_small_enough = @enumToInt(small_tag) };
- }
-
- pub fn initPayload(payload: *Payload) Value {
- assert(@enumToInt(payload.tag) >= Tag.no_payload_count);
- return .{ .ptr_otherwise = payload };
- }
-
- pub fn tag(self: Value) Tag {
- if (self.tag_if_small_enough < Tag.no_payload_count) {
- return @intToEnum(Tag, @intCast(@TagType(Tag), self.tag_if_small_enough));
- } else {
- return self.ptr_otherwise.tag;
- }
- }
-
- pub fn cast(self: Value, comptime T: type) ?*T {
- if (self.tag_if_small_enough < Tag.no_payload_count)
- return null;
-
- const expected_tag = std.meta.fieldInfo(T, "base").default_value.?.tag;
- if (self.ptr_otherwise.tag != expected_tag)
- return null;
-
- return @fieldParentPtr(T, "base", self.ptr_otherwise);
- }
-
- pub fn copy(self: Value, allocator: *Allocator) error{OutOfMemory}!Value {
- if (self.tag_if_small_enough < Tag.no_payload_count) {
- return Value{ .tag_if_small_enough = self.tag_if_small_enough };
- } else switch (self.ptr_otherwise.tag) {
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .anyframe_type,
- .undef,
- .zero,
- .one,
- .void_value,
- .unreachable_value,
- .empty_array,
- .null_value,
- .bool_true,
- .bool_false,
- => unreachable,
-
- .ty => {
- const payload = @fieldParentPtr(Payload.Ty, "base", self.ptr_otherwise);
- const new_payload = try allocator.create(Payload.Ty);
- new_payload.* = .{
- .base = payload.base,
- .ty = try payload.ty.copy(allocator),
- };
- return Value{ .ptr_otherwise = &new_payload.base };
- },
- .int_type => return self.copyPayloadShallow(allocator, Payload.IntType),
- .int_u64 => return self.copyPayloadShallow(allocator, Payload.Int_u64),
- .int_i64 => return self.copyPayloadShallow(allocator, Payload.Int_i64),
- .int_big_positive => {
- @panic("TODO implement copying of big ints");
- },
- .int_big_negative => {
- @panic("TODO implement copying of big ints");
- },
- .function => return self.copyPayloadShallow(allocator, Payload.Function),
- .variable => return self.copyPayloadShallow(allocator, Payload.Variable),
- .ref_val => {
- const payload = @fieldParentPtr(Payload.RefVal, "base", self.ptr_otherwise);
- const new_payload = try allocator.create(Payload.RefVal);
- new_payload.* = .{
- .base = payload.base,
- .val = try payload.val.copy(allocator),
- };
- return Value{ .ptr_otherwise = &new_payload.base };
- },
- .decl_ref => return self.copyPayloadShallow(allocator, Payload.DeclRef),
- .elem_ptr => {
- const payload = @fieldParentPtr(Payload.ElemPtr, "base", self.ptr_otherwise);
- const new_payload = try allocator.create(Payload.ElemPtr);
- new_payload.* = .{
- .base = payload.base,
- .array_ptr = try payload.array_ptr.copy(allocator),
- .index = payload.index,
- };
- return Value{ .ptr_otherwise = &new_payload.base };
- },
- .bytes => return self.copyPayloadShallow(allocator, Payload.Bytes),
- .repeated => {
- const payload = @fieldParentPtr(Payload.Repeated, "base", self.ptr_otherwise);
- const new_payload = try allocator.create(Payload.Repeated);
- new_payload.* = .{
- .base = payload.base,
- .val = try payload.val.copy(allocator),
- };
- return Value{ .ptr_otherwise = &new_payload.base };
- },
- .float_16 => return self.copyPayloadShallow(allocator, Payload.Float_16),
- .float_32 => return self.copyPayloadShallow(allocator, Payload.Float_32),
- .float_64 => return self.copyPayloadShallow(allocator, Payload.Float_64),
- .float_128 => return self.copyPayloadShallow(allocator, Payload.Float_128),
- .enum_literal => {
- const payload = @fieldParentPtr(Payload.Bytes, "base", self.ptr_otherwise);
- const new_payload = try allocator.create(Payload.Bytes);
- new_payload.* = .{
- .base = payload.base,
- .data = try allocator.dupe(u8, payload.data),
- };
- return Value{ .ptr_otherwise = &new_payload.base };
- },
- .@"error" => return self.copyPayloadShallow(allocator, Payload.Error),
-
- // memory is managed by the declaration
- .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet),
- }
- }
-
- fn copyPayloadShallow(self: Value, allocator: *Allocator, comptime T: type) error{OutOfMemory}!Value {
- const payload = @fieldParentPtr(T, "base", self.ptr_otherwise);
- const new_payload = try allocator.create(T);
- new_payload.* = payload.*;
- return Value{ .ptr_otherwise = &new_payload.base };
- }
-
- pub fn format(
- self: Value,
- comptime fmt: []const u8,
- options: std.fmt.FormatOptions,
- out_stream: anytype,
- ) !void {
- comptime assert(fmt.len == 0);
- var val = self;
- while (true) switch (val.tag()) {
- .u8_type => return out_stream.writeAll("u8"),
- .i8_type => return out_stream.writeAll("i8"),
- .u16_type => return out_stream.writeAll("u16"),
- .i16_type => return out_stream.writeAll("i16"),
- .u32_type => return out_stream.writeAll("u32"),
- .i32_type => return out_stream.writeAll("i32"),
- .u64_type => return out_stream.writeAll("u64"),
- .i64_type => return out_stream.writeAll("i64"),
- .isize_type => return out_stream.writeAll("isize"),
- .usize_type => return out_stream.writeAll("usize"),
- .c_short_type => return out_stream.writeAll("c_short"),
- .c_ushort_type => return out_stream.writeAll("c_ushort"),
- .c_int_type => return out_stream.writeAll("c_int"),
- .c_uint_type => return out_stream.writeAll("c_uint"),
- .c_long_type => return out_stream.writeAll("c_long"),
- .c_ulong_type => return out_stream.writeAll("c_ulong"),
- .c_longlong_type => return out_stream.writeAll("c_longlong"),
- .c_ulonglong_type => return out_stream.writeAll("c_ulonglong"),
- .c_longdouble_type => return out_stream.writeAll("c_longdouble"),
- .f16_type => return out_stream.writeAll("f16"),
- .f32_type => return out_stream.writeAll("f32"),
- .f64_type => return out_stream.writeAll("f64"),
- .f128_type => return out_stream.writeAll("f128"),
- .c_void_type => return out_stream.writeAll("c_void"),
- .bool_type => return out_stream.writeAll("bool"),
- .void_type => return out_stream.writeAll("void"),
- .type_type => return out_stream.writeAll("type"),
- .anyerror_type => return out_stream.writeAll("anyerror"),
- .comptime_int_type => return out_stream.writeAll("comptime_int"),
- .comptime_float_type => return out_stream.writeAll("comptime_float"),
- .noreturn_type => return out_stream.writeAll("noreturn"),
- .null_type => return out_stream.writeAll("@Type(.Null)"),
- .undefined_type => return out_stream.writeAll("@Type(.Undefined)"),
- .fn_noreturn_no_args_type => return out_stream.writeAll("fn() noreturn"),
- .fn_void_no_args_type => return out_stream.writeAll("fn() void"),
- .fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
- .fn_ccc_void_no_args_type => return out_stream.writeAll("fn() callconv(.C) void"),
- .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"),
- .const_slice_u8_type => return out_stream.writeAll("[]const u8"),
- .enum_literal_type => return out_stream.writeAll("@Type(.EnumLiteral)"),
- .anyframe_type => return out_stream.writeAll("anyframe"),
-
- .null_value => return out_stream.writeAll("null"),
- .undef => return out_stream.writeAll("undefined"),
- .zero => return out_stream.writeAll("0"),
- .one => return out_stream.writeAll("1"),
- .void_value => return out_stream.writeAll("{}"),
- .unreachable_value => return out_stream.writeAll("unreachable"),
- .bool_true => return out_stream.writeAll("true"),
- .bool_false => return out_stream.writeAll("false"),
- .ty => return val.cast(Payload.Ty).?.ty.format("", options, out_stream),
- .int_type => {
- const int_type = val.cast(Payload.IntType).?;
- return out_stream.print("{}{}", .{
- if (int_type.signed) "s" else "u",
- int_type.bits,
- });
- },
- .int_u64 => return std.fmt.formatIntValue(val.cast(Payload.Int_u64).?.int, "", options, out_stream),
- .int_i64 => return std.fmt.formatIntValue(val.cast(Payload.Int_i64).?.int, "", options, out_stream),
- .int_big_positive => return out_stream.print("{}", .{val.cast(Payload.IntBigPositive).?.asBigInt()}),
- .int_big_negative => return out_stream.print("{}", .{val.cast(Payload.IntBigNegative).?.asBigInt()}),
- .function => return out_stream.writeAll("(function)"),
- .variable => return out_stream.writeAll("(variable)"),
- .ref_val => {
- const ref_val = val.cast(Payload.RefVal).?;
- try out_stream.writeAll("&const ");
- val = ref_val.val;
- },
- .decl_ref => return out_stream.writeAll("(decl ref)"),
- .elem_ptr => {
- const elem_ptr = val.cast(Payload.ElemPtr).?;
- try out_stream.print("&[{}] ", .{elem_ptr.index});
- val = elem_ptr.array_ptr;
- },
- .empty_array => return out_stream.writeAll(".{}"),
- .enum_literal, .bytes => return std.zig.renderStringLiteral(self.cast(Payload.Bytes).?.data, out_stream),
- .repeated => {
- try out_stream.writeAll("(repeated) ");
- val = val.cast(Payload.Repeated).?.val;
- },
- .float_16 => return out_stream.print("{}", .{val.cast(Payload.Float_16).?.val}),
- .float_32 => return out_stream.print("{}", .{val.cast(Payload.Float_32).?.val}),
- .float_64 => return out_stream.print("{}", .{val.cast(Payload.Float_64).?.val}),
- .float_128 => return out_stream.print("{}", .{val.cast(Payload.Float_128).?.val}),
- .error_set => {
- const error_set = val.cast(Payload.ErrorSet).?;
- try out_stream.writeAll("error{");
- var it = error_set.fields.iterator();
- while (it.next()) |entry| {
- try out_stream.print("{},", .{entry.value});
- }
- return out_stream.writeAll("}");
- },
- .@"error" => return out_stream.print("error.{}", .{val.cast(Payload.Error).?.name}),
- };
- }
-
- /// Asserts that the value is representable as an array of bytes.
- /// Copies the value into a freshly allocated slice of memory, which is owned by the caller.
- pub fn toAllocatedBytes(self: Value, allocator: *Allocator) ![]u8 {
- if (self.cast(Payload.Bytes)) |bytes| {
- return std.mem.dupe(allocator, u8, bytes.data);
- }
- if (self.cast(Payload.Repeated)) |repeated| {
- @panic("TODO implement toAllocatedBytes for this Value tag");
- }
- if (self.cast(Payload.DeclRef)) |declref| {
- const val = try declref.decl.value();
- return val.toAllocatedBytes(allocator);
- }
- unreachable;
- }
-
- /// Asserts that the value is representable as a type.
- pub fn toType(self: Value, allocator: *Allocator) !Type {
- return switch (self.tag()) {
- .ty => self.cast(Payload.Ty).?.ty,
- .u8_type => Type.initTag(.u8),
- .i8_type => Type.initTag(.i8),
- .u16_type => Type.initTag(.u16),
- .i16_type => Type.initTag(.i16),
- .u32_type => Type.initTag(.u32),
- .i32_type => Type.initTag(.i32),
- .u64_type => Type.initTag(.u64),
- .i64_type => Type.initTag(.i64),
- .usize_type => Type.initTag(.usize),
- .isize_type => Type.initTag(.isize),
- .c_short_type => Type.initTag(.c_short),
- .c_ushort_type => Type.initTag(.c_ushort),
- .c_int_type => Type.initTag(.c_int),
- .c_uint_type => Type.initTag(.c_uint),
- .c_long_type => Type.initTag(.c_long),
- .c_ulong_type => Type.initTag(.c_ulong),
- .c_longlong_type => Type.initTag(.c_longlong),
- .c_ulonglong_type => Type.initTag(.c_ulonglong),
- .c_longdouble_type => Type.initTag(.c_longdouble),
- .f16_type => Type.initTag(.f16),
- .f32_type => Type.initTag(.f32),
- .f64_type => Type.initTag(.f64),
- .f128_type => Type.initTag(.f128),
- .c_void_type => Type.initTag(.c_void),
- .bool_type => Type.initTag(.bool),
- .void_type => Type.initTag(.void),
- .type_type => Type.initTag(.type),
- .anyerror_type => Type.initTag(.anyerror),
- .comptime_int_type => Type.initTag(.comptime_int),
- .comptime_float_type => Type.initTag(.comptime_float),
- .noreturn_type => Type.initTag(.noreturn),
- .null_type => Type.initTag(.@"null"),
- .undefined_type => Type.initTag(.@"undefined"),
- .fn_noreturn_no_args_type => Type.initTag(.fn_noreturn_no_args),
- .fn_void_no_args_type => Type.initTag(.fn_void_no_args),
- .fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args),
- .fn_ccc_void_no_args_type => Type.initTag(.fn_ccc_void_no_args),
- .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int),
- .const_slice_u8_type => Type.initTag(.const_slice_u8),
- .enum_literal_type => Type.initTag(.enum_literal),
- .anyframe_type => Type.initTag(.@"anyframe"),
-
- .int_type => {
- const payload = self.cast(Payload.IntType).?;
- if (payload.signed) {
- const new = try allocator.create(Type.Payload.IntSigned);
- new.* = .{ .bits = payload.bits };
- return Type.initPayload(&new.base);
- } else {
- const new = try allocator.create(Type.Payload.IntUnsigned);
- new.* = .{ .bits = payload.bits };
- return Type.initPayload(&new.base);
- }
- },
- .error_set => {
- const payload = self.cast(Payload.ErrorSet).?;
- const new = try allocator.create(Type.Payload.ErrorSet);
- new.* = .{ .decl = payload.decl };
- return Type.initPayload(&new.base);
- },
-
- .undef,
- .zero,
- .one,
- .void_value,
- .unreachable_value,
- .empty_array,
- .bool_true,
- .bool_false,
- .null_value,
- .int_u64,
- .int_i64,
- .int_big_positive,
- .int_big_negative,
- .function,
- .variable,
- .ref_val,
- .decl_ref,
- .elem_ptr,
- .bytes,
- .repeated,
- .float_16,
- .float_32,
- .float_64,
- .float_128,
- .enum_literal,
- .@"error",
- => unreachable,
- };
- }
-
- /// Asserts the value is an integer.
- pub fn toBigInt(self: Value, space: *BigIntSpace) BigIntConst {
- switch (self.tag()) {
- .ty,
- .int_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .anyframe_type,
- .null_value,
- .function,
- .variable,
- .ref_val,
- .decl_ref,
- .elem_ptr,
- .bytes,
- .repeated,
- .float_16,
- .float_32,
- .float_64,
- .float_128,
- .void_value,
- .unreachable_value,
- .empty_array,
- .enum_literal,
- .error_set,
- .@"error",
- => unreachable,
-
- .undef => unreachable,
-
- .zero,
- .bool_false,
- => return BigIntMutable.init(&space.limbs, 0).toConst(),
-
- .one,
- .bool_true,
- => return BigIntMutable.init(&space.limbs, 1).toConst(),
-
- .int_u64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_u64).?.int).toConst(),
- .int_i64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_i64).?.int).toConst(),
- .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt(),
- .int_big_negative => return self.cast(Payload.IntBigPositive).?.asBigInt(),
- }
- }
-
- /// Asserts the value is an integer and it fits in a u64
- pub fn toUnsignedInt(self: Value) u64 {
- switch (self.tag()) {
- .ty,
- .int_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .anyframe_type,
- .null_value,
- .function,
- .variable,
- .ref_val,
- .decl_ref,
- .elem_ptr,
- .bytes,
- .repeated,
- .float_16,
- .float_32,
- .float_64,
- .float_128,
- .void_value,
- .unreachable_value,
- .empty_array,
- .enum_literal,
- .error_set,
- .@"error",
- => unreachable,
-
- .undef => unreachable,
-
- .zero,
- .bool_false,
- => return 0,
-
- .one,
- .bool_true,
- => return 1,
-
- .int_u64 => return self.cast(Payload.Int_u64).?.int,
- .int_i64 => return @intCast(u64, self.cast(Payload.Int_i64).?.int),
- .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().to(u64) catch unreachable,
- .int_big_negative => return self.cast(Payload.IntBigNegative).?.asBigInt().to(u64) catch unreachable,
- }
- }
-
- /// Asserts the value is an integer and it fits in a i64
- pub fn toSignedInt(self: Value) i64 {
- switch (self.tag()) {
- .ty,
- .int_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .anyframe_type,
- .null_value,
- .function,
- .variable,
- .ref_val,
- .decl_ref,
- .elem_ptr,
- .bytes,
- .repeated,
- .float_16,
- .float_32,
- .float_64,
- .float_128,
- .void_value,
- .unreachable_value,
- .empty_array,
- .enum_literal,
- .error_set,
- .@"error",
- => unreachable,
-
- .undef => unreachable,
-
- .zero,
- .bool_false,
- => return 0,
-
- .one,
- .bool_true,
- => return 1,
-
- .int_u64 => return @intCast(i64, self.cast(Payload.Int_u64).?.int),
- .int_i64 => return self.cast(Payload.Int_i64).?.int,
- .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().to(i64) catch unreachable,
- .int_big_negative => return self.cast(Payload.IntBigNegative).?.asBigInt().to(i64) catch unreachable,
- }
- }
-
- pub fn toBool(self: Value) bool {
- return switch (self.tag()) {
- .bool_true => true,
- .bool_false, .zero => false,
- else => unreachable,
- };
- }
-
- /// Asserts that the value is a float or an integer.
- pub fn toFloat(self: Value, comptime T: type) T {
- return switch (self.tag()) {
- .float_16 => @panic("TODO soft float"),
- .float_32 => @floatCast(T, self.cast(Payload.Float_32).?.val),
- .float_64 => @floatCast(T, self.cast(Payload.Float_64).?.val),
- .float_128 => @floatCast(T, self.cast(Payload.Float_128).?.val),
-
- .zero => 0,
- .one => 1,
- .int_u64 => @intToFloat(T, self.cast(Payload.Int_u64).?.int),
- .int_i64 => @intToFloat(T, self.cast(Payload.Int_i64).?.int),
-
- .int_big_positive, .int_big_negative => @panic("big int to f128"),
- else => unreachable,
- };
- }
-
- /// Asserts the value is an integer and not undefined.
- /// Returns the number of bits the value requires to represent stored in twos complement form.
- pub fn intBitCountTwosComp(self: Value) usize {
- switch (self.tag()) {
- .ty,
- .int_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .anyframe_type,
- .null_value,
- .function,
- .variable,
- .ref_val,
- .decl_ref,
- .elem_ptr,
- .bytes,
- .undef,
- .repeated,
- .float_16,
- .float_32,
- .float_64,
- .float_128,
- .void_value,
- .unreachable_value,
- .empty_array,
- .enum_literal,
- .error_set,
- .@"error",
- => unreachable,
-
- .zero,
- .bool_false,
- => return 0,
-
- .one,
- .bool_true,
- => return 1,
-
- .int_u64 => {
- const x = self.cast(Payload.Int_u64).?.int;
- if (x == 0) return 0;
- return std.math.log2(x) + 1;
- },
- .int_i64 => {
- @panic("TODO implement i64 intBitCountTwosComp");
- },
- .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().bitCountTwosComp(),
- .int_big_negative => return self.cast(Payload.IntBigNegative).?.asBigInt().bitCountTwosComp(),
- }
- }
-
- /// Asserts the value is an integer, and the destination type is ComptimeInt or Int.
- pub fn intFitsInType(self: Value, ty: Type, target: Target) bool {
- switch (self.tag()) {
- .ty,
- .int_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .anyframe_type,
- .null_value,
- .function,
- .variable,
- .ref_val,
- .decl_ref,
- .elem_ptr,
- .bytes,
- .repeated,
- .float_16,
- .float_32,
- .float_64,
- .float_128,
- .void_value,
- .unreachable_value,
- .empty_array,
- .enum_literal,
- .error_set,
- .@"error",
- => unreachable,
-
- .zero,
- .undef,
- .bool_false,
- => return true,
-
- .one,
- .bool_true,
- => {
- const info = ty.intInfo(target);
- if (info.signed) {
- return info.bits >= 2;
- } else {
- return info.bits >= 1;
- }
- },
-
- .int_u64 => switch (ty.zigTypeTag()) {
- .Int => {
- const x = self.cast(Payload.Int_u64).?.int;
- if (x == 0) return true;
- const info = ty.intInfo(target);
- const needed_bits = std.math.log2(x) + 1 + @boolToInt(info.signed);
- return info.bits >= needed_bits;
- },
- .ComptimeInt => return true,
- else => unreachable,
- },
- .int_i64 => switch (ty.zigTypeTag()) {
- .Int => {
- const x = self.cast(Payload.Int_i64).?.int;
- if (x == 0) return true;
- const info = ty.intInfo(target);
- if (!info.signed and x < 0)
- return false;
- @panic("TODO implement i64 intFitsInType");
- },
- .ComptimeInt => return true,
- else => unreachable,
- },
- .int_big_positive => switch (ty.zigTypeTag()) {
- .Int => {
- const info = ty.intInfo(target);
- return self.cast(Payload.IntBigPositive).?.asBigInt().fitsInTwosComp(info.signed, info.bits);
- },
- .ComptimeInt => return true,
- else => unreachable,
- },
- .int_big_negative => switch (ty.zigTypeTag()) {
- .Int => {
- const info = ty.intInfo(target);
- return self.cast(Payload.IntBigNegative).?.asBigInt().fitsInTwosComp(info.signed, info.bits);
- },
- .ComptimeInt => return true,
- else => unreachable,
- },
- }
- }
-
- /// Converts an integer or a float to a float.
- /// Returns `error.Overflow` if the value does not fit in the new type.
- pub fn floatCast(self: Value, allocator: *Allocator, ty: Type, target: Target) !Value {
- const dest_bit_count = switch (ty.tag()) {
- .comptime_float => 128,
- else => ty.floatBits(target),
- };
- switch (dest_bit_count) {
- 16, 32, 64, 128 => {},
- else => std.debug.panic("TODO float cast bit count {}\n", .{dest_bit_count}),
- }
- if (ty.isInt()) {
- @panic("TODO int to float");
- }
-
- switch (dest_bit_count) {
- 16 => {
- @panic("TODO soft float");
- // var res_payload = Value.Payload.Float_16{.val = self.toFloat(f16)};
- // if (!self.eql(Value.initPayload(&res_payload.base)))
- // return error.Overflow;
- // return Value.initPayload(&res_payload.base).copy(allocator);
- },
- 32 => {
- var res_payload = Value.Payload.Float_32{ .val = self.toFloat(f32) };
- if (!self.eql(Value.initPayload(&res_payload.base)))
- return error.Overflow;
- return Value.initPayload(&res_payload.base).copy(allocator);
- },
- 64 => {
- var res_payload = Value.Payload.Float_64{ .val = self.toFloat(f64) };
- if (!self.eql(Value.initPayload(&res_payload.base)))
- return error.Overflow;
- return Value.initPayload(&res_payload.base).copy(allocator);
- },
- 128 => {
- const float_payload = try allocator.create(Value.Payload.Float_128);
- float_payload.* = .{ .val = self.toFloat(f128) };
- return Value.initPayload(&float_payload.base);
- },
- else => unreachable,
- }
- }
-
- /// Asserts the value is a float
- pub fn floatHasFraction(self: Value) bool {
- return switch (self.tag()) {
- .ty,
- .int_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .anyframe_type,
- .bool_true,
- .bool_false,
- .null_value,
- .function,
- .variable,
- .ref_val,
- .decl_ref,
- .elem_ptr,
- .bytes,
- .repeated,
- .undef,
- .int_u64,
- .int_i64,
- .int_big_positive,
- .int_big_negative,
- .empty_array,
- .void_value,
- .unreachable_value,
- .enum_literal,
- .error_set,
- .@"error",
- => unreachable,
-
- .zero,
- .one,
- => false,
-
- .float_16 => @rem(self.cast(Payload.Float_16).?.val, 1) != 0,
- .float_32 => @rem(self.cast(Payload.Float_32).?.val, 1) != 0,
- .float_64 => @rem(self.cast(Payload.Float_64).?.val, 1) != 0,
- // .float_128 => @rem(self.cast(Payload.Float_128).?.val, 1) != 0,
- .float_128 => @panic("TODO lld: error: undefined symbol: fmodl"),
- };
- }
-
- pub fn orderAgainstZero(lhs: Value) std.math.Order {
- return switch (lhs.tag()) {
- .ty,
- .int_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .anyframe_type,
- .null_value,
- .function,
- .variable,
- .ref_val,
- .decl_ref,
- .elem_ptr,
- .bytes,
- .repeated,
- .undef,
- .void_value,
- .unreachable_value,
- .empty_array,
- .enum_literal,
- .error_set,
- .@"error",
- => unreachable,
-
- .zero,
- .bool_false,
- => .eq,
-
- .one,
- .bool_true,
- => .gt,
-
- .int_u64 => std.math.order(lhs.cast(Payload.Int_u64).?.int, 0),
- .int_i64 => std.math.order(lhs.cast(Payload.Int_i64).?.int, 0),
- .int_big_positive => lhs.cast(Payload.IntBigPositive).?.asBigInt().orderAgainstScalar(0),
- .int_big_negative => lhs.cast(Payload.IntBigNegative).?.asBigInt().orderAgainstScalar(0),
-
- .float_16 => std.math.order(lhs.cast(Payload.Float_16).?.val, 0),
- .float_32 => std.math.order(lhs.cast(Payload.Float_32).?.val, 0),
- .float_64 => std.math.order(lhs.cast(Payload.Float_64).?.val, 0),
- .float_128 => std.math.order(lhs.cast(Payload.Float_128).?.val, 0),
- };
- }
-
- /// Asserts the value is comparable.
- pub fn order(lhs: Value, rhs: Value) std.math.Order {
- const lhs_tag = lhs.tag();
- const rhs_tag = rhs.tag();
- const lhs_is_zero = lhs_tag == .zero;
- const rhs_is_zero = rhs_tag == .zero;
- if (lhs_is_zero) return rhs.orderAgainstZero().invert();
- if (rhs_is_zero) return lhs.orderAgainstZero();
-
- const lhs_float = lhs.isFloat();
- const rhs_float = rhs.isFloat();
- if (lhs_float and rhs_float) {
- if (lhs_tag == rhs_tag) {
- return switch (lhs.tag()) {
- .float_16 => return std.math.order(lhs.cast(Payload.Float_16).?.val, rhs.cast(Payload.Float_16).?.val),
- .float_32 => return std.math.order(lhs.cast(Payload.Float_32).?.val, rhs.cast(Payload.Float_32).?.val),
- .float_64 => return std.math.order(lhs.cast(Payload.Float_64).?.val, rhs.cast(Payload.Float_64).?.val),
- .float_128 => return std.math.order(lhs.cast(Payload.Float_128).?.val, rhs.cast(Payload.Float_128).?.val),
- else => unreachable,
- };
- }
- }
- if (lhs_float or rhs_float) {
- const lhs_f128 = lhs.toFloat(f128);
- const rhs_f128 = rhs.toFloat(f128);
- return std.math.order(lhs_f128, rhs_f128);
- }
-
- var lhs_bigint_space: BigIntSpace = undefined;
- var rhs_bigint_space: BigIntSpace = undefined;
- const lhs_bigint = lhs.toBigInt(&lhs_bigint_space);
- const rhs_bigint = rhs.toBigInt(&rhs_bigint_space);
- return lhs_bigint.order(rhs_bigint);
- }
-
- /// Asserts the value is comparable.
- pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value) bool {
- return order(lhs, rhs).compare(op);
- }
-
- /// Asserts the value is comparable.
- pub fn compareWithZero(lhs: Value, op: std.math.CompareOperator) bool {
- return orderAgainstZero(lhs).compare(op);
- }
-
- pub fn eql(a: Value, b: Value) bool {
- if (a.tag() == b.tag() and a.tag() == .enum_literal) {
- const a_name = @fieldParentPtr(Payload.Bytes, "base", a.ptr_otherwise).data;
- const b_name = @fieldParentPtr(Payload.Bytes, "base", b.ptr_otherwise).data;
- return std.mem.eql(u8, a_name, b_name);
- }
- // TODO non numerical comparisons
- return compare(a, .eq, b);
- }
-
- /// Asserts the value is a pointer and dereferences it.
- /// Returns error.AnalysisFail if the pointer points to a Decl that failed semantic analysis.
- pub fn pointerDeref(self: Value, allocator: *Allocator) error{ AnalysisFail, OutOfMemory }!Value {
- return switch (self.tag()) {
- .ty,
- .int_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .anyframe_type,
- .zero,
- .one,
- .bool_true,
- .bool_false,
- .null_value,
- .function,
- .variable,
- .int_u64,
- .int_i64,
- .int_big_positive,
- .int_big_negative,
- .bytes,
- .undef,
- .repeated,
- .float_16,
- .float_32,
- .float_64,
- .float_128,
- .void_value,
- .unreachable_value,
- .empty_array,
- .enum_literal,
- .error_set,
- .@"error",
- => unreachable,
-
- .ref_val => self.cast(Payload.RefVal).?.val,
- .decl_ref => self.cast(Payload.DeclRef).?.decl.value(),
- .elem_ptr => {
- const elem_ptr = self.cast(Payload.ElemPtr).?;
- const array_val = try elem_ptr.array_ptr.pointerDeref(allocator);
- return array_val.elemValue(allocator, elem_ptr.index);
- },
- };
- }
-
- /// Asserts the value is a single-item pointer to an array, or an array,
- /// or an unknown-length pointer, and returns the element value at the index.
- pub fn elemValue(self: Value, allocator: *Allocator, index: usize) error{OutOfMemory}!Value {
- switch (self.tag()) {
- .ty,
- .int_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .anyframe_type,
- .zero,
- .one,
- .bool_true,
- .bool_false,
- .null_value,
- .function,
- .variable,
- .int_u64,
- .int_i64,
- .int_big_positive,
- .int_big_negative,
- .undef,
- .elem_ptr,
- .ref_val,
- .decl_ref,
- .float_16,
- .float_32,
- .float_64,
- .float_128,
- .void_value,
- .unreachable_value,
- .enum_literal,
- .error_set,
- .@"error",
- => unreachable,
-
- .empty_array => unreachable, // out of bounds array index
-
- .bytes => {
- const int_payload = try allocator.create(Payload.Int_u64);
- int_payload.* = .{ .int = self.cast(Payload.Bytes).?.data[index] };
- return Value.initPayload(&int_payload.base);
- },
-
- // No matter the index; all the elements are the same!
- .repeated => return self.cast(Payload.Repeated).?.val,
- }
- }
-
- /// Returns a pointer to the element value at the index.
- pub fn elemPtr(self: Value, allocator: *Allocator, index: usize) !Value {
- const payload = try allocator.create(Payload.ElemPtr);
- if (self.cast(Payload.ElemPtr)) |elem_ptr| {
- payload.* = .{ .array_ptr = elem_ptr.array_ptr, .index = elem_ptr.index + index };
- } else {
- payload.* = .{ .array_ptr = self, .index = index };
- }
- return Value.initPayload(&payload.base);
- }
-
- pub fn isUndef(self: Value) bool {
- return self.tag() == .undef;
- }
-
- /// Valid for all types. Asserts the value is not undefined and not unreachable.
- pub fn isNull(self: Value) bool {
- return switch (self.tag()) {
- .ty,
- .int_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .anyframe_type,
- .zero,
- .one,
- .empty_array,
- .bool_true,
- .bool_false,
- .function,
- .variable,
- .int_u64,
- .int_i64,
- .int_big_positive,
- .int_big_negative,
- .ref_val,
- .decl_ref,
- .elem_ptr,
- .bytes,
- .repeated,
- .float_16,
- .float_32,
- .float_64,
- .float_128,
- .void_value,
- .enum_literal,
- .error_set,
- .@"error",
- => false,
-
- .undef => unreachable,
- .unreachable_value => unreachable,
- .null_value => true,
- };
- }
-
- /// Valid for all types. Asserts the value is not undefined.
- pub fn isFloat(self: Value) bool {
- return switch (self.tag()) {
- .undef => unreachable,
-
- .float_16,
- .float_32,
- .float_64,
- .float_128,
- => true,
- else => false,
- };
- }
-
- /// This type is not copyable since it may contain pointers to its inner data.
- pub const Payload = struct {
- tag: Tag,
-
- pub const Int_u64 = struct {
- base: Payload = Payload{ .tag = .int_u64 },
- int: u64,
- };
-
- pub const Int_i64 = struct {
- base: Payload = Payload{ .tag = .int_i64 },
- int: i64,
- };
-
- pub const IntBigPositive = struct {
- base: Payload = Payload{ .tag = .int_big_positive },
- limbs: []const std.math.big.Limb,
-
- pub fn asBigInt(self: IntBigPositive) BigIntConst {
- return BigIntConst{ .limbs = self.limbs, .positive = true };
- }
- };
-
- pub const IntBigNegative = struct {
- base: Payload = Payload{ .tag = .int_big_negative },
- limbs: []const std.math.big.Limb,
-
- pub fn asBigInt(self: IntBigNegative) BigIntConst {
- return BigIntConst{ .limbs = self.limbs, .positive = false };
- }
- };
-
- pub const Function = struct {
- base: Payload = Payload{ .tag = .function },
- func: *Module.Fn,
- };
-
- pub const Variable = struct {
- base: Payload = Payload{ .tag = .variable },
- variable: *Module.Var,
- };
-
- pub const ArraySentinel0_u8_Type = struct {
- base: Payload = Payload{ .tag = .array_sentinel_0_u8_type },
- len: u64,
- };
-
- /// Represents a pointer to another immutable value.
- pub const RefVal = struct {
- base: Payload = Payload{ .tag = .ref_val },
- val: Value,
- };
-
- /// Represents a pointer to a decl, not the value of the decl.
- pub const DeclRef = struct {
- base: Payload = Payload{ .tag = .decl_ref },
- decl: *Module.Decl,
- };
-
- pub const ElemPtr = struct {
- base: Payload = Payload{ .tag = .elem_ptr },
- array_ptr: Value,
- index: usize,
- };
-
- pub const Bytes = struct {
- base: Payload = Payload{ .tag = .bytes },
- data: []const u8,
- };
-
- pub const Ty = struct {
- base: Payload = Payload{ .tag = .ty },
- ty: Type,
- };
-
- pub const IntType = struct {
- base: Payload = Payload{ .tag = .int_type },
- bits: u16,
- signed: bool,
- };
-
- pub const Repeated = struct {
- base: Payload = Payload{ .tag = .ty },
- /// This value is repeated some number of times. The amount of times to repeat
- /// is stored externally.
- val: Value,
- };
-
- pub const Float_16 = struct {
- base: Payload = .{ .tag = .float_16 },
- val: f16,
- };
-
- pub const Float_32 = struct {
- base: Payload = .{ .tag = .float_32 },
- val: f32,
- };
-
- pub const Float_64 = struct {
- base: Payload = .{ .tag = .float_64 },
- val: f64,
- };
-
- pub const Float_128 = struct {
- base: Payload = .{ .tag = .float_128 },
- val: f128,
- };
-
- pub const ErrorSet = struct {
- base: Payload = .{ .tag = .error_set },
-
- // TODO revisit this when we have the concept of the error tag type
- fields: std.StringHashMapUnmanaged(u16),
- decl: *Module.Decl,
- };
-
- pub const Error = struct {
- base: Payload = .{ .tag = .@"error" },
-
- // TODO revisit this when we have the concept of the error tag type
- /// `name` is owned by `Module` and will be valid for the entire
- /// duration of the compilation.
- name: []const u8,
- value: u16,
- };
- };
-
- /// Big enough to fit any non-BigInt value
- pub const BigIntSpace = struct {
- /// The +1 is headroom so that operations such as incrementing once or decrementing once
- /// are possible without using an allocator.
- limbs: [(@sizeOf(u64) / @sizeOf(std.math.big.Limb)) + 1]std.math.big.Limb,
- };
-};
diff --git a/src-self-hosted/windows_sdk.zig b/src-self-hosted/windows_sdk.zig
deleted file mode 100644
index 6dfdeb99fd0c2d0ba5f6e7934562a3a7b047bf32..0000000000000000000000000000000000000000
--- a/src-self-hosted/windows_sdk.zig
+++ /dev/null
@@ -1,22 +0,0 @@
-// C API bindings for src/windows_sdk.h
-
-pub const ZigWindowsSDK = extern struct {
- path10_ptr: ?[*]const u8,
- path10_len: usize,
- version10_ptr: ?[*]const u8,
- version10_len: usize,
- path81_ptr: ?[*]const u8,
- path81_len: usize,
- version81_ptr: ?[*]const u8,
- version81_len: usize,
- msvc_lib_dir_ptr: ?[*]const u8,
- msvc_lib_dir_len: usize,
-};
-pub const ZigFindWindowsSdkError = extern enum {
- None,
- OutOfMemory,
- NotFound,
- PathTooLong,
-};
-pub extern fn zig_find_windows_sdk(out_sdk: **ZigWindowsSDK) ZigFindWindowsSdkError;
-pub extern fn zig_free_windows_sdk(sdk: *ZigWindowsSDK) void;
diff --git a/src-self-hosted/zir.zig b/src-self-hosted/zir.zig
deleted file mode 100644
index 7e723fc6740b791d6801bdfb88a5014d51bc0dcf..0000000000000000000000000000000000000000
--- a/src-self-hosted/zir.zig
+++ /dev/null
@@ -1,2701 +0,0 @@
-//! This file has to do with parsing and rendering the ZIR text format.
-
-const std = @import("std");
-const mem = std.mem;
-const Allocator = std.mem.Allocator;
-const assert = std.debug.assert;
-const BigIntConst = std.math.big.int.Const;
-const BigIntMutable = std.math.big.int.Mutable;
-const Type = @import("type.zig").Type;
-const Value = @import("value.zig").Value;
-const TypedValue = @import("TypedValue.zig");
-const ir = @import("ir.zig");
-const IrModule = @import("Module.zig");
-
-/// This struct is relevent only for the ZIR Module text format. It is not used for
-/// semantic analysis of Zig source code.
-pub const Decl = struct {
- name: []const u8,
-
- /// Hash of slice into the source of the part after the = and before the next instruction.
- contents_hash: std.zig.SrcHash,
-
- inst: *Inst,
-};
-
-/// These are instructions that correspond to the ZIR text format. See `ir.Inst` for
-/// in-memory, analyzed instructions with types and values.
-pub const Inst = struct {
- tag: Tag,
- /// Byte offset into the source.
- src: usize,
- /// Pre-allocated field for mapping ZIR text instructions to post-analysis instructions.
- analyzed_inst: ?*ir.Inst = null,
-
- /// These names are used directly as the instruction names in the text format.
- pub const Tag = enum {
- /// Arithmetic addition, asserts no integer overflow.
- add,
- /// Twos complement wrapping integer addition.
- addwrap,
- /// Allocates stack local memory. Its lifetime ends when the block ends that contains
- /// this instruction. The operand is the type of the allocated object.
- alloc,
- /// Same as `alloc` except the type is inferred.
- alloc_inferred,
- /// Create an `anyframe->T`.
- anyframe_type,
- /// Array concatenation. `a ++ b`
- array_cat,
- /// Array multiplication `a ** b`
- array_mul,
- /// Create an array type
- array_type,
- /// Create an array type with sentinel
- array_type_sentinel,
- /// Function parameter value. These must be first in a function's main block,
- /// in respective order with the parameters.
- arg,
- /// Type coercion.
- as,
- /// Inline assembly.
- @"asm",
- /// Bitwise AND. `&`
- bitand,
- /// TODO delete this instruction, it has no purpose.
- bitcast,
- /// An arbitrary typed pointer is pointer-casted to a new Pointer.
- /// The destination type is given by LHS. The cast is to be evaluated
- /// as if it were a bit-cast operation from the operand pointer element type to the
- /// provided destination type.
- bitcast_ref,
- /// A typed result location pointer is bitcasted to a new result location pointer.
- /// The new result location pointer has an inferred type.
- bitcast_result_ptr,
- /// Bitwise NOT. `~`
- bitnot,
- /// Bitwise OR. `|`
- bitor,
- /// A labeled block of code, which can return a value.
- block,
- /// A block of code, which can return a value. There are no instructions that break out of
- /// this block; it is implied that the final instruction is the result.
- block_flat,
- /// Same as `block` but additionally makes the inner instructions execute at comptime.
- block_comptime,
- /// Same as `block_flat` but additionally makes the inner instructions execute at comptime.
- block_comptime_flat,
- /// Boolean NOT. See also `bitnot`.
- boolnot,
- /// Return a value from a `Block`.
- @"break",
- breakpoint,
- /// Same as `break` but without an operand; the operand is assumed to be the void value.
- breakvoid,
- /// Function call.
- call,
- /// `<`
- cmp_lt,
- /// `<=`
- cmp_lte,
- /// `==`
- cmp_eq,
- /// `>=`
- cmp_gte,
- /// `>`
- cmp_gt,
- /// `!=`
- cmp_neq,
- /// Coerces a result location pointer to a new element type. It is evaluated "backwards"-
- /// as type coercion from the new element type to the old element type.
- /// LHS is destination element type, RHS is result pointer.
- coerce_result_ptr,
- /// This instruction does a `coerce_result_ptr` operation on a `Block`'s
- /// result location pointer, whose type is inferred by peer type resolution on the
- /// `Block`'s corresponding `break` instructions.
- coerce_result_block_ptr,
- /// Equivalent to `as(ptr_child_type(typeof(ptr)), value)`.
- coerce_to_ptr_elem,
- /// Emit an error message and fail compilation.
- compileerror,
- /// Conditional branch. Splits control flow based on a boolean condition value.
- condbr,
- /// Special case, has no textual representation.
- @"const",
- /// Declares the beginning of a statement. Used for debug info.
- dbg_stmt,
- /// Represents a pointer to a global decl by name.
- declref,
- /// Represents a pointer to a global decl by string name.
- declref_str,
- /// The syntax `@foo` is equivalent to `declval("foo")`.
- /// declval is equivalent to declref followed by deref.
- declval,
- /// Same as declval but the parameter is a `*Module.Decl` rather than a name.
- declval_in_module,
- /// Load the value from a pointer.
- deref,
- /// Arithmetic division. Asserts no integer overflow.
- div,
- /// Given a pointer to an array, slice, or pointer, returns a pointer to the element at
- /// the provided index.
- elemptr,
- /// Emits a compile error if the operand is not `void`.
- ensure_result_used,
- /// Emits a compile error if an error is ignored.
- ensure_result_non_error,
- /// Emits a compile error if operand cannot be indexed.
- ensure_indexable,
- /// Create a `E!T` type.
- error_union_type,
- /// Create an error set.
- error_set,
- /// Export the provided Decl as the provided name in the compilation's output object file.
- @"export",
- /// Given a pointer to a struct or object that contains virtual fields, returns a pointer
- /// to the named field.
- fieldptr,
- /// Convert a larger float type to any other float type, possibly causing a loss of precision.
- floatcast,
- /// Declare a function body.
- @"fn",
- /// Returns a function type.
- fntype,
- /// Integer literal.
- int,
- /// Convert an integer value to another integer type, asserting that the destination type
- /// can hold the same mathematical value.
- intcast,
- /// Make an integer type out of signedness and bit count.
- inttype,
- /// Return a boolean false if an optional is null. `x != null`
- isnonnull,
- /// Return a boolean true if an optional is null. `x == null`
- isnull,
- /// Return a boolean true if value is an error
- iserr,
- /// A labeled block of code that loops forever. At the end of the body it is implied
- /// to repeat; no explicit "repeat" instruction terminates loop bodies.
- loop,
- /// Merge two error sets into one, `E1 || E2`.
- merge_error_sets,
- /// Ambiguously remainder division or modulus. If the computation would possibly have
- /// a different value depending on whether the operation is remainder division or modulus,
- /// a compile error is emitted. Otherwise the computation is performed.
- mod_rem,
- /// Arithmetic multiplication. Asserts no integer overflow.
- mul,
- /// Twos complement wrapping integer multiplication.
- mulwrap,
- /// Given a reference to a function and a parameter index, returns the
- /// type of the parameter. TODO what happens when the parameter is `anytype`?
- param_type,
- /// An alternative to using `const` for simple primitive values such as `true` or `u8`.
- /// TODO flatten so that each primitive has its own ZIR Inst Tag.
- primitive,
- /// Convert a pointer to a `usize` integer.
- ptrtoint,
- /// Turns an R-Value into a const L-Value. In other words, it takes a value,
- /// stores it in a memory location, and returns a const pointer to it. If the value
- /// is `comptime`, the memory location is global static constant data. Otherwise,
- /// the memory location is in the stack frame, local to the scope containing the
- /// instruction.
- ref,
- /// Obtains a pointer to the return value.
- ret_ptr,
- /// Obtains the return type of the in-scope function.
- ret_type,
- /// Sends control flow back to the function's callee. Takes an operand as the return value.
- @"return",
- /// Same as `return` but there is no operand; the operand is implicitly the void value.
- returnvoid,
- /// Integer shift-left. Zeroes are shifted in from the right hand side.
- shl,
- /// Integer shift-right. Arithmetic or logical depending on the signedness of the integer type.
- shr,
- /// Create a const pointer type with element type T. `*const T`
- single_const_ptr_type,
- /// Create a mutable pointer type with element type T. `*T`
- single_mut_ptr_type,
- /// Create a const pointer type with element type T. `[*]const T`
- many_const_ptr_type,
- /// Create a mutable pointer type with element type T. `[*]T`
- many_mut_ptr_type,
- /// Create a const pointer type with element type T. `[*c]const T`
- c_const_ptr_type,
- /// Create a mutable pointer type with element type T. `[*c]T`
- c_mut_ptr_type,
- /// Create a mutable slice type with element type T. `[]T`
- mut_slice_type,
- /// Create a const slice type with element type T. `[]T`
- const_slice_type,
- /// Create a pointer type with attributes
- ptr_type,
- /// Slice operation `array_ptr[start..end:sentinel]`
- slice,
- /// Slice operation with just start `lhs[rhs..]`
- slice_start,
- /// Write a value to a pointer. For loading, see `deref`.
- store,
- /// String Literal. Makes an anonymous Decl and then takes a pointer to it.
- str,
- /// Arithmetic subtraction. Asserts no integer overflow.
- sub,
- /// Twos complement wrapping integer subtraction.
- subwrap,
- /// Returns the type of a value.
- typeof,
- /// Asserts control-flow will not reach this instruction. Not safety checked - the compiler
- /// will assume the correctness of this instruction.
- unreach_nocheck,
- /// Asserts control-flow will not reach this instruction. In safety-checked modes,
- /// this will generate a call to the panic function unless it can be proven unreachable
- /// by the compiler.
- @"unreachable",
- /// Bitwise XOR. `^`
- xor,
- /// Create an optional type '?T'
- optional_type,
- /// Unwraps an optional value 'lhs.?'
- unwrap_optional_safe,
- /// Same as previous, but without safety checks. Used for orelse, if and while
- unwrap_optional_unsafe,
- /// Gets the payload of an error union
- unwrap_err_safe,
- /// Same as previous, but without safety checks. Used for orelse, if and while
- unwrap_err_unsafe,
- /// Gets the error code value of an error union
- unwrap_err_code,
- /// Takes a *E!T and raises a compiler error if T != void
- ensure_err_payload_void,
- /// Enum literal
- enum_literal,
-
- pub fn Type(tag: Tag) type {
- return switch (tag) {
- .breakpoint,
- .dbg_stmt,
- .returnvoid,
- .alloc_inferred,
- .ret_ptr,
- .ret_type,
- .unreach_nocheck,
- .@"unreachable",
- => NoOp,
-
- .boolnot,
- .deref,
- .@"return",
- .isnull,
- .isnonnull,
- .iserr,
- .ptrtoint,
- .alloc,
- .ensure_result_used,
- .ensure_result_non_error,
- .ensure_indexable,
- .bitcast_result_ptr,
- .ref,
- .bitcast_ref,
- .typeof,
- .single_const_ptr_type,
- .single_mut_ptr_type,
- .many_const_ptr_type,
- .many_mut_ptr_type,
- .c_const_ptr_type,
- .c_mut_ptr_type,
- .mut_slice_type,
- .const_slice_type,
- .optional_type,
- .unwrap_optional_safe,
- .unwrap_optional_unsafe,
- .unwrap_err_safe,
- .unwrap_err_unsafe,
- .unwrap_err_code,
- .ensure_err_payload_void,
- .anyframe_type,
- .bitnot,
- => UnOp,
-
- .add,
- .addwrap,
- .array_cat,
- .array_mul,
- .array_type,
- .bitand,
- .bitor,
- .div,
- .mod_rem,
- .mul,
- .mulwrap,
- .shl,
- .shr,
- .store,
- .sub,
- .subwrap,
- .cmp_lt,
- .cmp_lte,
- .cmp_eq,
- .cmp_gte,
- .cmp_gt,
- .cmp_neq,
- .as,
- .floatcast,
- .intcast,
- .bitcast,
- .coerce_result_ptr,
- .xor,
- .error_union_type,
- .merge_error_sets,
- .slice_start,
- => BinOp,
-
- .block,
- .block_flat,
- .block_comptime,
- .block_comptime_flat,
- => Block,
-
- .arg => Arg,
- .array_type_sentinel => ArrayTypeSentinel,
- .@"break" => Break,
- .breakvoid => BreakVoid,
- .call => Call,
- .coerce_to_ptr_elem => CoerceToPtrElem,
- .declref => DeclRef,
- .declref_str => DeclRefStr,
- .declval => DeclVal,
- .declval_in_module => DeclValInModule,
- .coerce_result_block_ptr => CoerceResultBlockPtr,
- .compileerror => CompileError,
- .loop => Loop,
- .@"const" => Const,
- .str => Str,
- .int => Int,
- .inttype => IntType,
- .fieldptr => FieldPtr,
- .@"asm" => Asm,
- .@"fn" => Fn,
- .@"export" => Export,
- .param_type => ParamType,
- .primitive => Primitive,
- .fntype => FnType,
- .elemptr => ElemPtr,
- .condbr => CondBr,
- .ptr_type => PtrType,
- .enum_literal => EnumLiteral,
- .error_set => ErrorSet,
- .slice => Slice,
- };
- }
-
- /// Returns whether the instruction is one of the control flow "noreturn" types.
- /// Function calls do not count.
- pub fn isNoReturn(tag: Tag) bool {
- return switch (tag) {
- .add,
- .addwrap,
- .alloc,
- .alloc_inferred,
- .array_cat,
- .array_mul,
- .array_type,
- .array_type_sentinel,
- .arg,
- .as,
- .@"asm",
- .bitand,
- .bitcast,
- .bitcast_ref,
- .bitcast_result_ptr,
- .bitor,
- .block,
- .block_flat,
- .block_comptime,
- .block_comptime_flat,
- .boolnot,
- .breakpoint,
- .call,
- .cmp_lt,
- .cmp_lte,
- .cmp_eq,
- .cmp_gte,
- .cmp_gt,
- .cmp_neq,
- .coerce_result_ptr,
- .coerce_result_block_ptr,
- .coerce_to_ptr_elem,
- .@"const",
- .dbg_stmt,
- .declref,
- .declref_str,
- .declval,
- .declval_in_module,
- .deref,
- .div,
- .elemptr,
- .ensure_result_used,
- .ensure_result_non_error,
- .ensure_indexable,
- .@"export",
- .floatcast,
- .fieldptr,
- .@"fn",
- .fntype,
- .int,
- .intcast,
- .inttype,
- .isnonnull,
- .isnull,
- .iserr,
- .mod_rem,
- .mul,
- .mulwrap,
- .param_type,
- .primitive,
- .ptrtoint,
- .ref,
- .ret_ptr,
- .ret_type,
- .shl,
- .shr,
- .single_const_ptr_type,
- .single_mut_ptr_type,
- .many_const_ptr_type,
- .many_mut_ptr_type,
- .c_const_ptr_type,
- .c_mut_ptr_type,
- .mut_slice_type,
- .const_slice_type,
- .store,
- .str,
- .sub,
- .subwrap,
- .typeof,
- .xor,
- .optional_type,
- .unwrap_optional_safe,
- .unwrap_optional_unsafe,
- .unwrap_err_safe,
- .unwrap_err_unsafe,
- .unwrap_err_code,
- .ptr_type,
- .ensure_err_payload_void,
- .enum_literal,
- .merge_error_sets,
- .anyframe_type,
- .error_union_type,
- .bitnot,
- .error_set,
- .slice,
- .slice_start,
- => false,
-
- .@"break",
- .breakvoid,
- .condbr,
- .compileerror,
- .@"return",
- .returnvoid,
- .unreach_nocheck,
- .@"unreachable",
- .loop,
- => true,
- };
- }
- };
-
- /// Prefer `castTag` to this.
- pub fn cast(base: *Inst, comptime T: type) ?*T {
- if (@hasField(T, "base_tag")) {
- return base.castTag(T.base_tag);
- }
- inline for (@typeInfo(Tag).Enum.fields) |field| {
- const tag = @intToEnum(Tag, field.value);
- if (base.tag == tag) {
- if (T == tag.Type()) {
- return @fieldParentPtr(T, "base", base);
- }
- return null;
- }
- }
- unreachable;
- }
-
- pub fn castTag(base: *Inst, comptime tag: Tag) ?*tag.Type() {
- if (base.tag == tag) {
- return @fieldParentPtr(tag.Type(), "base", base);
- }
- return null;
- }
-
- pub const NoOp = struct {
- base: Inst,
-
- positionals: struct {},
- kw_args: struct {},
- };
-
- pub const UnOp = struct {
- base: Inst,
-
- positionals: struct {
- operand: *Inst,
- },
- kw_args: struct {},
- };
-
- pub const BinOp = struct {
- base: Inst,
-
- positionals: struct {
- lhs: *Inst,
- rhs: *Inst,
- },
- kw_args: struct {},
- };
-
- pub const Arg = struct {
- pub const base_tag = Tag.arg;
- base: Inst,
-
- positionals: struct {
- name: []const u8,
- },
- kw_args: struct {},
- };
-
- pub const Block = struct {
- pub const base_tag = Tag.block;
- base: Inst,
-
- positionals: struct {
- body: Module.Body,
- },
- kw_args: struct {},
- };
-
- pub const Break = struct {
- pub const base_tag = Tag.@"break";
- base: Inst,
-
- positionals: struct {
- block: *Block,
- operand: *Inst,
- },
- kw_args: struct {},
- };
-
- pub const BreakVoid = struct {
- pub const base_tag = Tag.breakvoid;
- base: Inst,
-
- positionals: struct {
- block: *Block,
- },
- kw_args: struct {},
- };
-
- pub const Call = struct {
- pub const base_tag = Tag.call;
- base: Inst,
-
- positionals: struct {
- func: *Inst,
- args: []*Inst,
- },
- kw_args: struct {
- modifier: std.builtin.CallOptions.Modifier = .auto,
- },
- };
-
- pub const CoerceToPtrElem = struct {
- pub const base_tag = Tag.coerce_to_ptr_elem;
- base: Inst,
-
- positionals: struct {
- ptr: *Inst,
- value: *Inst,
- },
- kw_args: struct {},
- };
-
- pub const DeclRef = struct {
- pub const base_tag = Tag.declref;
- base: Inst,
-
- positionals: struct {
- name: []const u8,
- },
- kw_args: struct {},
- };
-
- pub const DeclRefStr = struct {
- pub const base_tag = Tag.declref_str;
- base: Inst,
-
- positionals: struct {
- name: *Inst,
- },
- kw_args: struct {},
- };
-
- pub const DeclVal = struct {
- pub const base_tag = Tag.declval;
- base: Inst,
-
- positionals: struct {
- name: []const u8,
- },
- kw_args: struct {},
- };
-
- pub const DeclValInModule = struct {
- pub const base_tag = Tag.declval_in_module;
- base: Inst,
-
- positionals: struct {
- decl: *IrModule.Decl,
- },
- kw_args: struct {},
- };
-
- pub const CoerceResultBlockPtr = struct {
- pub const base_tag = Tag.coerce_result_block_ptr;
- base: Inst,
-
- positionals: struct {
- dest_type: *Inst,
- block: *Block,
- },
- kw_args: struct {},
- };
-
- pub const CompileError = struct {
- pub const base_tag = Tag.compileerror;
- base: Inst,
-
- positionals: struct {
- msg: []const u8,
- },
- kw_args: struct {},
- };
-
- pub const Const = struct {
- pub const base_tag = Tag.@"const";
- base: Inst,
-
- positionals: struct {
- typed_value: TypedValue,
- },
- kw_args: struct {},
- };
-
- pub const Str = struct {
- pub const base_tag = Tag.str;
- base: Inst,
-
- positionals: struct {
- bytes: []const u8,
- },
- kw_args: struct {},
- };
-
- pub const Int = struct {
- pub const base_tag = Tag.int;
- base: Inst,
-
- positionals: struct {
- int: BigIntConst,
- },
- kw_args: struct {},
- };
-
- pub const Loop = struct {
- pub const base_tag = Tag.loop;
- base: Inst,
-
- positionals: struct {
- body: Module.Body,
- },
- kw_args: struct {},
- };
-
- pub const FieldPtr = struct {
- pub const base_tag = Tag.fieldptr;
- base: Inst,
-
- positionals: struct {
- object_ptr: *Inst,
- field_name: *Inst,
- },
- kw_args: struct {},
- };
-
- pub const Asm = struct {
- pub const base_tag = Tag.@"asm";
- base: Inst,
-
- positionals: struct {
- asm_source: *Inst,
- return_type: *Inst,
- },
- kw_args: struct {
- @"volatile": bool = false,
- output: ?*Inst = null,
- inputs: []*Inst = &[0]*Inst{},
- clobbers: []*Inst = &[0]*Inst{},
- args: []*Inst = &[0]*Inst{},
- },
- };
-
- pub const Fn = struct {
- pub const base_tag = Tag.@"fn";
- base: Inst,
-
- positionals: struct {
- fn_type: *Inst,
- body: Module.Body,
- },
- kw_args: struct {},
- };
-
- pub const FnType = struct {
- pub const base_tag = Tag.fntype;
- base: Inst,
-
- positionals: struct {
- param_types: []*Inst,
- return_type: *Inst,
- },
- kw_args: struct {
- cc: std.builtin.CallingConvention = .Unspecified,
- },
- };
-
- pub const IntType = struct {
- pub const base_tag = Tag.inttype;
- base: Inst,
-
- positionals: struct {
- signed: *Inst,
- bits: *Inst,
- },
- kw_args: struct {},
- };
-
- pub const Export = struct {
- pub const base_tag = Tag.@"export";
- base: Inst,
-
- positionals: struct {
- symbol_name: *Inst,
- decl_name: []const u8,
- },
- kw_args: struct {},
- };
-
- pub const ParamType = struct {
- pub const base_tag = Tag.param_type;
- base: Inst,
-
- positionals: struct {
- func: *Inst,
- arg_index: usize,
- },
- kw_args: struct {},
- };
-
- pub const Primitive = struct {
- pub const base_tag = Tag.primitive;
- base: Inst,
-
- positionals: struct {
- tag: Builtin,
- },
- kw_args: struct {},
-
- pub const Builtin = enum {
- i8,
- u8,
- i16,
- u16,
- i32,
- u32,
- i64,
- u64,
- isize,
- usize,
- c_short,
- c_ushort,
- c_int,
- c_uint,
- c_long,
- c_ulong,
- c_longlong,
- c_ulonglong,
- c_longdouble,
- c_void,
- f16,
- f32,
- f64,
- f128,
- bool,
- void,
- noreturn,
- type,
- anyerror,
- comptime_int,
- comptime_float,
- @"true",
- @"false",
- @"null",
- @"undefined",
- void_value,
-
- pub fn toTypedValue(self: Builtin) TypedValue {
- return switch (self) {
- .i8 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.i8_type) },
- .u8 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.u8_type) },
- .i16 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.i16_type) },
- .u16 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.u16_type) },
- .i32 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.i32_type) },
- .u32 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.u32_type) },
- .i64 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.i64_type) },
- .u64 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.u64_type) },
- .isize => .{ .ty = Type.initTag(.type), .val = Value.initTag(.isize_type) },
- .usize => .{ .ty = Type.initTag(.type), .val = Value.initTag(.usize_type) },
- .c_short => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_short_type) },
- .c_ushort => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_ushort_type) },
- .c_int => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_int_type) },
- .c_uint => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_uint_type) },
- .c_long => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_long_type) },
- .c_ulong => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_ulong_type) },
- .c_longlong => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_longlong_type) },
- .c_ulonglong => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_ulonglong_type) },
- .c_longdouble => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_longdouble_type) },
- .c_void => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_void_type) },
- .f16 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.f16_type) },
- .f32 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.f32_type) },
- .f64 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.f64_type) },
- .f128 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.f128_type) },
- .bool => .{ .ty = Type.initTag(.type), .val = Value.initTag(.bool_type) },
- .void => .{ .ty = Type.initTag(.type), .val = Value.initTag(.void_type) },
- .noreturn => .{ .ty = Type.initTag(.type), .val = Value.initTag(.noreturn_type) },
- .type => .{ .ty = Type.initTag(.type), .val = Value.initTag(.type_type) },
- .anyerror => .{ .ty = Type.initTag(.type), .val = Value.initTag(.anyerror_type) },
- .comptime_int => .{ .ty = Type.initTag(.type), .val = Value.initTag(.comptime_int_type) },
- .comptime_float => .{ .ty = Type.initTag(.type), .val = Value.initTag(.comptime_float_type) },
- .@"true" => .{ .ty = Type.initTag(.bool), .val = Value.initTag(.bool_true) },
- .@"false" => .{ .ty = Type.initTag(.bool), .val = Value.initTag(.bool_false) },
- .@"null" => .{ .ty = Type.initTag(.@"null"), .val = Value.initTag(.null_value) },
- .@"undefined" => .{ .ty = Type.initTag(.@"undefined"), .val = Value.initTag(.undef) },
- .void_value => .{ .ty = Type.initTag(.void), .val = Value.initTag(.void_value) },
- };
- }
- };
- };
-
- pub const ElemPtr = struct {
- pub const base_tag = Tag.elemptr;
- base: Inst,
-
- positionals: struct {
- array_ptr: *Inst,
- index: *Inst,
- },
- kw_args: struct {},
- };
-
- pub const CondBr = struct {
- pub const base_tag = Tag.condbr;
- base: Inst,
-
- positionals: struct {
- condition: *Inst,
- then_body: Module.Body,
- else_body: Module.Body,
- },
- kw_args: struct {},
- };
-
- pub const PtrType = struct {
- pub const base_tag = Tag.ptr_type;
- base: Inst,
-
- positionals: struct {
- child_type: *Inst,
- },
- kw_args: struct {
- @"allowzero": bool = false,
- @"align": ?*Inst = null,
- align_bit_start: ?*Inst = null,
- align_bit_end: ?*Inst = null,
- mutable: bool = true,
- @"volatile": bool = false,
- sentinel: ?*Inst = null,
- size: std.builtin.TypeInfo.Pointer.Size = .One,
- },
- };
-
- pub const ArrayTypeSentinel = struct {
- pub const base_tag = Tag.array_type_sentinel;
- base: Inst,
-
- positionals: struct {
- len: *Inst,
- sentinel: *Inst,
- elem_type: *Inst,
- },
- kw_args: struct {},
- };
-
- pub const EnumLiteral = struct {
- pub const base_tag = Tag.enum_literal;
- base: Inst,
-
- positionals: struct {
- name: []const u8,
- },
- kw_args: struct {},
- };
-
- pub const ErrorSet = struct {
- pub const base_tag = Tag.error_set;
- base: Inst,
-
- positionals: struct {
- fields: [][]const u8,
- },
- kw_args: struct {},
- };
-
- pub const Slice = struct {
- pub const base_tag = Tag.slice;
- base: Inst,
-
- positionals: struct {
- array_ptr: *Inst,
- start: *Inst,
- },
- kw_args: struct {
- end: ?*Inst = null,
- sentinel: ?*Inst = null,
- },
- };
-};
-
-pub const ErrorMsg = struct {
- byte_offset: usize,
- msg: []const u8,
-};
-
-pub const Module = struct {
- decls: []*Decl,
- arena: std.heap.ArenaAllocator,
- error_msg: ?ErrorMsg = null,
- metadata: std.AutoHashMap(*Inst, MetaData),
- body_metadata: std.AutoHashMap(*Body, BodyMetaData),
-
- pub const MetaData = struct {
- deaths: ir.Inst.DeathsInt,
- addr: usize,
- };
-
- pub const BodyMetaData = struct {
- deaths: []*Inst,
- };
-
- pub const Body = struct {
- instructions: []*Inst,
- };
-
- pub fn deinit(self: *Module, allocator: *Allocator) void {
- self.metadata.deinit();
- self.body_metadata.deinit();
- allocator.free(self.decls);
- self.arena.deinit();
- self.* = undefined;
- }
-
- /// This is a debugging utility for rendering the tree to stderr.
- pub fn dump(self: Module) void {
- self.writeToStream(std.heap.page_allocator, std.io.getStdErr().outStream()) catch {};
- }
-
- const DeclAndIndex = struct {
- decl: *Decl,
- index: usize,
- };
-
- /// TODO Look into making a table to speed this up.
- pub fn findDecl(self: Module, name: []const u8) ?DeclAndIndex {
- for (self.decls) |decl, i| {
- if (mem.eql(u8, decl.name, name)) {
- return DeclAndIndex{
- .decl = decl,
- .index = i,
- };
- }
- }
- return null;
- }
-
- pub fn findInstDecl(self: Module, inst: *Inst) ?DeclAndIndex {
- for (self.decls) |decl, i| {
- if (decl.inst == inst) {
- return DeclAndIndex{
- .decl = decl,
- .index = i,
- };
- }
- }
- return null;
- }
-
- /// The allocator is used for temporary storage, but this function always returns
- /// with no resources allocated.
- pub fn writeToStream(self: Module, allocator: *Allocator, stream: anytype) !void {
- var write = Writer{
- .module = &self,
- .inst_table = InstPtrTable.init(allocator),
- .block_table = std.AutoHashMap(*Inst.Block, []const u8).init(allocator),
- .loop_table = std.AutoHashMap(*Inst.Loop, []const u8).init(allocator),
- .arena = std.heap.ArenaAllocator.init(allocator),
- .indent = 2,
- .next_instr_index = undefined,
- };
- defer write.arena.deinit();
- defer write.inst_table.deinit();
- defer write.block_table.deinit();
- defer write.loop_table.deinit();
-
- // First, build a map of *Inst to @ or % indexes
- try write.inst_table.ensureCapacity(@intCast(u32, self.decls.len));
-
- for (self.decls) |decl, decl_i| {
- try write.inst_table.putNoClobber(decl.inst, .{ .inst = decl.inst, .index = null, .name = decl.name });
- }
-
- for (self.decls) |decl, i| {
- write.next_instr_index = 0;
- try stream.print("@{} ", .{decl.name});
- try write.writeInstToStream(stream, decl.inst);
- try stream.writeByte('\n');
- }
- }
-};
-
-const InstPtrTable = std.AutoHashMap(*Inst, struct { inst: *Inst, index: ?usize, name: []const u8 });
-
-const Writer = struct {
- module: *const Module,
- inst_table: InstPtrTable,
- block_table: std.AutoHashMap(*Inst.Block, []const u8),
- loop_table: std.AutoHashMap(*Inst.Loop, []const u8),
- arena: std.heap.ArenaAllocator,
- indent: usize,
- next_instr_index: usize,
-
- fn writeInstToStream(
- self: *Writer,
- stream: anytype,
- inst: *Inst,
- ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
- inline for (@typeInfo(Inst.Tag).Enum.fields) |enum_field| {
- const expected_tag = @field(Inst.Tag, enum_field.name);
- if (inst.tag == expected_tag) {
- return self.writeInstToStreamGeneric(stream, expected_tag, inst);
- }
- }
- unreachable; // all tags handled
- }
-
- fn writeInstToStreamGeneric(
- self: *Writer,
- stream: anytype,
- comptime inst_tag: Inst.Tag,
- base: *Inst,
- ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
- const SpecificInst = inst_tag.Type();
- const inst = @fieldParentPtr(SpecificInst, "base", base);
- const Positionals = @TypeOf(inst.positionals);
- try stream.writeAll("= " ++ @tagName(inst_tag) ++ "(");
- const pos_fields = @typeInfo(Positionals).Struct.fields;
- inline for (pos_fields) |arg_field, i| {
- if (i != 0) {
- try stream.writeAll(", ");
- }
- try self.writeParamToStream(stream, &@field(inst.positionals, arg_field.name));
- }
-
- comptime var need_comma = pos_fields.len != 0;
- const KW_Args = @TypeOf(inst.kw_args);
- inline for (@typeInfo(KW_Args).Struct.fields) |arg_field, i| {
- if (@typeInfo(arg_field.field_type) == .Optional) {
- if (@field(inst.kw_args, arg_field.name)) |non_optional| {
- if (need_comma) try stream.writeAll(", ");
- try stream.print("{}=", .{arg_field.name});
- try self.writeParamToStream(stream, &non_optional);
- need_comma = true;
- }
- } else {
- if (need_comma) try stream.writeAll(", ");
- try stream.print("{}=", .{arg_field.name});
- try self.writeParamToStream(stream, &@field(inst.kw_args, arg_field.name));
- need_comma = true;
- }
- }
-
- try stream.writeByte(')');
- }
-
- fn writeParamToStream(self: *Writer, stream: anytype, param_ptr: anytype) !void {
- const param = param_ptr.*;
- if (@typeInfo(@TypeOf(param)) == .Enum) {
- return stream.writeAll(@tagName(param));
- }
- switch (@TypeOf(param)) {
- *Inst => return self.writeInstParamToStream(stream, param),
- []*Inst => {
- try stream.writeByte('[');
- for (param) |inst, i| {
- if (i != 0) {
- try stream.writeAll(", ");
- }
- try self.writeInstParamToStream(stream, inst);
- }
- try stream.writeByte(']');
- },
- Module.Body => {
- try stream.writeAll("{\n");
- if (self.module.body_metadata.get(param_ptr)) |metadata| {
- if (metadata.deaths.len > 0) {
- try stream.writeByteNTimes(' ', self.indent);
- try stream.writeAll("; deaths={");
- for (metadata.deaths) |death, i| {
- if (i != 0) try stream.writeAll(", ");
- try self.writeInstParamToStream(stream, death);
- }
- try stream.writeAll("}\n");
- }
- }
-
- for (param.instructions) |inst| {
- const my_i = self.next_instr_index;
- self.next_instr_index += 1;
- try self.inst_table.putNoClobber(inst, .{ .inst = inst, .index = my_i, .name = undefined });
- try stream.writeByteNTimes(' ', self.indent);
- try stream.print("%{} ", .{my_i});
- if (inst.cast(Inst.Block)) |block| {
- const name = try std.fmt.allocPrint(&self.arena.allocator, "label_{}", .{my_i});
- try self.block_table.put(block, name);
- } else if (inst.cast(Inst.Loop)) |loop| {
- const name = try std.fmt.allocPrint(&self.arena.allocator, "loop_{}", .{my_i});
- try self.loop_table.put(loop, name);
- }
- self.indent += 2;
- try self.writeInstToStream(stream, inst);
- if (self.module.metadata.get(inst)) |metadata| {
- try stream.print(" ; deaths=0b{b}", .{metadata.deaths});
- // This is conditionally compiled in because addresses mess up the tests due
- // to Address Space Layout Randomization. It's super useful when debugging
- // codegen.zig though.
- if (!std.builtin.is_test) {
- try stream.print(" 0x{x}", .{metadata.addr});
- }
- }
- self.indent -= 2;
- try stream.writeByte('\n');
- }
- try stream.writeByteNTimes(' ', self.indent - 2);
- try stream.writeByte('}');
- },
- bool => return stream.writeByte("01"[@boolToInt(param)]),
- []u8, []const u8 => return std.zig.renderStringLiteral(param, stream),
- BigIntConst, usize => return stream.print("{}", .{param}),
- TypedValue => unreachable, // this is a special case
- *IrModule.Decl => unreachable, // this is a special case
- *Inst.Block => {
- const name = self.block_table.get(param).?;
- return std.zig.renderStringLiteral(name, stream);
- },
- *Inst.Loop => {
- const name = self.loop_table.get(param).?;
- return std.zig.renderStringLiteral(name, stream);
- },
- [][]const u8 => {
- try stream.writeByte('[');
- for (param) |str, i| {
- if (i != 0) {
- try stream.writeAll(", ");
- }
- try std.zig.renderStringLiteral(str, stream);
- }
- try stream.writeByte(']');
- },
- else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),
- }
- }
-
- fn writeInstParamToStream(self: *Writer, stream: anytype, inst: *Inst) !void {
- if (self.inst_table.get(inst)) |info| {
- if (info.index) |i| {
- try stream.print("%{}", .{info.index});
- } else {
- try stream.print("@{}", .{info.name});
- }
- } else if (inst.cast(Inst.DeclVal)) |decl_val| {
- try stream.print("@{}", .{decl_val.positionals.name});
- } else if (inst.cast(Inst.DeclValInModule)) |decl_val| {
- try stream.print("@{}", .{decl_val.positionals.decl.name});
- } else {
- // This should be unreachable in theory, but since ZIR is used for debugging the compiler
- // we output some debug text instead.
- try stream.print("?{}?", .{@tagName(inst.tag)});
- }
- }
-};
-
-pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module {
- var global_name_map = std.StringHashMap(*Inst).init(allocator);
- defer global_name_map.deinit();
-
- var parser: Parser = .{
- .allocator = allocator,
- .arena = std.heap.ArenaAllocator.init(allocator),
- .i = 0,
- .source = source,
- .global_name_map = &global_name_map,
- .decls = .{},
- .unnamed_index = 0,
- .block_table = std.StringHashMap(*Inst.Block).init(allocator),
- .loop_table = std.StringHashMap(*Inst.Loop).init(allocator),
- };
- defer parser.block_table.deinit();
- defer parser.loop_table.deinit();
- errdefer parser.arena.deinit();
-
- parser.parseRoot() catch |err| switch (err) {
- error.ParseFailure => {
- assert(parser.error_msg != null);
- },
- else => |e| return e,
- };
-
- return Module{
- .decls = parser.decls.toOwnedSlice(allocator),
- .arena = parser.arena,
- .error_msg = parser.error_msg,
- .metadata = std.AutoHashMap(*Inst, Module.MetaData).init(allocator),
- .body_metadata = std.AutoHashMap(*Module.Body, Module.BodyMetaData).init(allocator),
- };
-}
-
-const Parser = struct {
- allocator: *Allocator,
- arena: std.heap.ArenaAllocator,
- i: usize,
- source: [:0]const u8,
- decls: std.ArrayListUnmanaged(*Decl),
- global_name_map: *std.StringHashMap(*Inst),
- error_msg: ?ErrorMsg = null,
- unnamed_index: usize,
- block_table: std.StringHashMap(*Inst.Block),
- loop_table: std.StringHashMap(*Inst.Loop),
-
- const Body = struct {
- instructions: std.ArrayList(*Inst),
- name_map: *std.StringHashMap(*Inst),
- };
-
- fn parseBody(self: *Parser, body_ctx: ?*Body) !Module.Body {
- var name_map = std.StringHashMap(*Inst).init(self.allocator);
- defer name_map.deinit();
-
- var body_context = Body{
- .instructions = std.ArrayList(*Inst).init(self.allocator),
- .name_map = if (body_ctx) |bctx| bctx.name_map else &name_map,
- };
- defer body_context.instructions.deinit();
-
- try requireEatBytes(self, "{");
- skipSpace(self);
-
- while (true) : (self.i += 1) switch (self.source[self.i]) {
- ';' => _ = try skipToAndOver(self, '\n'),
- '%' => {
- self.i += 1;
- const ident = try skipToAndOver(self, ' ');
- skipSpace(self);
- try requireEatBytes(self, "=");
- skipSpace(self);
- const decl = try parseInstruction(self, &body_context, ident);
- const ident_index = body_context.instructions.items.len;
- if (try body_context.name_map.fetchPut(ident, decl.inst)) |_| {
- return self.fail("redefinition of identifier '{}'", .{ident});
- }
- try body_context.instructions.append(decl.inst);
- continue;
- },
- ' ', '\n' => continue,
- '}' => {
- self.i += 1;
- break;
- },
- else => |byte| return self.failByte(byte),
- };
-
- // Move the instructions to the arena
- const instrs = try self.arena.allocator.alloc(*Inst, body_context.instructions.items.len);
- mem.copy(*Inst, instrs, body_context.instructions.items);
- return Module.Body{ .instructions = instrs };
- }
-
- fn parseStringLiteral(self: *Parser) ![]u8 {
- const start = self.i;
- try self.requireEatBytes("\"");
-
- while (true) : (self.i += 1) switch (self.source[self.i]) {
- '"' => {
- self.i += 1;
- const span = self.source[start..self.i];
- var bad_index: usize = undefined;
- const parsed = std.zig.parseStringLiteral(&self.arena.allocator, span, &bad_index) catch |err| switch (err) {
- error.InvalidCharacter => {
- self.i = start + bad_index;
- const bad_byte = self.source[self.i];
- return self.fail("invalid string literal character: '{c}'\n", .{bad_byte});
- },
- else => |e| return e,
- };
- return parsed;
- },
- '\\' => {
- self.i += 1;
- continue;
- },
- 0 => return self.failByte(0),
- else => continue,
- };
- }
-
- fn parseIntegerLiteral(self: *Parser) !BigIntConst {
- const start = self.i;
- if (self.source[self.i] == '-') self.i += 1;
- while (true) : (self.i += 1) switch (self.source[self.i]) {
- '0'...'9' => continue,
- else => break,
- };
- const number_text = self.source[start..self.i];
- const base = 10;
- // TODO reuse the same array list for this
- const limbs_buffer_len = std.math.big.int.calcSetStringLimbsBufferLen(base, number_text.len);
- const limbs_buffer = try self.allocator.alloc(std.math.big.Limb, limbs_buffer_len);
- defer self.allocator.free(limbs_buffer);
- const limb_len = std.math.big.int.calcSetStringLimbCount(base, number_text.len);
- const limbs = try self.arena.allocator.alloc(std.math.big.Limb, limb_len);
- var result = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
- result.setString(base, number_text, limbs_buffer, self.allocator) catch |err| switch (err) {
- error.InvalidCharacter => {
- self.i = start;
- return self.fail("invalid digit in integer literal", .{});
- },
- };
- return result.toConst();
- }
-
- fn parseRoot(self: *Parser) !void {
- // The IR format is designed so that it can be tokenized and parsed at the same time.
- while (true) {
- switch (self.source[self.i]) {
- ';' => _ = try skipToAndOver(self, '\n'),
- '@' => {
- self.i += 1;
- const ident = try skipToAndOver(self, ' ');
- skipSpace(self);
- try requireEatBytes(self, "=");
- skipSpace(self);
- const decl = try parseInstruction(self, null, ident);
- const ident_index = self.decls.items.len;
- if (try self.global_name_map.fetchPut(ident, decl.inst)) |_| {
- return self.fail("redefinition of identifier '{}'", .{ident});
- }
- try self.decls.append(self.allocator, decl);
- },
- ' ', '\n' => self.i += 1,
- 0 => break,
- else => |byte| return self.fail("unexpected byte: '{c}'", .{byte}),
- }
- }
- }
-
- fn eatByte(self: *Parser, byte: u8) bool {
- if (self.source[self.i] != byte) return false;
- self.i += 1;
- return true;
- }
-
- fn skipSpace(self: *Parser) void {
- while (self.source[self.i] == ' ' or self.source[self.i] == '\n') {
- self.i += 1;
- }
- }
-
- fn requireEatBytes(self: *Parser, bytes: []const u8) !void {
- const start = self.i;
- for (bytes) |byte| {
- if (self.source[self.i] != byte) {
- self.i = start;
- return self.fail("expected '{}'", .{bytes});
- }
- self.i += 1;
- }
- }
-
- fn skipToAndOver(self: *Parser, byte: u8) ![]const u8 {
- const start_i = self.i;
- while (self.source[self.i] != 0) : (self.i += 1) {
- if (self.source[self.i] == byte) {
- const result = self.source[start_i..self.i];
- self.i += 1;
- return result;
- }
- }
- return self.fail("unexpected EOF", .{});
- }
-
- /// ParseFailure is an internal error code; handled in `parse`.
- const InnerError = error{ ParseFailure, OutOfMemory };
-
- fn failByte(self: *Parser, byte: u8) InnerError {
- if (byte == 0) {
- return self.fail("unexpected EOF", .{});
- } else {
- return self.fail("unexpected byte: '{c}'", .{byte});
- }
- }
-
- fn fail(self: *Parser, comptime format: []const u8, args: anytype) InnerError {
- @setCold(true);
- self.error_msg = ErrorMsg{
- .byte_offset = self.i,
- .msg = try std.fmt.allocPrint(&self.arena.allocator, format, args),
- };
- return error.ParseFailure;
- }
-
- fn parseInstruction(self: *Parser, body_ctx: ?*Body, name: []const u8) InnerError!*Decl {
- const contents_start = self.i;
- const fn_name = try skipToAndOver(self, '(');
- inline for (@typeInfo(Inst.Tag).Enum.fields) |field| {
- if (mem.eql(u8, field.name, fn_name)) {
- const tag = @field(Inst.Tag, field.name);
- return parseInstructionGeneric(self, field.name, tag.Type(), tag, body_ctx, name, contents_start);
- }
- }
- return self.fail("unknown instruction '{}'", .{fn_name});
- }
-
- fn parseInstructionGeneric(
- self: *Parser,
- comptime fn_name: []const u8,
- comptime InstType: type,
- tag: Inst.Tag,
- body_ctx: ?*Body,
- inst_name: []const u8,
- contents_start: usize,
- ) InnerError!*Decl {
- const inst_specific = try self.arena.allocator.create(InstType);
- inst_specific.base = .{
- .src = self.i,
- .tag = tag,
- };
-
- if (InstType == Inst.Block) {
- try self.block_table.put(inst_name, inst_specific);
- } else if (InstType == Inst.Loop) {
- try self.loop_table.put(inst_name, inst_specific);
- }
-
- if (@hasField(InstType, "ty")) {
- inst_specific.ty = opt_type orelse {
- return self.fail("instruction '" ++ fn_name ++ "' requires type", .{});
- };
- }
-
- const Positionals = @TypeOf(inst_specific.positionals);
- inline for (@typeInfo(Positionals).Struct.fields) |arg_field| {
- if (self.source[self.i] == ',') {
- self.i += 1;
- skipSpace(self);
- } else if (self.source[self.i] == ')') {
- return self.fail("expected positional parameter '{}'", .{arg_field.name});
- }
- @field(inst_specific.positionals, arg_field.name) = try parseParameterGeneric(
- self,
- arg_field.field_type,
- body_ctx,
- );
- skipSpace(self);
- }
-
- const KW_Args = @TypeOf(inst_specific.kw_args);
- inst_specific.kw_args = .{}; // assign defaults
- skipSpace(self);
- while (eatByte(self, ',')) {
- skipSpace(self);
- const name = try skipToAndOver(self, '=');
- inline for (@typeInfo(KW_Args).Struct.fields) |arg_field| {
- const field_name = arg_field.name;
- if (mem.eql(u8, name, field_name)) {
- const NonOptional = switch (@typeInfo(arg_field.field_type)) {
- .Optional => |info| info.child,
- else => arg_field.field_type,
- };
- @field(inst_specific.kw_args, field_name) = try parseParameterGeneric(self, NonOptional, body_ctx);
- break;
- }
- } else {
- return self.fail("unrecognized keyword parameter: '{}'", .{name});
- }
- skipSpace(self);
- }
- try requireEatBytes(self, ")");
-
- const decl = try self.arena.allocator.create(Decl);
- decl.* = .{
- .name = inst_name,
- .contents_hash = std.zig.hashSrc(self.source[contents_start..self.i]),
- .inst = &inst_specific.base,
- };
- //std.debug.warn("parsed {} = '{}'\n", .{ inst_specific.base.name, inst_specific.base.contents });
-
- return decl;
- }
-
- fn parseParameterGeneric(self: *Parser, comptime T: type, body_ctx: ?*Body) !T {
- if (@typeInfo(T) == .Enum) {
- const start = self.i;
- while (true) : (self.i += 1) switch (self.source[self.i]) {
- ' ', '\n', ',', ')' => {
- const enum_name = self.source[start..self.i];
- return std.meta.stringToEnum(T, enum_name) orelse {
- return self.fail("tag '{}' not a member of enum '{}'", .{ enum_name, @typeName(T) });
- };
- },
- 0 => return self.failByte(0),
- else => continue,
- };
- }
- switch (T) {
- Module.Body => return parseBody(self, body_ctx),
- bool => {
- const bool_value = switch (self.source[self.i]) {
- '0' => false,
- '1' => true,
- else => |byte| return self.fail("expected '0' or '1' for boolean value, found {c}", .{byte}),
- };
- self.i += 1;
- return bool_value;
- },
- []*Inst => {
- try requireEatBytes(self, "[");
- skipSpace(self);
- if (eatByte(self, ']')) return &[0]*Inst{};
-
- var instructions = std.ArrayList(*Inst).init(&self.arena.allocator);
- while (true) {
- skipSpace(self);
- try instructions.append(try parseParameterInst(self, body_ctx));
- skipSpace(self);
- if (!eatByte(self, ',')) break;
- }
- try requireEatBytes(self, "]");
- return instructions.toOwnedSlice();
- },
- *Inst => return parseParameterInst(self, body_ctx),
- []u8, []const u8 => return self.parseStringLiteral(),
- BigIntConst => return self.parseIntegerLiteral(),
- usize => {
- const big_int = try self.parseIntegerLiteral();
- return big_int.to(usize) catch |err| return self.fail("integer literal: {}", .{@errorName(err)});
- },
- TypedValue => return self.fail("'const' is a special instruction; not legal in ZIR text", .{}),
- *IrModule.Decl => return self.fail("'declval_in_module' is a special instruction; not legal in ZIR text", .{}),
- *Inst.Block => {
- const name = try self.parseStringLiteral();
- return self.block_table.get(name).?;
- },
- *Inst.Loop => {
- const name = try self.parseStringLiteral();
- return self.loop_table.get(name).?;
- },
- [][]const u8 => {
- try requireEatBytes(self, "[");
- skipSpace(self);
- if (eatByte(self, ']')) return &[0][]const u8{};
-
- var strings = std.ArrayList([]const u8).init(&self.arena.allocator);
- while (true) {
- skipSpace(self);
- try strings.append(try self.parseStringLiteral());
- skipSpace(self);
- if (!eatByte(self, ',')) break;
- }
- try requireEatBytes(self, "]");
- return strings.toOwnedSlice();
- },
- else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),
- }
- return self.fail("TODO parse parameter {}", .{@typeName(T)});
- }
-
- fn parseParameterInst(self: *Parser, body_ctx: ?*Body) !*Inst {
- const local_ref = switch (self.source[self.i]) {
- '@' => false,
- '%' => true,
- else => |byte| return self.fail("unexpected byte: '{c}'", .{byte}),
- };
- const map = if (local_ref)
- if (body_ctx) |bc|
- bc.name_map
- else
- return self.fail("referencing a % instruction in global scope", .{})
- else
- self.global_name_map;
-
- self.i += 1;
- const name_start = self.i;
- while (true) : (self.i += 1) switch (self.source[self.i]) {
- 0, ' ', '\n', ',', ')', ']' => break,
- else => continue,
- };
- const ident = self.source[name_start..self.i];
- return map.get(ident) orelse {
- const bad_name = self.source[name_start - 1 .. self.i];
- const src = name_start - 1;
- if (local_ref) {
- self.i = src;
- return self.fail("unrecognized identifier: {}", .{bad_name});
- } else {
- const declval = try self.arena.allocator.create(Inst.DeclVal);
- declval.* = .{
- .base = .{
- .src = src,
- .tag = Inst.DeclVal.base_tag,
- },
- .positionals = .{ .name = ident },
- .kw_args = .{},
- };
- return &declval.base;
- }
- };
- }
-
- fn generateName(self: *Parser) ![]u8 {
- const result = try std.fmt.allocPrint(&self.arena.allocator, "unnamed${}", .{self.unnamed_index});
- self.unnamed_index += 1;
- return result;
- }
-};
-
-pub fn emit(allocator: *Allocator, old_module: *IrModule) !Module {
- var ctx: EmitZIR = .{
- .allocator = allocator,
- .decls = .{},
- .arena = std.heap.ArenaAllocator.init(allocator),
- .old_module = old_module,
- .next_auto_name = 0,
- .names = std.StringArrayHashMap(void).init(allocator),
- .primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Decl).init(allocator),
- .indent = 0,
- .block_table = std.AutoHashMap(*ir.Inst.Block, *Inst.Block).init(allocator),
- .loop_table = std.AutoHashMap(*ir.Inst.Loop, *Inst.Loop).init(allocator),
- .metadata = std.AutoHashMap(*Inst, Module.MetaData).init(allocator),
- .body_metadata = std.AutoHashMap(*Module.Body, Module.BodyMetaData).init(allocator),
- };
- errdefer ctx.metadata.deinit();
- errdefer ctx.body_metadata.deinit();
- defer ctx.block_table.deinit();
- defer ctx.loop_table.deinit();
- defer ctx.decls.deinit(allocator);
- defer ctx.names.deinit();
- defer ctx.primitive_table.deinit();
- errdefer ctx.arena.deinit();
-
- try ctx.emit();
-
- return Module{
- .decls = ctx.decls.toOwnedSlice(allocator),
- .arena = ctx.arena,
- .metadata = ctx.metadata,
- .body_metadata = ctx.body_metadata,
- };
-}
-
-/// For debugging purposes, prints a function representation to stderr.
-pub fn dumpFn(old_module: IrModule, module_fn: *IrModule.Fn) void {
- const allocator = old_module.gpa;
- var ctx: EmitZIR = .{
- .allocator = allocator,
- .decls = .{},
- .arena = std.heap.ArenaAllocator.init(allocator),
- .old_module = &old_module,
- .next_auto_name = 0,
- .names = std.StringHashMap(void).init(allocator),
- .primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Decl).init(allocator),
- .indent = 0,
- .block_table = std.AutoHashMap(*ir.Inst.Block, *Inst.Block).init(allocator),
- .loop_table = std.AutoHashMap(*ir.Inst.Loop, *Inst.Loop).init(allocator),
- .metadata = std.AutoHashMap(*Inst, Module.MetaData).init(allocator),
- .body_metadata = std.AutoHashMap(*Module.Body, Module.BodyMetaData).init(allocator),
- };
- defer ctx.metadata.deinit();
- defer ctx.body_metadata.deinit();
- defer ctx.block_table.deinit();
- defer ctx.loop_table.deinit();
- defer ctx.decls.deinit(allocator);
- defer ctx.names.deinit();
- defer ctx.primitive_table.deinit();
- defer ctx.arena.deinit();
-
- const fn_ty = module_fn.owner_decl.typed_value.most_recent.typed_value.ty;
- _ = ctx.emitFn(module_fn, 0, fn_ty) catch |err| {
- std.debug.print("unable to dump function: {}\n", .{err});
- return;
- };
- var module = Module{
- .decls = ctx.decls.items,
- .arena = ctx.arena,
- .metadata = ctx.metadata,
- .body_metadata = ctx.body_metadata,
- };
-
- module.dump();
-}
-
-const EmitZIR = struct {
- allocator: *Allocator,
- arena: std.heap.ArenaAllocator,
- old_module: *const IrModule,
- decls: std.ArrayListUnmanaged(*Decl),
- names: std.StringArrayHashMap(void),
- next_auto_name: usize,
- primitive_table: std.AutoHashMap(Inst.Primitive.Builtin, *Decl),
- indent: usize,
- block_table: std.AutoHashMap(*ir.Inst.Block, *Inst.Block),
- loop_table: std.AutoHashMap(*ir.Inst.Loop, *Inst.Loop),
- metadata: std.AutoHashMap(*Inst, Module.MetaData),
- body_metadata: std.AutoHashMap(*Module.Body, Module.BodyMetaData),
-
- fn emit(self: *EmitZIR) !void {
- // Put all the Decls in a list and sort them by name to avoid nondeterminism introduced
- // by the hash table.
- var src_decls = std.ArrayList(*IrModule.Decl).init(self.allocator);
- defer src_decls.deinit();
- try src_decls.ensureCapacity(self.old_module.decl_table.items().len);
- try self.decls.ensureCapacity(self.allocator, self.old_module.decl_table.items().len);
- try self.names.ensureCapacity(self.old_module.decl_table.items().len);
-
- for (self.old_module.decl_table.items()) |entry| {
- const decl = entry.value;
- src_decls.appendAssumeCapacity(decl);
- self.names.putAssumeCapacityNoClobber(mem.spanZ(decl.name), {});
- }
- std.sort.sort(*IrModule.Decl, src_decls.items, {}, (struct {
- fn lessThan(context: void, a: *IrModule.Decl, b: *IrModule.Decl) bool {
- return a.src_index < b.src_index;
- }
- }).lessThan);
-
- // Emit all the decls.
- for (src_decls.items) |ir_decl| {
- switch (ir_decl.analysis) {
- .unreferenced => continue,
-
- .complete => {},
- .codegen_failure => {}, // We still can emit the ZIR.
- .codegen_failure_retryable => {}, // We still can emit the ZIR.
-
- .in_progress => unreachable,
- .outdated => unreachable,
-
- .sema_failure,
- .sema_failure_retryable,
- .dependency_failure,
- => if (self.old_module.failed_decls.get(ir_decl)) |err_msg| {
- const fail_inst = try self.arena.allocator.create(Inst.CompileError);
- fail_inst.* = .{
- .base = .{
- .src = ir_decl.src(),
- .tag = Inst.CompileError.base_tag,
- },
- .positionals = .{
- .msg = try self.arena.allocator.dupe(u8, err_msg.msg),
- },
- .kw_args = .{},
- };
- const decl = try self.arena.allocator.create(Decl);
- decl.* = .{
- .name = mem.spanZ(ir_decl.name),
- .contents_hash = undefined,
- .inst = &fail_inst.base,
- };
- try self.decls.append(self.allocator, decl);
- continue;
- },
- }
- if (self.old_module.export_owners.get(ir_decl)) |exports| {
- for (exports) |module_export| {
- const symbol_name = try self.emitStringLiteral(module_export.src, module_export.options.name);
- const export_inst = try self.arena.allocator.create(Inst.Export);
- export_inst.* = .{
- .base = .{
- .src = module_export.src,
- .tag = Inst.Export.base_tag,
- },
- .positionals = .{
- .symbol_name = symbol_name.inst,
- .decl_name = mem.spanZ(module_export.exported_decl.name),
- },
- .kw_args = .{},
- };
- _ = try self.emitUnnamedDecl(&export_inst.base);
- }
- } else {
- const new_decl = try self.emitTypedValue(ir_decl.src(), ir_decl.typed_value.most_recent.typed_value);
- new_decl.name = try self.arena.allocator.dupe(u8, mem.spanZ(ir_decl.name));
- }
- }
- }
-
- const ZirBody = struct {
- inst_table: *std.AutoHashMap(*ir.Inst, *Inst),
- instructions: *std.ArrayList(*Inst),
- };
-
- fn resolveInst(self: *EmitZIR, new_body: ZirBody, inst: *ir.Inst) !*Inst {
- if (inst.cast(ir.Inst.Constant)) |const_inst| {
- const new_inst = if (const_inst.val.cast(Value.Payload.Function)) |func_pl| blk: {
- const owner_decl = func_pl.func.owner_decl;
- break :blk try self.emitDeclVal(inst.src, mem.spanZ(owner_decl.name));
- } else if (const_inst.val.cast(Value.Payload.DeclRef)) |declref| blk: {
- const decl_ref = try self.emitDeclRef(inst.src, declref.decl);
- try new_body.instructions.append(decl_ref);
- break :blk decl_ref;
- } else if (const_inst.val.cast(Value.Payload.Variable)) |var_pl| blk: {
- const owner_decl = var_pl.variable.owner_decl;
- break :blk try self.emitDeclVal(inst.src, mem.spanZ(owner_decl.name));
- } else blk: {
- break :blk (try self.emitTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val })).inst;
- };
- _ = try new_body.inst_table.put(inst, new_inst);
- return new_inst;
- } else {
- return new_body.inst_table.get(inst).?;
- }
- }
-
- fn emitDeclVal(self: *EmitZIR, src: usize, decl_name: []const u8) !*Inst {
- const declval = try self.arena.allocator.create(Inst.DeclVal);
- declval.* = .{
- .base = .{
- .src = src,
- .tag = Inst.DeclVal.base_tag,
- },
- .positionals = .{ .name = try self.arena.allocator.dupe(u8, decl_name) },
- .kw_args = .{},
- };
- return &declval.base;
- }
-
- fn emitComptimeIntVal(self: *EmitZIR, src: usize, val: Value) !*Decl {
- const big_int_space = try self.arena.allocator.create(Value.BigIntSpace);
- const int_inst = try self.arena.allocator.create(Inst.Int);
- int_inst.* = .{
- .base = .{
- .src = src,
- .tag = Inst.Int.base_tag,
- },
- .positionals = .{
- .int = val.toBigInt(big_int_space),
- },
- .kw_args = .{},
- };
- return self.emitUnnamedDecl(&int_inst.base);
- }
-
- fn emitDeclRef(self: *EmitZIR, src: usize, module_decl: *IrModule.Decl) !*Inst {
- const declref_inst = try self.arena.allocator.create(Inst.DeclRef);
- declref_inst.* = .{
- .base = .{
- .src = src,
- .tag = Inst.DeclRef.base_tag,
- },
- .positionals = .{
- .name = mem.spanZ(module_decl.name),
- },
- .kw_args = .{},
- };
- return &declref_inst.base;
- }
-
- fn emitFn(self: *EmitZIR, module_fn: *IrModule.Fn, src: usize, ty: Type) Allocator.Error!*Decl {
- var inst_table = std.AutoHashMap(*ir.Inst, *Inst).init(self.allocator);
- defer inst_table.deinit();
-
- var instructions = std.ArrayList(*Inst).init(self.allocator);
- defer instructions.deinit();
-
- switch (module_fn.analysis) {
- .queued => unreachable,
- .in_progress => unreachable,
- .success => |body| {
- try self.emitBody(body, &inst_table, &instructions);
- },
- .sema_failure => {
- const err_msg = self.old_module.failed_decls.get(module_fn.owner_decl).?;
- const fail_inst = try self.arena.allocator.create(Inst.CompileError);
- fail_inst.* = .{
- .base = .{
- .src = src,
- .tag = Inst.CompileError.base_tag,
- },
- .positionals = .{
- .msg = try self.arena.allocator.dupe(u8, err_msg.msg),
- },
- .kw_args = .{},
- };
- try instructions.append(&fail_inst.base);
- },
- .dependency_failure => {
- const fail_inst = try self.arena.allocator.create(Inst.CompileError);
- fail_inst.* = .{
- .base = .{
- .src = src,
- .tag = Inst.CompileError.base_tag,
- },
- .positionals = .{
- .msg = try self.arena.allocator.dupe(u8, "depends on another failed Decl"),
- },
- .kw_args = .{},
- };
- try instructions.append(&fail_inst.base);
- },
- }
-
- const fn_type = try self.emitType(src, ty);
-
- const arena_instrs = try self.arena.allocator.alloc(*Inst, instructions.items.len);
- mem.copy(*Inst, arena_instrs, instructions.items);
-
- const fn_inst = try self.arena.allocator.create(Inst.Fn);
- fn_inst.* = .{
- .base = .{
- .src = src,
- .tag = Inst.Fn.base_tag,
- },
- .positionals = .{
- .fn_type = fn_type.inst,
- .body = .{ .instructions = arena_instrs },
- },
- .kw_args = .{},
- };
- return self.emitUnnamedDecl(&fn_inst.base);
- }
-
- fn emitTypedValue(self: *EmitZIR, src: usize, typed_value: TypedValue) Allocator.Error!*Decl {
- const allocator = &self.arena.allocator;
- if (typed_value.val.cast(Value.Payload.DeclRef)) |decl_ref| {
- const decl = decl_ref.decl;
- return try self.emitUnnamedDecl(try self.emitDeclRef(src, decl));
- } else if (typed_value.val.cast(Value.Payload.Variable)) |variable| {
- return self.emitTypedValue(src, .{
- .ty = typed_value.ty,
- .val = variable.variable.init,
- });
- }
- if (typed_value.val.isUndef()) {
- const as_inst = try self.arena.allocator.create(Inst.BinOp);
- as_inst.* = .{
- .base = .{
- .tag = .as,
- .src = src,
- },
- .positionals = .{
- .lhs = (try self.emitType(src, typed_value.ty)).inst,
- .rhs = (try self.emitPrimitive(src, .@"undefined")).inst,
- },
- .kw_args = .{},
- };
- return self.emitUnnamedDecl(&as_inst.base);
- }
- switch (typed_value.ty.zigTypeTag()) {
- .Pointer => {
- const ptr_elem_type = typed_value.ty.elemType();
- switch (ptr_elem_type.zigTypeTag()) {
- .Array => {
- // TODO more checks to make sure this can be emitted as a string literal
- //const array_elem_type = ptr_elem_type.elemType();
- //if (array_elem_type.eql(Type.initTag(.u8)) and
- // ptr_elem_type.hasSentinel(Value.initTag(.zero)))
- //{
- //}
- const bytes = typed_value.val.toAllocatedBytes(allocator) catch |err| switch (err) {
- error.AnalysisFail => unreachable,
- else => |e| return e,
- };
- return self.emitStringLiteral(src, bytes);
- },
- else => |t| std.debug.panic("TODO implement emitTypedValue for pointer to {}", .{@tagName(t)}),
- }
- },
- .ComptimeInt => return self.emitComptimeIntVal(src, typed_value.val),
- .Int => {
- const as_inst = try self.arena.allocator.create(Inst.BinOp);
- as_inst.* = .{
- .base = .{
- .tag = .as,
- .src = src,
- },
- .positionals = .{
- .lhs = (try self.emitType(src, typed_value.ty)).inst,
- .rhs = (try self.emitComptimeIntVal(src, typed_value.val)).inst,
- },
- .kw_args = .{},
- };
- return self.emitUnnamedDecl(&as_inst.base);
- },
- .Type => {
- const ty = try typed_value.val.toType(&self.arena.allocator);
- return self.emitType(src, ty);
- },
- .Fn => {
- const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
- return self.emitFn(module_fn, src, typed_value.ty);
- },
- .Array => {
- // TODO more checks to make sure this can be emitted as a string literal
- //const array_elem_type = ptr_elem_type.elemType();
- //if (array_elem_type.eql(Type.initTag(.u8)) and
- // ptr_elem_type.hasSentinel(Value.initTag(.zero)))
- //{
- //}
- const bytes = typed_value.val.toAllocatedBytes(allocator) catch |err| switch (err) {
- error.AnalysisFail => unreachable,
- else => |e| return e,
- };
- const str_inst = try self.arena.allocator.create(Inst.Str);
- str_inst.* = .{
- .base = .{
- .src = src,
- .tag = Inst.Str.base_tag,
- },
- .positionals = .{
- .bytes = bytes,
- },
- .kw_args = .{},
- };
- return self.emitUnnamedDecl(&str_inst.base);
- },
- .Void => return self.emitPrimitive(src, .void_value),
- .Bool => if (typed_value.val.toBool())
- return self.emitPrimitive(src, .@"true")
- else
- return self.emitPrimitive(src, .@"false"),
- .EnumLiteral => {
- const enum_literal = @fieldParentPtr(Value.Payload.Bytes, "base", typed_value.val.ptr_otherwise);
- const inst = try self.arena.allocator.create(Inst.Str);
- inst.* = .{
- .base = .{
- .src = src,
- .tag = .enum_literal,
- },
- .positionals = .{
- .bytes = enum_literal.data,
- },
- .kw_args = .{},
- };
- return self.emitUnnamedDecl(&inst.base);
- },
- else => |t| std.debug.panic("TODO implement emitTypedValue for {}", .{@tagName(t)}),
- }
- }
-
- fn emitNoOp(self: *EmitZIR, src: usize, old_inst: *ir.Inst.NoOp, tag: Inst.Tag) Allocator.Error!*Inst {
- const new_inst = try self.arena.allocator.create(Inst.NoOp);
- new_inst.* = .{
- .base = .{
- .src = src,
- .tag = tag,
- },
- .positionals = .{},
- .kw_args = .{},
- };
- return &new_inst.base;
- }
-
- fn emitUnOp(
- self: *EmitZIR,
- src: usize,
- new_body: ZirBody,
- old_inst: *ir.Inst.UnOp,
- tag: Inst.Tag,
- ) Allocator.Error!*Inst {
- const new_inst = try self.arena.allocator.create(Inst.UnOp);
- new_inst.* = .{
- .base = .{
- .src = src,
- .tag = tag,
- },
- .positionals = .{
- .operand = try self.resolveInst(new_body, old_inst.operand),
- },
- .kw_args = .{},
- };
- return &new_inst.base;
- }
-
- fn emitBinOp(
- self: *EmitZIR,
- src: usize,
- new_body: ZirBody,
- old_inst: *ir.Inst.BinOp,
- tag: Inst.Tag,
- ) Allocator.Error!*Inst {
- const new_inst = try self.arena.allocator.create(Inst.BinOp);
- new_inst.* = .{
- .base = .{
- .src = src,
- .tag = tag,
- },
- .positionals = .{
- .lhs = try self.resolveInst(new_body, old_inst.lhs),
- .rhs = try self.resolveInst(new_body, old_inst.rhs),
- },
- .kw_args = .{},
- };
- return &new_inst.base;
- }
-
- fn emitCast(
- self: *EmitZIR,
- src: usize,
- new_body: ZirBody,
- old_inst: *ir.Inst.UnOp,
- tag: Inst.Tag,
- ) Allocator.Error!*Inst {
- const new_inst = try self.arena.allocator.create(Inst.BinOp);
- new_inst.* = .{
- .base = .{
- .src = src,
- .tag = tag,
- },
- .positionals = .{
- .lhs = (try self.emitType(old_inst.base.src, old_inst.base.ty)).inst,
- .rhs = try self.resolveInst(new_body, old_inst.operand),
- },
- .kw_args = .{},
- };
- return &new_inst.base;
- }
-
- fn emitBody(
- self: *EmitZIR,
- body: ir.Body,
- inst_table: *std.AutoHashMap(*ir.Inst, *Inst),
- instructions: *std.ArrayList(*Inst),
- ) Allocator.Error!void {
- const new_body = ZirBody{
- .inst_table = inst_table,
- .instructions = instructions,
- };
- for (body.instructions) |inst| {
- const new_inst = switch (inst.tag) {
- .constant => unreachable, // excluded from function bodies
-
- .breakpoint => try self.emitNoOp(inst.src, inst.castTag(.breakpoint).?, .breakpoint),
- .unreach => try self.emitNoOp(inst.src, inst.castTag(.unreach).?, .unreach_nocheck),
- .retvoid => try self.emitNoOp(inst.src, inst.castTag(.retvoid).?, .returnvoid),
- .dbg_stmt => try self.emitNoOp(inst.src, inst.castTag(.dbg_stmt).?, .dbg_stmt),
-
- .not => try self.emitUnOp(inst.src, new_body, inst.castTag(.not).?, .boolnot),
- .ret => try self.emitUnOp(inst.src, new_body, inst.castTag(.ret).?, .@"return"),
- .ptrtoint => try self.emitUnOp(inst.src, new_body, inst.castTag(.ptrtoint).?, .ptrtoint),
- .isnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnull).?, .isnull),
- .isnonnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnonnull).?, .isnonnull),
- .iserr => try self.emitUnOp(inst.src, new_body, inst.castTag(.iserr).?, .iserr),
- .load => try self.emitUnOp(inst.src, new_body, inst.castTag(.load).?, .deref),
- .ref => try self.emitUnOp(inst.src, new_body, inst.castTag(.ref).?, .ref),
- .unwrap_optional => try self.emitUnOp(inst.src, new_body, inst.castTag(.unwrap_optional).?, .unwrap_optional_unsafe),
- .wrap_optional => try self.emitCast(inst.src, new_body, inst.castTag(.wrap_optional).?, .as),
-
- .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, .add),
- .sub => try self.emitBinOp(inst.src, new_body, inst.castTag(.sub).?, .sub),
- .store => try self.emitBinOp(inst.src, new_body, inst.castTag(.store).?, .store),
- .cmp_lt => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_lt).?, .cmp_lt),
- .cmp_lte => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_lte).?, .cmp_lte),
- .cmp_eq => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_eq).?, .cmp_eq),
- .cmp_gte => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_gte).?, .cmp_gte),
- .cmp_gt => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_gt).?, .cmp_gt),
- .cmp_neq => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_neq).?, .cmp_neq),
-
- .bitcast => try self.emitCast(inst.src, new_body, inst.castTag(.bitcast).?, .bitcast),
- .intcast => try self.emitCast(inst.src, new_body, inst.castTag(.intcast).?, .intcast),
- .floatcast => try self.emitCast(inst.src, new_body, inst.castTag(.floatcast).?, .floatcast),
-
- .alloc => blk: {
- const new_inst = try self.arena.allocator.create(Inst.UnOp);
- new_inst.* = .{
- .base = .{
- .src = inst.src,
- .tag = .alloc,
- },
- .positionals = .{
- .operand = (try self.emitType(inst.src, inst.ty)).inst,
- },
- .kw_args = .{},
- };
- break :blk &new_inst.base;
- },
-
- .arg => blk: {
- const old_inst = inst.castTag(.arg).?;
- const new_inst = try self.arena.allocator.create(Inst.Arg);
- new_inst.* = .{
- .base = .{
- .src = inst.src,
- .tag = .arg,
- },
- .positionals = .{
- .name = try self.arena.allocator.dupe(u8, mem.spanZ(old_inst.name)),
- },
- .kw_args = .{},
- };
- break :blk &new_inst.base;
- },
-
- .block => blk: {
- const old_inst = inst.castTag(.block).?;
- const new_inst = try self.arena.allocator.create(Inst.Block);
-
- try self.block_table.put(old_inst, new_inst);
-
- var block_body = std.ArrayList(*Inst).init(self.allocator);
- defer block_body.deinit();
-
- try self.emitBody(old_inst.body, inst_table, &block_body);
-
- new_inst.* = .{
- .base = .{
- .src = inst.src,
- .tag = Inst.Block.base_tag,
- },
- .positionals = .{
- .body = .{ .instructions = block_body.toOwnedSlice() },
- },
- .kw_args = .{},
- };
-
- break :blk &new_inst.base;
- },
-
- .loop => blk: {
- const old_inst = inst.castTag(.loop).?;
- const new_inst = try self.arena.allocator.create(Inst.Loop);
-
- try self.loop_table.put(old_inst, new_inst);
-
- var loop_body = std.ArrayList(*Inst).init(self.allocator);
- defer loop_body.deinit();
-
- try self.emitBody(old_inst.body, inst_table, &loop_body);
-
- new_inst.* = .{
- .base = .{
- .src = inst.src,
- .tag = Inst.Loop.base_tag,
- },
- .positionals = .{
- .body = .{ .instructions = loop_body.toOwnedSlice() },
- },
- .kw_args = .{},
- };
-
- break :blk &new_inst.base;
- },
-
- .brvoid => blk: {
- const old_inst = inst.cast(ir.Inst.BrVoid).?;
- const new_block = self.block_table.get(old_inst.block).?;
- const new_inst = try self.arena.allocator.create(Inst.BreakVoid);
- new_inst.* = .{
- .base = .{
- .src = inst.src,
- .tag = Inst.BreakVoid.base_tag,
- },
- .positionals = .{
- .block = new_block,
- },
- .kw_args = .{},
- };
- break :blk &new_inst.base;
- },
-
- .br => blk: {
- const old_inst = inst.castTag(.br).?;
- const new_block = self.block_table.get(old_inst.block).?;
- const new_inst = try self.arena.allocator.create(Inst.Break);
- new_inst.* = .{
- .base = .{
- .src = inst.src,
- .tag = Inst.Break.base_tag,
- },
- .positionals = .{
- .block = new_block,
- .operand = try self.resolveInst(new_body, old_inst.operand),
- },
- .kw_args = .{},
- };
- break :blk &new_inst.base;
- },
-
- .call => blk: {
- const old_inst = inst.castTag(.call).?;
- const new_inst = try self.arena.allocator.create(Inst.Call);
-
- const args = try self.arena.allocator.alloc(*Inst, old_inst.args.len);
- for (args) |*elem, i| {
- elem.* = try self.resolveInst(new_body, old_inst.args[i]);
- }
- new_inst.* = .{
- .base = .{
- .src = inst.src,
- .tag = Inst.Call.base_tag,
- },
- .positionals = .{
- .func = try self.resolveInst(new_body, old_inst.func),
- .args = args,
- },
- .kw_args = .{},
- };
- break :blk &new_inst.base;
- },
-
- .assembly => blk: {
- const old_inst = inst.castTag(.assembly).?;
- const new_inst = try self.arena.allocator.create(Inst.Asm);
-
- const inputs = try self.arena.allocator.alloc(*Inst, old_inst.inputs.len);
- for (inputs) |*elem, i| {
- elem.* = (try self.emitStringLiteral(inst.src, old_inst.inputs[i])).inst;
- }
-
- const clobbers = try self.arena.allocator.alloc(*Inst, old_inst.clobbers.len);
- for (clobbers) |*elem, i| {
- elem.* = (try self.emitStringLiteral(inst.src, old_inst.clobbers[i])).inst;
- }
-
- const args = try self.arena.allocator.alloc(*Inst, old_inst.args.len);
- for (args) |*elem, i| {
- elem.* = try self.resolveInst(new_body, old_inst.args[i]);
- }
-
- new_inst.* = .{
- .base = .{
- .src = inst.src,
- .tag = Inst.Asm.base_tag,
- },
- .positionals = .{
- .asm_source = (try self.emitStringLiteral(inst.src, old_inst.asm_source)).inst,
- .return_type = (try self.emitType(inst.src, inst.ty)).inst,
- },
- .kw_args = .{
- .@"volatile" = old_inst.is_volatile,
- .output = if (old_inst.output) |o|
- (try self.emitStringLiteral(inst.src, o)).inst
- else
- null,
- .inputs = inputs,
- .clobbers = clobbers,
- .args = args,
- },
- };
- break :blk &new_inst.base;
- },
-
- .condbr => blk: {
- const old_inst = inst.castTag(.condbr).?;
-
- var then_body = std.ArrayList(*Inst).init(self.allocator);
- var else_body = std.ArrayList(*Inst).init(self.allocator);
-
- defer then_body.deinit();
- defer else_body.deinit();
-
- const then_deaths = try self.arena.allocator.alloc(*Inst, old_inst.thenDeaths().len);
- const else_deaths = try self.arena.allocator.alloc(*Inst, old_inst.elseDeaths().len);
-
- for (old_inst.thenDeaths()) |death, i| {
- then_deaths[i] = try self.resolveInst(new_body, death);
- }
- for (old_inst.elseDeaths()) |death, i| {
- else_deaths[i] = try self.resolveInst(new_body, death);
- }
-
- try self.emitBody(old_inst.then_body, inst_table, &then_body);
- try self.emitBody(old_inst.else_body, inst_table, &else_body);
-
- const new_inst = try self.arena.allocator.create(Inst.CondBr);
-
- try self.body_metadata.put(&new_inst.positionals.then_body, .{ .deaths = then_deaths });
- try self.body_metadata.put(&new_inst.positionals.else_body, .{ .deaths = else_deaths });
-
- new_inst.* = .{
- .base = .{
- .src = inst.src,
- .tag = Inst.CondBr.base_tag,
- },
- .positionals = .{
- .condition = try self.resolveInst(new_body, old_inst.condition),
- .then_body = .{ .instructions = then_body.toOwnedSlice() },
- .else_body = .{ .instructions = else_body.toOwnedSlice() },
- },
- .kw_args = .{},
- };
- break :blk &new_inst.base;
- },
-
- .varptr => @panic("TODO"),
- };
- try self.metadata.put(new_inst, .{
- .deaths = inst.deaths,
- .addr = @ptrToInt(inst),
- });
- try instructions.append(new_inst);
- try inst_table.put(inst, new_inst);
- }
- }
-
- fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Decl {
- switch (ty.tag()) {
- .i8 => return self.emitPrimitive(src, .i8),
- .u8 => return self.emitPrimitive(src, .u8),
- .i16 => return self.emitPrimitive(src, .i16),
- .u16 => return self.emitPrimitive(src, .u16),
- .i32 => return self.emitPrimitive(src, .i32),
- .u32 => return self.emitPrimitive(src, .u32),
- .i64 => return self.emitPrimitive(src, .i64),
- .u64 => return self.emitPrimitive(src, .u64),
- .isize => return self.emitPrimitive(src, .isize),
- .usize => return self.emitPrimitive(src, .usize),
- .c_short => return self.emitPrimitive(src, .c_short),
- .c_ushort => return self.emitPrimitive(src, .c_ushort),
- .c_int => return self.emitPrimitive(src, .c_int),
- .c_uint => return self.emitPrimitive(src, .c_uint),
- .c_long => return self.emitPrimitive(src, .c_long),
- .c_ulong => return self.emitPrimitive(src, .c_ulong),
- .c_longlong => return self.emitPrimitive(src, .c_longlong),
- .c_ulonglong => return self.emitPrimitive(src, .c_ulonglong),
- .c_longdouble => return self.emitPrimitive(src, .c_longdouble),
- .c_void => return self.emitPrimitive(src, .c_void),
- .f16 => return self.emitPrimitive(src, .f16),
- .f32 => return self.emitPrimitive(src, .f32),
- .f64 => return self.emitPrimitive(src, .f64),
- .f128 => return self.emitPrimitive(src, .f128),
- .anyerror => return self.emitPrimitive(src, .anyerror),
- else => switch (ty.zigTypeTag()) {
- .Bool => return self.emitPrimitive(src, .bool),
- .Void => return self.emitPrimitive(src, .void),
- .NoReturn => return self.emitPrimitive(src, .noreturn),
- .Type => return self.emitPrimitive(src, .type),
- .ComptimeInt => return self.emitPrimitive(src, .comptime_int),
- .ComptimeFloat => return self.emitPrimitive(src, .comptime_float),
- .Fn => {
- const param_types = try self.allocator.alloc(Type, ty.fnParamLen());
- defer self.allocator.free(param_types);
-
- ty.fnParamTypes(param_types);
- const emitted_params = try self.arena.allocator.alloc(*Inst, param_types.len);
- for (param_types) |param_type, i| {
- emitted_params[i] = (try self.emitType(src, param_type)).inst;
- }
-
- const fntype_inst = try self.arena.allocator.create(Inst.FnType);
- fntype_inst.* = .{
- .base = .{
- .src = src,
- .tag = Inst.FnType.base_tag,
- },
- .positionals = .{
- .param_types = emitted_params,
- .return_type = (try self.emitType(src, ty.fnReturnType())).inst,
- },
- .kw_args = .{
- .cc = ty.fnCallingConvention(),
- },
- };
- return self.emitUnnamedDecl(&fntype_inst.base);
- },
- .Int => {
- const info = ty.intInfo(self.old_module.getTarget());
- const signed = try self.emitPrimitive(src, if (info.signed) .@"true" else .@"false");
- const bits_payload = try self.arena.allocator.create(Value.Payload.Int_u64);
- bits_payload.* = .{ .int = info.bits };
- const bits = try self.emitComptimeIntVal(src, Value.initPayload(&bits_payload.base));
- const inttype_inst = try self.arena.allocator.create(Inst.IntType);
- inttype_inst.* = .{
- .base = .{
- .src = src,
- .tag = Inst.IntType.base_tag,
- },
- .positionals = .{
- .signed = signed.inst,
- .bits = bits.inst,
- },
- .kw_args = .{},
- };
- return self.emitUnnamedDecl(&inttype_inst.base);
- },
- .Pointer => {
- if (ty.isSinglePointer()) {
- const inst = try self.arena.allocator.create(Inst.UnOp);
- const tag: Inst.Tag = if (ty.isConstPtr()) .single_const_ptr_type else .single_mut_ptr_type;
- inst.* = .{
- .base = .{
- .src = src,
- .tag = tag,
- },
- .positionals = .{
- .operand = (try self.emitType(src, ty.elemType())).inst,
- },
- .kw_args = .{},
- };
- return self.emitUnnamedDecl(&inst.base);
- } else {
- std.debug.panic("TODO implement emitType for {}", .{ty});
- }
- },
- .Optional => {
- var buf: Type.Payload.PointerSimple = undefined;
- const inst = try self.arena.allocator.create(Inst.UnOp);
- inst.* = .{
- .base = .{
- .src = src,
- .tag = .optional_type,
- },
- .positionals = .{
- .operand = (try self.emitType(src, ty.optionalChild(&buf))).inst,
- },
- .kw_args = .{},
- };
- return self.emitUnnamedDecl(&inst.base);
- },
- .Array => {
- var len_pl = Value.Payload.Int_u64{ .int = ty.arrayLen() };
- const len = Value.initPayload(&len_pl.base);
-
- const inst = if (ty.sentinel()) |sentinel| blk: {
- const inst = try self.arena.allocator.create(Inst.ArrayTypeSentinel);
- inst.* = .{
- .base = .{
- .src = src,
- .tag = .array_type,
- },
- .positionals = .{
- .len = (try self.emitTypedValue(src, .{
- .ty = Type.initTag(.usize),
- .val = len,
- })).inst,
- .sentinel = (try self.emitTypedValue(src, .{
- .ty = ty.elemType(),
- .val = sentinel,
- })).inst,
- .elem_type = (try self.emitType(src, ty.elemType())).inst,
- },
- .kw_args = .{},
- };
- break :blk &inst.base;
- } else blk: {
- const inst = try self.arena.allocator.create(Inst.BinOp);
- inst.* = .{
- .base = .{
- .src = src,
- .tag = .array_type,
- },
- .positionals = .{
- .lhs = (try self.emitTypedValue(src, .{
- .ty = Type.initTag(.usize),
- .val = len,
- })).inst,
- .rhs = (try self.emitType(src, ty.elemType())).inst,
- },
- .kw_args = .{},
- };
- break :blk &inst.base;
- };
- return self.emitUnnamedDecl(inst);
- },
- else => std.debug.panic("TODO implement emitType for {}", .{ty}),
- },
- }
- }
-
- fn autoName(self: *EmitZIR) ![]u8 {
- while (true) {
- const proposed_name = try std.fmt.allocPrint(&self.arena.allocator, "unnamed${}", .{self.next_auto_name});
- self.next_auto_name += 1;
- const gop = try self.names.getOrPut(proposed_name);
- if (!gop.found_existing) {
- gop.entry.value = {};
- return proposed_name;
- }
- }
- }
-
- fn emitPrimitive(self: *EmitZIR, src: usize, tag: Inst.Primitive.Builtin) !*Decl {
- const gop = try self.primitive_table.getOrPut(tag);
- if (!gop.found_existing) {
- const primitive_inst = try self.arena.allocator.create(Inst.Primitive);
- primitive_inst.* = .{
- .base = .{
- .src = src,
- .tag = Inst.Primitive.base_tag,
- },
- .positionals = .{
- .tag = tag,
- },
- .kw_args = .{},
- };
- gop.entry.value = try self.emitUnnamedDecl(&primitive_inst.base);
- }
- return gop.entry.value;
- }
-
- fn emitStringLiteral(self: *EmitZIR, src: usize, str: []const u8) !*Decl {
- const str_inst = try self.arena.allocator.create(Inst.Str);
- str_inst.* = .{
- .base = .{
- .src = src,
- .tag = Inst.Str.base_tag,
- },
- .positionals = .{
- .bytes = str,
- },
- .kw_args = .{},
- };
- return self.emitUnnamedDecl(&str_inst.base);
- }
-
- fn emitUnnamedDecl(self: *EmitZIR, inst: *Inst) !*Decl {
- const decl = try self.arena.allocator.create(Decl);
- decl.* = .{
- .name = try self.autoName(),
- .contents_hash = undefined,
- .inst = inst,
- };
- try self.decls.append(self.allocator, decl);
- return decl;
- }
-};
diff --git a/src-self-hosted/zir_sema.zig b/src-self-hosted/zir_sema.zig
deleted file mode 100644
index 10543d2ee66230501041dbbc75acd95b0af77fb1..0000000000000000000000000000000000000000
--- a/src-self-hosted/zir_sema.zig
+++ /dev/null
@@ -1,1595 +0,0 @@
-//! Semantic analysis of ZIR instructions.
-//! This file operates on a `Module` instance, transforming untyped ZIR
-//! instructions into semantically-analyzed IR instructions. It does type
-//! checking, comptime control flow, and safety-check generation. This is the
-//! the heart of the Zig compiler.
-//! When deciding if something goes into this file or into Module, here is a
-//! guiding principle: if it has to do with (untyped) ZIR instructions, it goes
-//! here. If the analysis operates on typed IR instructions, it goes in Module.
-
-const std = @import("std");
-const mem = std.mem;
-const Allocator = std.mem.Allocator;
-const Value = @import("value.zig").Value;
-const Type = @import("type.zig").Type;
-const TypedValue = @import("TypedValue.zig");
-const assert = std.debug.assert;
-const ir = @import("ir.zig");
-const zir = @import("zir.zig");
-const Module = @import("Module.zig");
-const Inst = ir.Inst;
-const Body = ir.Body;
-const trace = @import("tracy.zig").trace;
-const Scope = Module.Scope;
-const InnerError = Module.InnerError;
-const Decl = Module.Decl;
-
-pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst {
- switch (old_inst.tag) {
- .alloc => return analyzeInstAlloc(mod, scope, old_inst.castTag(.alloc).?),
- .alloc_inferred => return analyzeInstAllocInferred(mod, scope, old_inst.castTag(.alloc_inferred).?),
- .arg => return analyzeInstArg(mod, scope, old_inst.castTag(.arg).?),
- .bitcast_ref => return analyzeInstBitCastRef(mod, scope, old_inst.castTag(.bitcast_ref).?),
- .bitcast_result_ptr => return analyzeInstBitCastResultPtr(mod, scope, old_inst.castTag(.bitcast_result_ptr).?),
- .block => return analyzeInstBlock(mod, scope, old_inst.castTag(.block).?, false),
- .block_comptime => return analyzeInstBlock(mod, scope, old_inst.castTag(.block_comptime).?, true),
- .block_flat => return analyzeInstBlockFlat(mod, scope, old_inst.castTag(.block_flat).?, false),
- .block_comptime_flat => return analyzeInstBlockFlat(mod, scope, old_inst.castTag(.block_comptime_flat).?, true),
- .@"break" => return analyzeInstBreak(mod, scope, old_inst.castTag(.@"break").?),
- .breakpoint => return analyzeInstBreakpoint(mod, scope, old_inst.castTag(.breakpoint).?),
- .breakvoid => return analyzeInstBreakVoid(mod, scope, old_inst.castTag(.breakvoid).?),
- .call => return analyzeInstCall(mod, scope, old_inst.castTag(.call).?),
- .coerce_result_block_ptr => return analyzeInstCoerceResultBlockPtr(mod, scope, old_inst.castTag(.coerce_result_block_ptr).?),
- .coerce_result_ptr => return analyzeInstCoerceResultPtr(mod, scope, old_inst.castTag(.coerce_result_ptr).?),
- .coerce_to_ptr_elem => return analyzeInstCoerceToPtrElem(mod, scope, old_inst.castTag(.coerce_to_ptr_elem).?),
- .compileerror => return analyzeInstCompileError(mod, scope, old_inst.castTag(.compileerror).?),
- .@"const" => return analyzeInstConst(mod, scope, old_inst.castTag(.@"const").?),
- .dbg_stmt => return analyzeInstDbgStmt(mod, scope, old_inst.castTag(.dbg_stmt).?),
- .declref => return analyzeInstDeclRef(mod, scope, old_inst.castTag(.declref).?),
- .declref_str => return analyzeInstDeclRefStr(mod, scope, old_inst.castTag(.declref_str).?),
- .declval => return analyzeInstDeclVal(mod, scope, old_inst.castTag(.declval).?),
- .declval_in_module => return analyzeInstDeclValInModule(mod, scope, old_inst.castTag(.declval_in_module).?),
- .ensure_result_used => return analyzeInstEnsureResultUsed(mod, scope, old_inst.castTag(.ensure_result_used).?),
- .ensure_result_non_error => return analyzeInstEnsureResultNonError(mod, scope, old_inst.castTag(.ensure_result_non_error).?),
- .ensure_indexable => return analyzeInstEnsureIndexable(mod, scope, old_inst.castTag(.ensure_indexable).?),
- .ref => return analyzeInstRef(mod, scope, old_inst.castTag(.ref).?),
- .ret_ptr => return analyzeInstRetPtr(mod, scope, old_inst.castTag(.ret_ptr).?),
- .ret_type => return analyzeInstRetType(mod, scope, old_inst.castTag(.ret_type).?),
- .single_const_ptr_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.single_const_ptr_type).?, false, .One),
- .single_mut_ptr_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.single_mut_ptr_type).?, true, .One),
- .many_const_ptr_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.many_const_ptr_type).?, false, .Many),
- .many_mut_ptr_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.many_mut_ptr_type).?, true, .Many),
- .c_const_ptr_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.c_const_ptr_type).?, false, .C),
- .c_mut_ptr_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.c_mut_ptr_type).?, true, .C),
- .const_slice_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.const_slice_type).?, false, .Slice),
- .mut_slice_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.mut_slice_type).?, true, .Slice),
- .ptr_type => return analyzeInstPtrType(mod, scope, old_inst.castTag(.ptr_type).?),
- .store => return analyzeInstStore(mod, scope, old_inst.castTag(.store).?),
- .str => return analyzeInstStr(mod, scope, old_inst.castTag(.str).?),
- .int => {
- const big_int = old_inst.castTag(.int).?.positionals.int;
- return mod.constIntBig(scope, old_inst.src, Type.initTag(.comptime_int), big_int);
- },
- .inttype => return analyzeInstIntType(mod, scope, old_inst.castTag(.inttype).?),
- .loop => return analyzeInstLoop(mod, scope, old_inst.castTag(.loop).?),
- .param_type => return analyzeInstParamType(mod, scope, old_inst.castTag(.param_type).?),
- .ptrtoint => return analyzeInstPtrToInt(mod, scope, old_inst.castTag(.ptrtoint).?),
- .fieldptr => return analyzeInstFieldPtr(mod, scope, old_inst.castTag(.fieldptr).?),
- .deref => return analyzeInstDeref(mod, scope, old_inst.castTag(.deref).?),
- .as => return analyzeInstAs(mod, scope, old_inst.castTag(.as).?),
- .@"asm" => return analyzeInstAsm(mod, scope, old_inst.castTag(.@"asm").?),
- .@"unreachable" => return analyzeInstUnreachable(mod, scope, old_inst.castTag(.@"unreachable").?, true),
- .unreach_nocheck => return analyzeInstUnreachable(mod, scope, old_inst.castTag(.unreach_nocheck).?, false),
- .@"return" => return analyzeInstRet(mod, scope, old_inst.castTag(.@"return").?),
- .returnvoid => return analyzeInstRetVoid(mod, scope, old_inst.castTag(.returnvoid).?),
- .@"fn" => return analyzeInstFn(mod, scope, old_inst.castTag(.@"fn").?),
- .@"export" => return analyzeInstExport(mod, scope, old_inst.castTag(.@"export").?),
- .primitive => return analyzeInstPrimitive(mod, scope, old_inst.castTag(.primitive).?),
- .fntype => return analyzeInstFnType(mod, scope, old_inst.castTag(.fntype).?),
- .intcast => return analyzeInstIntCast(mod, scope, old_inst.castTag(.intcast).?),
- .bitcast => return analyzeInstBitCast(mod, scope, old_inst.castTag(.bitcast).?),
- .floatcast => return analyzeInstFloatCast(mod, scope, old_inst.castTag(.floatcast).?),
- .elemptr => return analyzeInstElemPtr(mod, scope, old_inst.castTag(.elemptr).?),
- .add => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.add).?),
- .addwrap => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.addwrap).?),
- .sub => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.sub).?),
- .subwrap => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.subwrap).?),
- .mul => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.mul).?),
- .mulwrap => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.mulwrap).?),
- .div => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.div).?),
- .mod_rem => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.mod_rem).?),
- .array_cat => return analyzeInstArrayCat(mod, scope, old_inst.castTag(.array_cat).?),
- .array_mul => return analyzeInstArrayMul(mod, scope, old_inst.castTag(.array_mul).?),
- .bitand => return analyzeInstBitwise(mod, scope, old_inst.castTag(.bitand).?),
- .bitnot => return analyzeInstBitNot(mod, scope, old_inst.castTag(.bitnot).?),
- .bitor => return analyzeInstBitwise(mod, scope, old_inst.castTag(.bitor).?),
- .xor => return analyzeInstBitwise(mod, scope, old_inst.castTag(.xor).?),
- .shl => return analyzeInstShl(mod, scope, old_inst.castTag(.shl).?),
- .shr => return analyzeInstShr(mod, scope, old_inst.castTag(.shr).?),
- .cmp_lt => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_lt).?, .lt),
- .cmp_lte => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_lte).?, .lte),
- .cmp_eq => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_eq).?, .eq),
- .cmp_gte => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_gte).?, .gte),
- .cmp_gt => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_gt).?, .gt),
- .cmp_neq => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_neq).?, .neq),
- .condbr => return analyzeInstCondBr(mod, scope, old_inst.castTag(.condbr).?),
- .isnull => return analyzeInstIsNonNull(mod, scope, old_inst.castTag(.isnull).?, true),
- .isnonnull => return analyzeInstIsNonNull(mod, scope, old_inst.castTag(.isnonnull).?, false),
- .iserr => return analyzeInstIsErr(mod, scope, old_inst.castTag(.iserr).?),
- .boolnot => return analyzeInstBoolNot(mod, scope, old_inst.castTag(.boolnot).?),
- .typeof => return analyzeInstTypeOf(mod, scope, old_inst.castTag(.typeof).?),
- .optional_type => return analyzeInstOptionalType(mod, scope, old_inst.castTag(.optional_type).?),
- .unwrap_optional_safe => return analyzeInstUnwrapOptional(mod, scope, old_inst.castTag(.unwrap_optional_safe).?, true),
- .unwrap_optional_unsafe => return analyzeInstUnwrapOptional(mod, scope, old_inst.castTag(.unwrap_optional_unsafe).?, false),
- .unwrap_err_safe => return analyzeInstUnwrapErr(mod, scope, old_inst.castTag(.unwrap_err_safe).?, true),
- .unwrap_err_unsafe => return analyzeInstUnwrapErr(mod, scope, old_inst.castTag(.unwrap_err_unsafe).?, false),
- .unwrap_err_code => return analyzeInstUnwrapErrCode(mod, scope, old_inst.castTag(.unwrap_err_code).?),
- .ensure_err_payload_void => return analyzeInstEnsureErrPayloadVoid(mod, scope, old_inst.castTag(.ensure_err_payload_void).?),
- .array_type => return analyzeInstArrayType(mod, scope, old_inst.castTag(.array_type).?),
- .array_type_sentinel => return analyzeInstArrayTypeSentinel(mod, scope, old_inst.castTag(.array_type_sentinel).?),
- .enum_literal => return analyzeInstEnumLiteral(mod, scope, old_inst.castTag(.enum_literal).?),
- .merge_error_sets => return analyzeInstMergeErrorSets(mod, scope, old_inst.castTag(.merge_error_sets).?),
- .error_union_type => return analyzeInstErrorUnionType(mod, scope, old_inst.castTag(.error_union_type).?),
- .anyframe_type => return analyzeInstAnyframeType(mod, scope, old_inst.castTag(.anyframe_type).?),
- .error_set => return analyzeInstErrorSet(mod, scope, old_inst.castTag(.error_set).?),
- .slice => return analyzeInstSlice(mod, scope, old_inst.castTag(.slice).?),
- .slice_start => return analyzeInstSliceStart(mod, scope, old_inst.castTag(.slice_start).?),
- }
-}
-
-pub fn analyzeBody(mod: *Module, scope: *Scope, body: zir.Module.Body) !void {
- for (body.instructions) |src_inst, i| {
- const analyzed_inst = try analyzeInst(mod, scope, src_inst);
- src_inst.analyzed_inst = analyzed_inst;
- if (analyzed_inst.ty.zigTypeTag() == .NoReturn) {
- for (body.instructions[i..]) |unreachable_inst| {
- if (unreachable_inst.castTag(.dbg_stmt)) |dbg_stmt| {
- return mod.fail(scope, dbg_stmt.base.src, "unreachable code", .{});
- }
- }
- break;
- }
- }
-}
-
-pub fn analyzeBodyValueAsType(
- mod: *Module,
- block_scope: *Scope.Block,
- zir_result_inst: *zir.Inst,
- body: zir.Module.Body,
-) !Type {
- try analyzeBody(mod, &block_scope.base, body);
- const result_inst = zir_result_inst.analyzed_inst.?;
- const val = try mod.resolveConstValue(&block_scope.base, result_inst);
- return val.toType(block_scope.base.arena());
-}
-
-pub fn analyzeZirDecl(mod: *Module, decl: *Decl, src_decl: *zir.Decl) InnerError!bool {
- var decl_scope: Scope.DeclAnalysis = .{
- .decl = decl,
- .arena = std.heap.ArenaAllocator.init(mod.gpa),
- };
- errdefer decl_scope.arena.deinit();
-
- decl.analysis = .in_progress;
-
- const typed_value = try analyzeConstInst(mod, &decl_scope.base, src_decl.inst);
- const arena_state = try decl_scope.arena.allocator.create(std.heap.ArenaAllocator.State);
-
- var prev_type_has_bits = false;
- var type_changed = true;
-
- if (decl.typedValueManaged()) |tvm| {
- prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits();
- type_changed = !tvm.typed_value.ty.eql(typed_value.ty);
-
- tvm.deinit(mod.gpa);
- }
-
- arena_state.* = decl_scope.arena.state;
- decl.typed_value = .{
- .most_recent = .{
- .typed_value = typed_value,
- .arena = arena_state,
- },
- };
- decl.analysis = .complete;
- decl.generation = mod.generation;
- if (typed_value.ty.hasCodeGenBits()) {
- // We don't fully codegen the decl until later, but we do need to reserve a global
- // offset table index for it. This allows us to codegen decls out of dependency order,
- // increasing how many computations can be done in parallel.
- try mod.comp.bin_file.allocateDeclIndexes(decl);
- try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl });
- } else if (prev_type_has_bits) {
- mod.comp.bin_file.freeDecl(decl);
- }
-
- return type_changed;
-}
-
-pub fn resolveZirDecl(mod: *Module, scope: *Scope, src_decl: *zir.Decl) InnerError!*Decl {
- const zir_module = mod.root_scope.cast(Scope.ZIRModule).?;
- const entry = zir_module.contents.module.findDecl(src_decl.name).?;
- return resolveZirDeclHavingIndex(mod, scope, src_decl, entry.index);
-}
-
-fn resolveZirDeclHavingIndex(mod: *Module, scope: *Scope, src_decl: *zir.Decl, src_index: usize) InnerError!*Decl {
- const name_hash = scope.namespace().fullyQualifiedNameHash(src_decl.name);
- const decl = mod.decl_table.get(name_hash).?;
- decl.src_index = src_index;
- try mod.ensureDeclAnalyzed(decl);
- return decl;
-}
-
-/// Declares a dependency on the decl.
-fn resolveCompleteZirDecl(mod: *Module, scope: *Scope, src_decl: *zir.Decl) InnerError!*Decl {
- const decl = try resolveZirDecl(mod, scope, src_decl);
- switch (decl.analysis) {
- .unreferenced => unreachable,
- .in_progress => unreachable,
- .outdated => unreachable,
-
- .dependency_failure,
- .sema_failure,
- .sema_failure_retryable,
- .codegen_failure,
- .codegen_failure_retryable,
- => return error.AnalysisFail,
-
- .complete => {},
- }
- return decl;
-}
-
-/// TODO Look into removing this function. The body is only needed for .zir files, not .zig files.
-pub fn resolveInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst {
- if (old_inst.analyzed_inst) |inst| return inst;
-
- // If this assert trips, the instruction that was referenced did not get properly
- // analyzed before it was referenced.
- const zir_module = scope.namespace().cast(Scope.ZIRModule).?;
- const entry = if (old_inst.cast(zir.Inst.DeclVal)) |declval| blk: {
- const decl_name = declval.positionals.name;
- const entry = zir_module.contents.module.findDecl(decl_name) orelse
- return mod.fail(scope, old_inst.src, "decl '{}' not found", .{decl_name});
- break :blk entry;
- } else blk: {
- // If this assert trips, the instruction that was referenced did not get
- // properly analyzed by a previous instruction analysis before it was
- // referenced by the current one.
- break :blk zir_module.contents.module.findInstDecl(old_inst).?;
- };
- const decl = try resolveCompleteZirDecl(mod, scope, entry.decl);
- const decl_ref = try mod.analyzeDeclRef(scope, old_inst.src, decl);
- // Note: it would be tempting here to store the result into old_inst.analyzed_inst field,
- // but this would prevent the analyzeDeclRef from happening, which is needed to properly
- // detect Decl dependencies and dependency failures on updates.
- return mod.analyzeDeref(scope, old_inst.src, decl_ref, old_inst.src);
-}
-
-fn resolveConstString(mod: *Module, scope: *Scope, old_inst: *zir.Inst) ![]u8 {
- const new_inst = try resolveInst(mod, scope, old_inst);
- const wanted_type = Type.initTag(.const_slice_u8);
- const coerced_inst = try mod.coerce(scope, wanted_type, new_inst);
- const val = try mod.resolveConstValue(scope, coerced_inst);
- return val.toAllocatedBytes(scope.arena());
-}
-
-fn resolveType(mod: *Module, scope: *Scope, old_inst: *zir.Inst) !Type {
- const new_inst = try resolveInst(mod, scope, old_inst);
- const wanted_type = Type.initTag(.@"type");
- const coerced_inst = try mod.coerce(scope, wanted_type, new_inst);
- const val = try mod.resolveConstValue(scope, coerced_inst);
- return val.toType(scope.arena());
-}
-
-fn resolveInt(mod: *Module, scope: *Scope, old_inst: *zir.Inst, dest_type: Type) !u64 {
- const new_inst = try resolveInst(mod, scope, old_inst);
- const coerced = try mod.coerce(scope, dest_type, new_inst);
- const val = try mod.resolveConstValue(scope, coerced);
-
- return val.toUnsignedInt();
-}
-
-pub fn resolveInstConst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!TypedValue {
- const new_inst = try resolveInst(mod, scope, old_inst);
- const val = try mod.resolveConstValue(scope, new_inst);
- return TypedValue{
- .ty = new_inst.ty,
- .val = val,
- };
-}
-
-fn analyzeInstConst(mod: *Module, scope: *Scope, const_inst: *zir.Inst.Const) InnerError!*Inst {
- // Move the TypedValue from old memory to new memory. This allows freeing the ZIR instructions
- // after analysis.
- const typed_value_copy = try const_inst.positionals.typed_value.copy(scope.arena());
- return mod.constInst(scope, const_inst.base.src, typed_value_copy);
-}
-
-fn analyzeConstInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!TypedValue {
- const new_inst = try analyzeInst(mod, scope, old_inst);
- return TypedValue{
- .ty = new_inst.ty,
- .val = try mod.resolveConstValue(scope, new_inst),
- };
-}
-
-fn analyzeInstCoerceResultBlockPtr(
- mod: *Module,
- scope: *Scope,
- inst: *zir.Inst.CoerceResultBlockPtr,
-) InnerError!*Inst {
- return mod.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceResultBlockPtr", .{});
-}
-
-fn analyzeInstBitCastRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
- return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitCastRef", .{});
-}
-
-fn analyzeInstBitCastResultPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
- return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitCastResultPtr", .{});
-}
-
-fn analyzeInstCoerceResultPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
- return mod.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceResultPtr", .{});
-}
-
-/// Equivalent to `as(ptr_child_type(typeof(ptr)), value)`.
-fn analyzeInstCoerceToPtrElem(mod: *Module, scope: *Scope, inst: *zir.Inst.CoerceToPtrElem) InnerError!*Inst {
- const ptr = try resolveInst(mod, scope, inst.positionals.ptr);
- const operand = try resolveInst(mod, scope, inst.positionals.value);
- return mod.coerce(scope, ptr.ty.elemType(), operand);
-}
-
-fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
- return mod.fail(scope, inst.base.src, "TODO implement analyzeInstRetPtr", .{});
-}
-
-fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
- const operand = try resolveInst(mod, scope, inst.positionals.operand);
- const ptr_type = try mod.simplePtrType(scope, inst.base.src, operand.ty, false, .One);
-
- if (operand.value()) |val| {
- const ref_payload = try scope.arena().create(Value.Payload.RefVal);
- ref_payload.* = .{ .val = val };
-
- return mod.constInst(scope, inst.base.src, .{
- .ty = ptr_type,
- .val = Value.initPayload(&ref_payload.base),
- });
- }
-
- const b = try mod.requireRuntimeBlock(scope, inst.base.src);
- return mod.addUnOp(b, inst.base.src, ptr_type, .ref, operand);
-}
-
-fn analyzeInstRetType(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
- const b = try mod.requireFunctionBlock(scope, inst.base.src);
- const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
- const ret_type = fn_ty.fnReturnType();
- return mod.constType(scope, inst.base.src, ret_type);
-}
-
-fn analyzeInstEnsureResultUsed(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
- const operand = try resolveInst(mod, scope, inst.positionals.operand);
- switch (operand.ty.zigTypeTag()) {
- .Void, .NoReturn => return mod.constVoid(scope, operand.src),
- else => return mod.fail(scope, operand.src, "expression value is ignored", .{}),
- }
-}
-
-fn analyzeInstEnsureResultNonError(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
- const operand = try resolveInst(mod, scope, inst.positionals.operand);
- switch (operand.ty.zigTypeTag()) {
- .ErrorSet, .ErrorUnion => return mod.fail(scope, operand.src, "error is discarded", .{}),
- else => return mod.constVoid(scope, operand.src),
- }
-}
-
-fn analyzeInstEnsureIndexable(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
- const operand = try resolveInst(mod, scope, inst.positionals.operand);
- const elem_ty = operand.ty.elemType();
- if (elem_ty.isIndexable()) {
- return mod.constVoid(scope, operand.src);
- } else {
- // TODO error notes
- // error: type '{}' does not support indexing
- // note: for loop operand must be an array, a slice or a tuple
- return mod.fail(scope, operand.src, "for loop operand must be an array, a slice or a tuple", .{});
- }
-}
-
-fn analyzeInstAlloc(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
- const var_type = try resolveType(mod, scope, inst.positionals.operand);
- // TODO this should happen only for var allocs
- if (!var_type.isValidVarType(false)) {
- return mod.fail(scope, inst.base.src, "variable of type '{}' must be const or comptime", .{var_type});
- }
- const ptr_type = try mod.simplePtrType(scope, inst.base.src, var_type, true, .One);
- const b = try mod.requireRuntimeBlock(scope, inst.base.src);
- return mod.addNoOp(b, inst.base.src, ptr_type, .alloc);
-}
-
-fn analyzeInstAllocInferred(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
- return mod.fail(scope, inst.base.src, "TODO implement analyzeInstAllocInferred", .{});
-}
-
-fn analyzeInstStore(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
- const ptr = try resolveInst(mod, scope, inst.positionals.lhs);
- const value = try resolveInst(mod, scope, inst.positionals.rhs);
- return mod.storePtr(scope, inst.base.src, ptr, value);
-}
-
-fn analyzeInstParamType(mod: *Module, scope: *Scope, inst: *zir.Inst.ParamType) InnerError!*Inst {
- const fn_inst = try resolveInst(mod, scope, inst.positionals.func);
- const arg_index = inst.positionals.arg_index;
-
- const fn_ty: Type = switch (fn_inst.ty.zigTypeTag()) {
- .Fn => fn_inst.ty,
- .BoundFn => {
- return mod.fail(scope, fn_inst.src, "TODO implement analyzeInstParamType for method call syntax", .{});
- },
- else => {
- return mod.fail(scope, fn_inst.src, "expected function, found '{}'", .{fn_inst.ty});
- },
- };
-
- // TODO support C-style var args
- const param_count = fn_ty.fnParamLen();
- if (arg_index >= param_count) {
- return mod.fail(scope, inst.base.src, "arg index {} out of bounds; '{}' has {} argument(s)", .{
- arg_index,
- fn_ty,
- param_count,
- });
- }
-
- // TODO support generic functions
- const param_type = fn_ty.fnParamType(arg_index);
- return mod.constType(scope, inst.base.src, param_type);
-}
-
-fn analyzeInstStr(mod: *Module, scope: *Scope, str_inst: *zir.Inst.Str) InnerError!*Inst {
- // The bytes references memory inside the ZIR module, which can get deallocated
- // after semantic analysis is complete. We need the memory to be in the new anonymous Decl's arena.
- var new_decl_arena = std.heap.ArenaAllocator.init(mod.gpa);
- errdefer new_decl_arena.deinit();
- const arena_bytes = try new_decl_arena.allocator.dupe(u8, str_inst.positionals.bytes);
-
- const ty_payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0);
- ty_payload.* = .{ .len = arena_bytes.len };
-
- const bytes_payload = try scope.arena().create(Value.Payload.Bytes);
- bytes_payload.* = .{ .data = arena_bytes };
-
- const new_decl = try mod.createAnonymousDecl(scope, &new_decl_arena, .{
- .ty = Type.initPayload(&ty_payload.base),
- .val = Value.initPayload(&bytes_payload.base),
- });
- return mod.analyzeDeclRef(scope, str_inst.base.src, new_decl);
-}
-
-fn analyzeInstExport(mod: *Module, scope: *Scope, export_inst: *zir.Inst.Export) InnerError!*Inst {
- const symbol_name = try resolveConstString(mod, scope, export_inst.positionals.symbol_name);
- const exported_decl = mod.lookupDeclName(scope, export_inst.positionals.decl_name) orelse
- return mod.fail(scope, export_inst.base.src, "decl '{}' not found", .{export_inst.positionals.decl_name});
- try mod.analyzeExport(scope, export_inst.base.src, symbol_name, exported_decl);
- return mod.constVoid(scope, export_inst.base.src);
-}
-
-fn analyzeInstCompileError(mod: *Module, scope: *Scope, inst: *zir.Inst.CompileError) InnerError!*Inst {
- return mod.fail(scope, inst.base.src, "{}", .{inst.positionals.msg});
-}
-
-fn analyzeInstArg(mod: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!*Inst {
- const b = try mod.requireRuntimeBlock(scope, inst.base.src);
- const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
- const param_index = b.instructions.items.len;
- const param_count = fn_ty.fnParamLen();
- if (param_index >= param_count) {
- return mod.fail(scope, inst.base.src, "parameter index {} outside list of length {}", .{
- param_index,
- param_count,
- });
- }
- const param_type = fn_ty.fnParamType(param_index);
- const name = try scope.arena().dupeZ(u8, inst.positionals.name);
- return mod.addArg(b, inst.base.src, param_type, name);
-}
-
-fn analyzeInstLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError!*Inst {
- const parent_block = scope.cast(Scope.Block).?;
-
- // Reserve space for a Loop instruction so that generated Break instructions can
- // point to it, even if it doesn't end up getting used because the code ends up being
- // comptime evaluated.
- const loop_inst = try parent_block.arena.create(Inst.Loop);
- loop_inst.* = .{
- .base = .{
- .tag = Inst.Loop.base_tag,
- .ty = Type.initTag(.noreturn),
- .src = inst.base.src,
- },
- .body = undefined,
- };
-
- var child_block: Scope.Block = .{
- .parent = parent_block,
- .func = parent_block.func,
- .decl = parent_block.decl,
- .instructions = .{},
- .arena = parent_block.arena,
- .is_comptime = parent_block.is_comptime,
- };
- defer child_block.instructions.deinit(mod.gpa);
-
- try analyzeBody(mod, &child_block.base, inst.positionals.body);
-
- // Loop repetition is implied so the last instruction may or may not be a noreturn instruction.
-
- try parent_block.instructions.append(mod.gpa, &loop_inst.base);
- loop_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) };
- return &loop_inst.base;
-}
-
-fn analyzeInstBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_comptime: bool) InnerError!*Inst {
- const parent_block = scope.cast(Scope.Block).?;
-
- var child_block: Scope.Block = .{
- .parent = parent_block,
- .func = parent_block.func,
- .decl = parent_block.decl,
- .instructions = .{},
- .arena = parent_block.arena,
- .label = null,
- .is_comptime = parent_block.is_comptime or is_comptime,
- };
- defer child_block.instructions.deinit(mod.gpa);
-
- try analyzeBody(mod, &child_block.base, inst.positionals.body);
-
- const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items);
- try parent_block.instructions.appendSlice(mod.gpa, copied_instructions);
-
- return copied_instructions[copied_instructions.len - 1];
-}
-
-fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_comptime: bool) InnerError!*Inst {
- const parent_block = scope.cast(Scope.Block).?;
-
- // Reserve space for a Block instruction so that generated Break instructions can
- // point to it, even if it doesn't end up getting used because the code ends up being
- // comptime evaluated.
- const block_inst = try parent_block.arena.create(Inst.Block);
- block_inst.* = .{
- .base = .{
- .tag = Inst.Block.base_tag,
- .ty = undefined, // Set after analysis.
- .src = inst.base.src,
- },
- .body = undefined,
- };
-
- var child_block: Scope.Block = .{
- .parent = parent_block,
- .func = parent_block.func,
- .decl = parent_block.decl,
- .instructions = .{},
- .arena = parent_block.arena,
- // TODO @as here is working around a stage1 miscompilation bug :(
- .label = @as(?Scope.Block.Label, Scope.Block.Label{
- .zir_block = inst,
- .results = .{},
- .block_inst = block_inst,
- }),
- .is_comptime = is_comptime or parent_block.is_comptime,
- };
- const label = &child_block.label.?;
-
- defer child_block.instructions.deinit(mod.gpa);
- defer label.results.deinit(mod.gpa);
-
- try analyzeBody(mod, &child_block.base, inst.positionals.body);
-
- // Blocks must terminate with noreturn instruction.
- assert(child_block.instructions.items.len != 0);
- assert(child_block.instructions.items[child_block.instructions.items.len - 1].ty.isNoReturn());
-
- if (label.results.items.len == 0) {
- // No need for a block instruction. We can put the new instructions directly into the parent block.
- const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items);
- try parent_block.instructions.appendSlice(mod.gpa, copied_instructions);
- return copied_instructions[copied_instructions.len - 1];
- }
- if (label.results.items.len == 1) {
- const last_inst_index = child_block.instructions.items.len - 1;
- const last_inst = child_block.instructions.items[last_inst_index];
- if (last_inst.breakBlock()) |br_block| {
- if (br_block == block_inst) {
- // No need for a block instruction. We can put the new instructions directly into the parent block.
- // Here we omit the break instruction.
- const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items[0..last_inst_index]);
- try parent_block.instructions.appendSlice(mod.gpa, copied_instructions);
- return label.results.items[0];
- }
- }
- }
- // It should be impossible to have the number of results be > 1 in a comptime scope.
- assert(!child_block.is_comptime); // We should have already got a compile error in the condbr condition.
-
- // Need to set the type and emit the Block instruction. This allows machine code generation
- // to emit a jump instruction to after the block when it encounters the break.
- try parent_block.instructions.append(mod.gpa, &block_inst.base);
- block_inst.base.ty = try mod.resolvePeerTypes(scope, label.results.items);
- block_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) };
- return &block_inst.base;
-}
-
-fn analyzeInstBreakpoint(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
- const b = try mod.requireRuntimeBlock(scope, inst.base.src);
- return mod.addNoOp(b, inst.base.src, Type.initTag(.void), .breakpoint);
-}
-
-fn analyzeInstBreak(mod: *Module, scope: *Scope, inst: *zir.Inst.Break) InnerError!*Inst {
- const operand = try resolveInst(mod, scope, inst.positionals.operand);
- const block = inst.positionals.block;
- return analyzeBreak(mod, scope, inst.base.src, block, operand);
-}
-
-fn analyzeInstBreakVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.BreakVoid) InnerError!*Inst {
- const block = inst.positionals.block;
- const void_inst = try mod.constVoid(scope, inst.base.src);
- return analyzeBreak(mod, scope, inst.base.src, block, void_inst);
-}
-
-fn analyzeInstDbgStmt(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
- if (scope.cast(Scope.Block)) |b| {
- if (!b.is_comptime) {
- return mod.addNoOp(b, inst.base.src, Type.initTag(.void), .dbg_stmt);
- }
- }
- return mod.constVoid(scope, inst.base.src);
-}
-
-fn analyzeInstDeclRefStr(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclRefStr) InnerError!*Inst {
- const decl_name = try resolveConstString(mod, scope, inst.positionals.name);
- return mod.analyzeDeclRefByName(scope, inst.base.src, decl_name);
-}
-
-fn analyzeInstDeclRef(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclRef) InnerError!*Inst {
- return mod.analyzeDeclRefByName(scope, inst.base.src, inst.positionals.name);
-}
-
-fn analyzeInstDeclVal(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Inst {
- const decl = try analyzeDeclVal(mod, scope, inst);
- const ptr = try mod.analyzeDeclRef(scope, inst.base.src, decl);
- return mod.analyzeDeref(scope, inst.base.src, ptr, inst.base.src);
-}
-
-fn analyzeInstDeclValInModule(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclValInModule) InnerError!*Inst {
- const decl = inst.positionals.decl;
- return mod.analyzeDeclRef(scope, inst.base.src, decl);
-}
-
-fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst {
- const func = try resolveInst(mod, scope, inst.positionals.func);
- if (func.ty.zigTypeTag() != .Fn)
- return mod.fail(scope, inst.positionals.func.src, "type '{}' not a function", .{func.ty});
-
- const cc = func.ty.fnCallingConvention();
- if (cc == .Naked) {
- // TODO add error note: declared here
- return mod.fail(
- scope,
- inst.positionals.func.src,
- "unable to call function with naked calling convention",
- .{},
- );
- }
- const call_params_len = inst.positionals.args.len;
- const fn_params_len = func.ty.fnParamLen();
- if (func.ty.fnIsVarArgs()) {
- if (call_params_len < fn_params_len) {
- // TODO add error note: declared here
- return mod.fail(
- scope,
- inst.positionals.func.src,
- "expected at least {} argument(s), found {}",
- .{ fn_params_len, call_params_len },
- );
- }
- return mod.fail(scope, inst.base.src, "TODO implement support for calling var args functions", .{});
- } else if (fn_params_len != call_params_len) {
- // TODO add error note: declared here
- return mod.fail(
- scope,
- inst.positionals.func.src,
- "expected {} argument(s), found {}",
- .{ fn_params_len, call_params_len },
- );
- }
-
- if (inst.kw_args.modifier == .compile_time) {
- return mod.fail(scope, inst.base.src, "TODO implement comptime function calls", .{});
- }
- if (inst.kw_args.modifier != .auto) {
- return mod.fail(scope, inst.base.src, "TODO implement call with modifier {}", .{inst.kw_args.modifier});
- }
-
- // TODO handle function calls of generic functions
-
- const fn_param_types = try mod.gpa.alloc(Type, fn_params_len);
- defer mod.gpa.free(fn_param_types);
- func.ty.fnParamTypes(fn_param_types);
-
- const casted_args = try scope.arena().alloc(*Inst, fn_params_len);
- for (inst.positionals.args) |src_arg, i| {
- const uncasted_arg = try resolveInst(mod, scope, src_arg);
- casted_args[i] = try mod.coerce(scope, fn_param_types[i], uncasted_arg);
- }
-
- const ret_type = func.ty.fnReturnType();
-
- const b = try mod.requireRuntimeBlock(scope, inst.base.src);
- return mod.addCall(b, inst.base.src, ret_type, func, casted_args);
-}
-
-fn analyzeInstFn(mod: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst {
- const fn_type = try resolveType(mod, scope, fn_inst.positionals.fn_type);
- const fn_zir = blk: {
- var fn_arena = std.heap.ArenaAllocator.init(mod.gpa);
- errdefer fn_arena.deinit();
-
- const fn_zir = try scope.arena().create(Module.Fn.ZIR);
- fn_zir.* = .{
- .body = .{
- .instructions = fn_inst.positionals.body.instructions,
- },
- .arena = fn_arena.state,
- };
- break :blk fn_zir;
- };
- const new_func = try scope.arena().create(Module.Fn);
- new_func.* = .{
- .analysis = .{ .queued = fn_zir },
- .owner_decl = scope.decl().?,
- };
- const fn_payload = try scope.arena().create(Value.Payload.Function);
- fn_payload.* = .{ .func = new_func };
- return mod.constInst(scope, fn_inst.base.src, .{
- .ty = fn_type,
- .val = Value.initPayload(&fn_payload.base),
- });
-}
-
-fn analyzeInstIntType(mod: *Module, scope: *Scope, inttype: *zir.Inst.IntType) InnerError!*Inst {
- return mod.fail(scope, inttype.base.src, "TODO implement inttype", .{});
-}
-
-fn analyzeInstOptionalType(mod: *Module, scope: *Scope, optional: *zir.Inst.UnOp) InnerError!*Inst {
- const child_type = try resolveType(mod, scope, optional.positionals.operand);
-
- return mod.constType(scope, optional.base.src, try mod.optionalType(scope, child_type));
-}
-
-fn analyzeInstArrayType(mod: *Module, scope: *Scope, array: *zir.Inst.BinOp) InnerError!*Inst {
- // TODO these should be lazily evaluated
- const len = try resolveInstConst(mod, scope, array.positionals.lhs);
- const elem_type = try resolveType(mod, scope, array.positionals.rhs);
-
- return mod.constType(scope, array.base.src, try mod.arrayType(scope, len.val.toUnsignedInt(), null, elem_type));
-}
-
-fn analyzeInstArrayTypeSentinel(mod: *Module, scope: *Scope, array: *zir.Inst.ArrayTypeSentinel) InnerError!*Inst {
- // TODO these should be lazily evaluated
- const len = try resolveInstConst(mod, scope, array.positionals.len);
- const sentinel = try resolveInstConst(mod, scope, array.positionals.sentinel);
- const elem_type = try resolveType(mod, scope, array.positionals.elem_type);
-
- return mod.constType(scope, array.base.src, try mod.arrayType(scope, len.val.toUnsignedInt(), sentinel.val, elem_type));
-}
-
-fn analyzeInstErrorUnionType(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
- const error_union = try resolveType(mod, scope, inst.positionals.lhs);
- const payload = try resolveType(mod, scope, inst.positionals.rhs);
-
- if (error_union.zigTypeTag() != .ErrorSet) {
- return mod.fail(scope, inst.base.src, "expected error set type, found {}", .{error_union.elemType()});
- }
-
- return mod.constType(scope, inst.base.src, try mod.errorUnionType(scope, error_union, payload));
-}
-
-fn analyzeInstAnyframeType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
- const return_type = try resolveType(mod, scope, inst.positionals.operand);
-
- return mod.constType(scope, inst.base.src, try mod.anyframeType(scope, return_type));
-}
-
-fn analyzeInstErrorSet(mod: *Module, scope: *Scope, inst: *zir.Inst.ErrorSet) InnerError!*Inst {
- // The declarations arena will store the hashmap.
- var new_decl_arena = std.heap.ArenaAllocator.init(mod.gpa);
- errdefer new_decl_arena.deinit();
-
- const payload = try scope.arena().create(Value.Payload.ErrorSet);
- payload.* = .{
- .fields = .{},
- .decl = undefined, // populated below
- };
- try payload.fields.ensureCapacity(&new_decl_arena.allocator, @intCast(u32, inst.positionals.fields.len));
-
- for (inst.positionals.fields) |field_name| {
- const entry = try mod.getErrorValue(field_name);
- if (payload.fields.fetchPutAssumeCapacity(entry.key, entry.value)) |prev| {
- return mod.fail(scope, inst.base.src, "duplicate error: '{}'", .{field_name});
- }
- }
- // TODO create name in format "error:line:column"
- const new_decl = try mod.createAnonymousDecl(scope, &new_decl_arena, .{
- .ty = Type.initTag(.type),
- .val = Value.initPayload(&payload.base),
- });
- payload.decl = new_decl;
- return mod.analyzeDeclRef(scope, inst.base.src, new_decl);
-}
-
-fn analyzeInstMergeErrorSets(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
- return mod.fail(scope, inst.base.src, "TODO implement merge_error_sets", .{});
-}
-
-fn analyzeInstEnumLiteral(mod: *Module, scope: *Scope, inst: *zir.Inst.EnumLiteral) InnerError!*Inst {
- const payload = try scope.arena().create(Value.Payload.Bytes);
- payload.* = .{
- .base = .{ .tag = .enum_literal },
- .data = try scope.arena().dupe(u8, inst.positionals.name),
- };
- return mod.constInst(scope, inst.base.src, .{
- .ty = Type.initTag(.enum_literal),
- .val = Value.initPayload(&payload.base),
- });
-}
-
-fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst {
- const operand = try resolveInst(mod, scope, unwrap.positionals.operand);
- assert(operand.ty.zigTypeTag() == .Pointer);
-
- const elem_type = operand.ty.elemType();
- if (elem_type.zigTypeTag() != .Optional) {
- return mod.fail(scope, unwrap.base.src, "expected optional type, found {}", .{elem_type});
- }
-
- const child_type = try elem_type.optionalChildAlloc(scope.arena());
- const child_pointer = try mod.simplePtrType(scope, unwrap.base.src, child_type, operand.ty.isConstPtr(), .One);
-
- if (operand.value()) |val| {
- if (val.isNull()) {
- return mod.fail(scope, unwrap.base.src, "unable to unwrap null", .{});
- }
- return mod.constInst(scope, unwrap.base.src, .{
- .ty = child_pointer,
- .val = val,
- });
- }
-
- const b = try mod.requireRuntimeBlock(scope, unwrap.base.src);
- if (safety_check and mod.wantSafety(scope)) {
- const is_non_null = try mod.addUnOp(b, unwrap.base.src, Type.initTag(.bool), .isnonnull, operand);
- try mod.addSafetyCheck(b, is_non_null, .unwrap_null);
- }
- return mod.addUnOp(b, unwrap.base.src, child_pointer, .unwrap_optional, operand);
-}
-
-fn analyzeInstUnwrapErr(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst {
- return mod.fail(scope, unwrap.base.src, "TODO implement analyzeInstUnwrapErr", .{});
-}
-
-fn analyzeInstUnwrapErrCode(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst {
- return mod.fail(scope, unwrap.base.src, "TODO implement analyzeInstUnwrapErrCode", .{});
-}
-
-fn analyzeInstEnsureErrPayloadVoid(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst {
- return mod.fail(scope, unwrap.base.src, "TODO implement analyzeInstEnsureErrPayloadVoid", .{});
-}
-
-fn analyzeInstFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst {
- const return_type = try resolveType(mod, scope, fntype.positionals.return_type);
-
- // Hot path for some common function types.
- if (fntype.positionals.param_types.len == 0) {
- if (return_type.zigTypeTag() == .NoReturn and fntype.kw_args.cc == .Unspecified) {
- return mod.constType(scope, fntype.base.src, Type.initTag(.fn_noreturn_no_args));
- }
-
- if (return_type.zigTypeTag() == .Void and fntype.kw_args.cc == .Unspecified) {
- return mod.constType(scope, fntype.base.src, Type.initTag(.fn_void_no_args));
- }
-
- if (return_type.zigTypeTag() == .NoReturn and fntype.kw_args.cc == .Naked) {
- return mod.constType(scope, fntype.base.src, Type.initTag(.fn_naked_noreturn_no_args));
- }
-
- if (return_type.zigTypeTag() == .Void and fntype.kw_args.cc == .C) {
- return mod.constType(scope, fntype.base.src, Type.initTag(.fn_ccc_void_no_args));
- }
- }
-
- const arena = scope.arena();
- const param_types = try arena.alloc(Type, fntype.positionals.param_types.len);
- for (fntype.positionals.param_types) |param_type, i| {
- const resolved = try resolveType(mod, scope, param_type);
- // TODO skip for comptime params
- if (!resolved.isValidVarType(false)) {
- return mod.fail(scope, param_type.src, "parameter of type '{}' must be declared comptime", .{resolved});
- }
- param_types[i] = resolved;
- }
-
- const payload = try arena.create(Type.Payload.Function);
- payload.* = .{
- .cc = fntype.kw_args.cc,
- .return_type = return_type,
- .param_types = param_types,
- };
- return mod.constType(scope, fntype.base.src, Type.initPayload(&payload.base));
-}
-
-fn analyzeInstPrimitive(mod: *Module, scope: *Scope, primitive: *zir.Inst.Primitive) InnerError!*Inst {
- return mod.constInst(scope, primitive.base.src, primitive.positionals.tag.toTypedValue());
-}
-
-fn analyzeInstAs(mod: *Module, scope: *Scope, as: *zir.Inst.BinOp) InnerError!*Inst {
- const dest_type = try resolveType(mod, scope, as.positionals.lhs);
- const new_inst = try resolveInst(mod, scope, as.positionals.rhs);
- return mod.coerce(scope, dest_type, new_inst);
-}
-
-fn analyzeInstPtrToInt(mod: *Module, scope: *Scope, ptrtoint: *zir.Inst.UnOp) InnerError!*Inst {
- const ptr = try resolveInst(mod, scope, ptrtoint.positionals.operand);
- if (ptr.ty.zigTypeTag() != .Pointer) {
- return mod.fail(scope, ptrtoint.positionals.operand.src, "expected pointer, found '{}'", .{ptr.ty});
- }
- // TODO handle known-pointer-address
- const b = try mod.requireRuntimeBlock(scope, ptrtoint.base.src);
- const ty = Type.initTag(.usize);
- return mod.addUnOp(b, ptrtoint.base.src, ty, .ptrtoint, ptr);
-}
-
-fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr) InnerError!*Inst {
- const object_ptr = try resolveInst(mod, scope, fieldptr.positionals.object_ptr);
- const field_name = try resolveConstString(mod, scope, fieldptr.positionals.field_name);
-
- const elem_ty = switch (object_ptr.ty.zigTypeTag()) {
- .Pointer => object_ptr.ty.elemType(),
- else => return mod.fail(scope, fieldptr.positionals.object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}),
- };
- switch (elem_ty.zigTypeTag()) {
- .Array => {
- if (mem.eql(u8, field_name, "len")) {
- const len_payload = try scope.arena().create(Value.Payload.Int_u64);
- len_payload.* = .{ .int = elem_ty.arrayLen() };
-
- const ref_payload = try scope.arena().create(Value.Payload.RefVal);
- ref_payload.* = .{ .val = Value.initPayload(&len_payload.base) };
-
- return mod.constInst(scope, fieldptr.base.src, .{
- .ty = Type.initTag(.single_const_pointer_to_comptime_int),
- .val = Value.initPayload(&ref_payload.base),
- });
- } else {
- return mod.fail(
- scope,
- fieldptr.positionals.field_name.src,
- "no member named '{}' in '{}'",
- .{ field_name, elem_ty },
- );
- }
- },
- .Pointer => {
- const ptr_child = elem_ty.elemType();
- switch (ptr_child.zigTypeTag()) {
- .Array => {
- if (mem.eql(u8, field_name, "len")) {
- const len_payload = try scope.arena().create(Value.Payload.Int_u64);
- len_payload.* = .{ .int = ptr_child.arrayLen() };
-
- const ref_payload = try scope.arena().create(Value.Payload.RefVal);
- ref_payload.* = .{ .val = Value.initPayload(&len_payload.base) };
-
- return mod.constInst(scope, fieldptr.base.src, .{
- .ty = Type.initTag(.single_const_pointer_to_comptime_int),
- .val = Value.initPayload(&ref_payload.base),
- });
- } else {
- return mod.fail(
- scope,
- fieldptr.positionals.field_name.src,
- "no member named '{}' in '{}'",
- .{ field_name, elem_ty },
- );
- }
- },
- else => {},
- }
- },
- .Type => {
- _ = try mod.resolveConstValue(scope, object_ptr);
- const result = try mod.analyzeDeref(scope, fieldptr.base.src, object_ptr, object_ptr.src);
- const val = result.value().?;
- const child_type = try val.toType(scope.arena());
- switch (child_type.zigTypeTag()) {
- .ErrorSet => {
- // TODO resolve inferred error sets
- const entry = if (val.cast(Value.Payload.ErrorSet)) |payload|
- (payload.fields.getEntry(field_name) orelse
- return mod.fail(scope, fieldptr.base.src, "no error named '{}' in '{}'", .{ field_name, child_type })).*
- else
- try mod.getErrorValue(field_name);
-
- const error_payload = try scope.arena().create(Value.Payload.Error);
- error_payload.* = .{
- .name = entry.key,
- .value = entry.value,
- };
-
- const ref_payload = try scope.arena().create(Value.Payload.RefVal);
- ref_payload.* = .{ .val = Value.initPayload(&error_payload.base) };
-
- const result_type = if (child_type.tag() == .anyerror) blk: {
- const result_payload = try scope.arena().create(Type.Payload.ErrorSetSingle);
- result_payload.* = .{ .name = entry.key };
- break :blk Type.initPayload(&result_payload.base);
- } else child_type;
-
- return mod.constInst(scope, fieldptr.base.src, .{
- .ty = try mod.simplePtrType(scope, fieldptr.base.src, result_type, false, .One),
- .val = Value.initPayload(&ref_payload.base),
- });
- },
- else => return mod.fail(scope, fieldptr.base.src, "type '{}' does not support field access", .{child_type}),
- }
- },
- else => {},
- }
- return mod.fail(scope, fieldptr.base.src, "type '{}' does not support field access", .{elem_ty});
-}
-
-fn analyzeInstIntCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
- const dest_type = try resolveType(mod, scope, inst.positionals.lhs);
- const operand = try resolveInst(mod, scope, inst.positionals.rhs);
-
- const dest_is_comptime_int = switch (dest_type.zigTypeTag()) {
- .ComptimeInt => true,
- .Int => false,
- else => return mod.fail(
- scope,
- inst.positionals.lhs.src,
- "expected integer type, found '{}'",
- .{
- dest_type,
- },
- ),
- };
-
- switch (operand.ty.zigTypeTag()) {
- .ComptimeInt, .Int => {},
- else => return mod.fail(
- scope,
- inst.positionals.rhs.src,
- "expected integer type, found '{}'",
- .{operand.ty},
- ),
- }
-
- if (operand.value() != null) {
- return mod.coerce(scope, dest_type, operand);
- } else if (dest_is_comptime_int) {
- return mod.fail(scope, inst.base.src, "unable to cast runtime value to 'comptime_int'", .{});
- }
-
- return mod.fail(scope, inst.base.src, "TODO implement analyze widen or shorten int", .{});
-}
-
-fn analyzeInstBitCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
- const dest_type = try resolveType(mod, scope, inst.positionals.lhs);
- const operand = try resolveInst(mod, scope, inst.positionals.rhs);
- return mod.bitcast(scope, dest_type, operand);
-}
-
-fn analyzeInstFloatCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
- const dest_type = try resolveType(mod, scope, inst.positionals.lhs);
- const operand = try resolveInst(mod, scope, inst.positionals.rhs);
-
- const dest_is_comptime_float = switch (dest_type.zigTypeTag()) {
- .ComptimeFloat => true,
- .Float => false,
- else => return mod.fail(
- scope,
- inst.positionals.lhs.src,
- "expected float type, found '{}'",
- .{
- dest_type,
- },
- ),
- };
-
- switch (operand.ty.zigTypeTag()) {
- .ComptimeFloat, .Float, .ComptimeInt => {},
- else => return mod.fail(
- scope,
- inst.positionals.rhs.src,
- "expected float type, found '{}'",
- .{operand.ty},
- ),
- }
-
- if (operand.value() != null) {
- return mod.coerce(scope, dest_type, operand);
- } else if (dest_is_comptime_float) {
- return mod.fail(scope, inst.base.src, "unable to cast runtime value to 'comptime_float'", .{});
- }
-
- return mod.fail(scope, inst.base.src, "TODO implement analyze widen or shorten float", .{});
-}
-
-fn analyzeInstElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) InnerError!*Inst {
- const array_ptr = try resolveInst(mod, scope, inst.positionals.array_ptr);
- const uncasted_index = try resolveInst(mod, scope, inst.positionals.index);
- const elem_index = try mod.coerce(scope, Type.initTag(.usize), uncasted_index);
-
- const elem_ty = switch (array_ptr.ty.zigTypeTag()) {
- .Pointer => array_ptr.ty.elemType(),
- else => return mod.fail(scope, inst.positionals.array_ptr.src, "expected pointer, found '{}'", .{array_ptr.ty}),
- };
- if (!elem_ty.isIndexable()) {
- return mod.fail(scope, inst.base.src, "array access of non-array type '{}'", .{elem_ty});
- }
-
- if (elem_ty.isSinglePointer() and elem_ty.elemType().zigTypeTag() == .Array) {
- // we have to deref the ptr operand to get the actual array pointer
- const array_ptr_deref = try mod.analyzeDeref(scope, inst.base.src, array_ptr, inst.positionals.array_ptr.src);
- if (array_ptr_deref.value()) |array_ptr_val| {
- if (elem_index.value()) |index_val| {
- // Both array pointer and index are compile-time known.
- const index_u64 = index_val.toUnsignedInt();
- // @intCast here because it would have been impossible to construct a value that
- // required a larger index.
- const elem_ptr = try array_ptr_val.elemPtr(scope.arena(), @intCast(usize, index_u64));
-
- const type_payload = try scope.arena().create(Type.Payload.PointerSimple);
- type_payload.* = .{
- .base = .{ .tag = .single_const_pointer },
- .pointee_type = elem_ty.elemType().elemType(),
- };
-
- return mod.constInst(scope, inst.base.src, .{
- .ty = Type.initPayload(&type_payload.base),
- .val = elem_ptr,
- });
- }
- }
- }
-
- return mod.fail(scope, inst.base.src, "TODO implement more analyze elemptr", .{});
-}
-
-fn analyzeInstSlice(mod: *Module, scope: *Scope, inst: *zir.Inst.Slice) InnerError!*Inst {
- const array_ptr = try resolveInst(mod, scope, inst.positionals.array_ptr);
- const start = try resolveInst(mod, scope, inst.positionals.start);
- const end = if (inst.kw_args.end) |end| try resolveInst(mod, scope, end) else null;
- const sentinel = if (inst.kw_args.sentinel) |sentinel| try resolveInst(mod, scope, sentinel) else null;
-
- return mod.analyzeSlice(scope, inst.base.src, array_ptr, start, end, sentinel);
-}
-
-fn analyzeInstSliceStart(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
- const array_ptr = try resolveInst(mod, scope, inst.positionals.lhs);
- const start = try resolveInst(mod, scope, inst.positionals.rhs);
-
- return mod.analyzeSlice(scope, inst.base.src, array_ptr, start, null, null);
-}
-
-fn analyzeInstShl(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
- return mod.fail(scope, inst.base.src, "TODO implement analyzeInstShl", .{});
-}
-
-fn analyzeInstShr(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
- return mod.fail(scope, inst.base.src, "TODO implement analyzeInstShr", .{});
-}
-
-fn analyzeInstBitwise(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
- return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitwise", .{});
-}
-
-fn analyzeInstBitNot(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
- return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitNot", .{});
-}
-
-fn analyzeInstArrayCat(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
- return mod.fail(scope, inst.base.src, "TODO implement analyzeInstArrayCat", .{});
-}
-
-fn analyzeInstArrayMul(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
- return mod.fail(scope, inst.base.src, "TODO implement analyzeInstArrayMul", .{});
-}
-
-fn analyzeInstArithmetic(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
- const tracy = trace(@src());
- defer tracy.end();
-
- const lhs = try resolveInst(mod, scope, inst.positionals.lhs);
- const rhs = try resolveInst(mod, scope, inst.positionals.rhs);
-
- const instructions = &[_]*Inst{ lhs, rhs };
- const resolved_type = try mod.resolvePeerTypes(scope, instructions);
- const casted_lhs = try mod.coerce(scope, resolved_type, lhs);
- const casted_rhs = try mod.coerce(scope, resolved_type, rhs);
-
- const scalar_type = if (resolved_type.zigTypeTag() == .Vector)
- resolved_type.elemType()
- else
- resolved_type;
-
- const scalar_tag = scalar_type.zigTypeTag();
-
- if (lhs.ty.zigTypeTag() == .Vector and rhs.ty.zigTypeTag() == .Vector) {
- if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) {
- return mod.fail(scope, inst.base.src, "vector length mismatch: {} and {}", .{
- lhs.ty.arrayLen(),
- rhs.ty.arrayLen(),
- });
- }
- return mod.fail(scope, inst.base.src, "TODO implement support for vectors in analyzeInstBinOp", .{});
- } else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) {
- return mod.fail(scope, inst.base.src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{
- lhs.ty,
- rhs.ty,
- });
- }
-
- const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
- const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat;
-
- if (!is_int and !(is_float and floatOpAllowed(inst.base.tag))) {
- return mod.fail(scope, inst.base.src, "invalid operands to binary expression: '{}' and '{}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) });
- }
-
- if (casted_lhs.value()) |lhs_val| {
- if (casted_rhs.value()) |rhs_val| {
- if (lhs_val.isUndef() or rhs_val.isUndef()) {
- return mod.constInst(scope, inst.base.src, .{
- .ty = resolved_type,
- .val = Value.initTag(.undef),
- });
- }
- return analyzeInstComptimeOp(mod, scope, scalar_type, inst, lhs_val, rhs_val);
- }
- }
-
- const b = try mod.requireRuntimeBlock(scope, inst.base.src);
- const ir_tag = switch (inst.base.tag) {
- .add => Inst.Tag.add,
- .sub => Inst.Tag.sub,
- else => return mod.fail(scope, inst.base.src, "TODO implement arithmetic for operand '{}''", .{@tagName(inst.base.tag)}),
- };
-
- return mod.addBinOp(b, inst.base.src, scalar_type, ir_tag, casted_lhs, casted_rhs);
-}
-
-/// Analyzes operands that are known at comptime
-fn analyzeInstComptimeOp(mod: *Module, scope: *Scope, res_type: Type, inst: *zir.Inst.BinOp, lhs_val: Value, rhs_val: Value) InnerError!*Inst {
- // incase rhs is 0, simply return lhs without doing any calculations
- // TODO Once division is implemented we should throw an error when dividing by 0.
- if (rhs_val.compareWithZero(.eq)) {
- return mod.constInst(scope, inst.base.src, .{
- .ty = res_type,
- .val = lhs_val,
- });
- }
- const is_int = res_type.isInt() or res_type.zigTypeTag() == .ComptimeInt;
-
- const value = try switch (inst.base.tag) {
- .add => blk: {
- const val = if (is_int)
- Module.intAdd(scope.arena(), lhs_val, rhs_val)
- else
- mod.floatAdd(scope, res_type, inst.base.src, lhs_val, rhs_val);
- break :blk val;
- },
- .sub => blk: {
- const val = if (is_int)
- Module.intSub(scope.arena(), lhs_val, rhs_val)
- else
- mod.floatSub(scope, res_type, inst.base.src, lhs_val, rhs_val);
- break :blk val;
- },
- else => return mod.fail(scope, inst.base.src, "TODO Implement arithmetic operand '{}'", .{@tagName(inst.base.tag)}),
- };
-
- return mod.constInst(scope, inst.base.src, .{
- .ty = res_type,
- .val = value,
- });
-}
-
-fn analyzeInstDeref(mod: *Module, scope: *Scope, deref: *zir.Inst.UnOp) InnerError!*Inst {
- const ptr = try resolveInst(mod, scope, deref.positionals.operand);
- return mod.analyzeDeref(scope, deref.base.src, ptr, deref.positionals.operand.src);
-}
-
-fn analyzeInstAsm(mod: *Module, scope: *Scope, assembly: *zir.Inst.Asm) InnerError!*Inst {
- const return_type = try resolveType(mod, scope, assembly.positionals.return_type);
- const asm_source = try resolveConstString(mod, scope, assembly.positionals.asm_source);
- const output = if (assembly.kw_args.output) |o| try resolveConstString(mod, scope, o) else null;
-
- const inputs = try scope.arena().alloc([]const u8, assembly.kw_args.inputs.len);
- const clobbers = try scope.arena().alloc([]const u8, assembly.kw_args.clobbers.len);
- const args = try scope.arena().alloc(*Inst, assembly.kw_args.args.len);
-
- for (inputs) |*elem, i| {
- elem.* = try resolveConstString(mod, scope, assembly.kw_args.inputs[i]);
- }
- for (clobbers) |*elem, i| {
- elem.* = try resolveConstString(mod, scope, assembly.kw_args.clobbers[i]);
- }
- for (args) |*elem, i| {
- const arg = try resolveInst(mod, scope, assembly.kw_args.args[i]);
- elem.* = try mod.coerce(scope, Type.initTag(.usize), arg);
- }
-
- const b = try mod.requireRuntimeBlock(scope, assembly.base.src);
- const inst = try b.arena.create(Inst.Assembly);
- inst.* = .{
- .base = .{
- .tag = .assembly,
- .ty = return_type,
- .src = assembly.base.src,
- },
- .asm_source = asm_source,
- .is_volatile = assembly.kw_args.@"volatile",
- .output = output,
- .inputs = inputs,
- .clobbers = clobbers,
- .args = args,
- };
- try b.instructions.append(mod.gpa, &inst.base);
- return &inst.base;
-}
-
-fn analyzeInstCmp(
- mod: *Module,
- scope: *Scope,
- inst: *zir.Inst.BinOp,
- op: std.math.CompareOperator,
-) InnerError!*Inst {
- const lhs = try resolveInst(mod, scope, inst.positionals.lhs);
- const rhs = try resolveInst(mod, scope, inst.positionals.rhs);
-
- const is_equality_cmp = switch (op) {
- .eq, .neq => true,
- else => false,
- };
- const lhs_ty_tag = lhs.ty.zigTypeTag();
- const rhs_ty_tag = rhs.ty.zigTypeTag();
- if (is_equality_cmp and lhs_ty_tag == .Null and rhs_ty_tag == .Null) {
- // null == null, null != null
- return mod.constBool(scope, inst.base.src, op == .eq);
- } else if (is_equality_cmp and
- ((lhs_ty_tag == .Null and rhs_ty_tag == .Optional) or
- rhs_ty_tag == .Null and lhs_ty_tag == .Optional))
- {
- // comparing null with optionals
- const opt_operand = if (lhs_ty_tag == .Optional) lhs else rhs;
- return mod.analyzeIsNull(scope, inst.base.src, opt_operand, op == .neq);
- } else if (is_equality_cmp and
- ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr())))
- {
- return mod.fail(scope, inst.base.src, "TODO implement C pointer cmp", .{});
- } else if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) {
- const non_null_type = if (lhs_ty_tag == .Null) rhs.ty else lhs.ty;
- return mod.fail(scope, inst.base.src, "comparison of '{}' with null", .{non_null_type});
- } else if (is_equality_cmp and
- ((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or
- (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union)))
- {
- return mod.fail(scope, inst.base.src, "TODO implement equality comparison between a union's tag value and an enum literal", .{});
- } else if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) {
- if (!is_equality_cmp) {
- return mod.fail(scope, inst.base.src, "{} operator not allowed for errors", .{@tagName(op)});
- }
- return mod.fail(scope, inst.base.src, "TODO implement equality comparison between errors", .{});
- } else if (lhs.ty.isNumeric() and rhs.ty.isNumeric()) {
- // This operation allows any combination of integer and float types, regardless of the
- // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for
- // numeric types.
- return mod.cmpNumeric(scope, inst.base.src, lhs, rhs, op);
- }
- return mod.fail(scope, inst.base.src, "TODO implement more cmp analysis", .{});
-}
-
-fn analyzeInstTypeOf(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
- const operand = try resolveInst(mod, scope, inst.positionals.operand);
- return mod.constType(scope, inst.base.src, operand.ty);
-}
-
-fn analyzeInstBoolNot(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
- const uncasted_operand = try resolveInst(mod, scope, inst.positionals.operand);
- const bool_type = Type.initTag(.bool);
- const operand = try mod.coerce(scope, bool_type, uncasted_operand);
- if (try mod.resolveDefinedValue(scope, operand)) |val| {
- return mod.constBool(scope, inst.base.src, !val.toBool());
- }
- const b = try mod.requireRuntimeBlock(scope, inst.base.src);
- return mod.addUnOp(b, inst.base.src, bool_type, .not, operand);
-}
-
-fn analyzeInstIsNonNull(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, invert_logic: bool) InnerError!*Inst {
- const operand = try resolveInst(mod, scope, inst.positionals.operand);
- return mod.analyzeIsNull(scope, inst.base.src, operand, invert_logic);
-}
-
-fn analyzeInstIsErr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
- const operand = try resolveInst(mod, scope, inst.positionals.operand);
- return mod.analyzeIsErr(scope, inst.base.src, operand);
-}
-
-fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerError!*Inst {
- const uncasted_cond = try resolveInst(mod, scope, inst.positionals.condition);
- const cond = try mod.coerce(scope, Type.initTag(.bool), uncasted_cond);
-
- if (try mod.resolveDefinedValue(scope, cond)) |cond_val| {
- const body = if (cond_val.toBool()) &inst.positionals.then_body else &inst.positionals.else_body;
- try analyzeBody(mod, scope, body.*);
- return mod.constVoid(scope, inst.base.src);
- }
-
- const parent_block = try mod.requireRuntimeBlock(scope, inst.base.src);
-
- var true_block: Scope.Block = .{
- .parent = parent_block,
- .func = parent_block.func,
- .decl = parent_block.decl,
- .instructions = .{},
- .arena = parent_block.arena,
- .is_comptime = parent_block.is_comptime,
- };
- defer true_block.instructions.deinit(mod.gpa);
- try analyzeBody(mod, &true_block.base, inst.positionals.then_body);
-
- var false_block: Scope.Block = .{
- .parent = parent_block,
- .func = parent_block.func,
- .decl = parent_block.decl,
- .instructions = .{},
- .arena = parent_block.arena,
- .is_comptime = parent_block.is_comptime,
- };
- defer false_block.instructions.deinit(mod.gpa);
- try analyzeBody(mod, &false_block.base, inst.positionals.else_body);
-
- const then_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, true_block.instructions.items) };
- const else_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, false_block.instructions.items) };
- return mod.addCondBr(parent_block, inst.base.src, cond, then_body, else_body);
-}
-
-fn analyzeInstUnreachable(
- mod: *Module,
- scope: *Scope,
- unreach: *zir.Inst.NoOp,
- safety_check: bool,
-) InnerError!*Inst {
- const b = try mod.requireRuntimeBlock(scope, unreach.base.src);
- // TODO Add compile error for @optimizeFor occurring too late in a scope.
- if (safety_check and mod.wantSafety(scope)) {
- return mod.safetyPanic(b, unreach.base.src, .unreach);
- } else {
- return mod.addNoOp(b, unreach.base.src, Type.initTag(.noreturn), .unreach);
- }
-}
-
-fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
- const operand = try resolveInst(mod, scope, inst.positionals.operand);
- const b = try mod.requireRuntimeBlock(scope, inst.base.src);
- return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, operand);
-}
-
-fn analyzeInstRetVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
- const b = try mod.requireRuntimeBlock(scope, inst.base.src);
- if (b.func) |func| {
- // Need to emit a compile error if returning void is not allowed.
- const void_inst = try mod.constVoid(scope, inst.base.src);
- const fn_ty = func.owner_decl.typed_value.most_recent.typed_value.ty;
- const casted_void = try mod.coerce(scope, fn_ty.fnReturnType(), void_inst);
- if (casted_void.ty.zigTypeTag() != .Void) {
- return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, casted_void);
- }
- }
- return mod.addNoOp(b, inst.base.src, Type.initTag(.noreturn), .retvoid);
-}
-
-fn floatOpAllowed(tag: zir.Inst.Tag) bool {
- // extend this swich as additional operators are implemented
- return switch (tag) {
- .add, .sub => true,
- else => false,
- };
-}
-
-fn analyzeBreak(
- mod: *Module,
- scope: *Scope,
- src: usize,
- zir_block: *zir.Inst.Block,
- operand: *Inst,
-) InnerError!*Inst {
- var opt_block = scope.cast(Scope.Block);
- while (opt_block) |block| {
- if (block.label) |*label| {
- if (label.zir_block == zir_block) {
- try label.results.append(mod.gpa, operand);
- const b = try mod.requireRuntimeBlock(scope, src);
- return mod.addBr(b, src, label.block_inst, operand);
- }
- }
- opt_block = block.parent;
- } else unreachable;
-}
-
-fn analyzeDeclVal(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Decl {
- const decl_name = inst.positionals.name;
- const zir_module = scope.namespace().cast(Scope.ZIRModule).?;
- const src_decl = zir_module.contents.module.findDecl(decl_name) orelse
- return mod.fail(scope, inst.base.src, "use of undeclared identifier '{}'", .{decl_name});
-
- const decl = try resolveCompleteZirDecl(mod, scope, src_decl.decl);
-
- return decl;
-}
-
-fn analyzeInstSimplePtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, mutable: bool, size: std.builtin.TypeInfo.Pointer.Size) InnerError!*Inst {
- const elem_type = try resolveType(mod, scope, inst.positionals.operand);
- const ty = try mod.simplePtrType(scope, inst.base.src, elem_type, mutable, size);
- return mod.constType(scope, inst.base.src, ty);
-}
-
-fn analyzeInstPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.PtrType) InnerError!*Inst {
- // TODO lazy values
- const @"align" = if (inst.kw_args.@"align") |some|
- @truncate(u32, try resolveInt(mod, scope, some, Type.initTag(.u32)))
- else
- 0;
- const bit_offset = if (inst.kw_args.align_bit_start) |some|
- @truncate(u16, try resolveInt(mod, scope, some, Type.initTag(.u16)))
- else
- 0;
- const host_size = if (inst.kw_args.align_bit_end) |some|
- @truncate(u16, try resolveInt(mod, scope, some, Type.initTag(.u16)))
- else
- 0;
-
- if (host_size != 0 and bit_offset >= host_size * 8)
- return mod.fail(scope, inst.base.src, "bit offset starts after end of host integer", .{});
-
- const sentinel = if (inst.kw_args.sentinel) |some|
- (try resolveInstConst(mod, scope, some)).val
- else
- null;
-
- const elem_type = try resolveType(mod, scope, inst.positionals.child_type);
-
- const ty = try mod.ptrType(
- scope,
- inst.base.src,
- elem_type,
- sentinel,
- @"align",
- bit_offset,
- host_size,
- inst.kw_args.mutable,
- inst.kw_args.@"allowzero",
- inst.kw_args.@"volatile",
- inst.kw_args.size,
- );
- return mod.constType(scope, inst.base.src, ty);
-}
diff --git a/src/Cache.zig b/src/Cache.zig
new file mode 100644
index 0000000000000000000000000000000000000000..24c6ae3ac4e62f12c19471038dfc933d77085fbd
--- /dev/null
+++ b/src/Cache.zig
@@ -0,0 +1,890 @@
+gpa: *Allocator,
+manifest_dir: fs.Dir,
+hash: HashHelper = .{},
+
+const Cache = @This();
+const std = @import("std");
+const crypto = std.crypto;
+const fs = std.fs;
+const assert = std.debug.assert;
+const testing = std.testing;
+const mem = std.mem;
+const fmt = std.fmt;
+const Allocator = std.mem.Allocator;
+
+/// Be sure to call `CacheHash.deinit` after successful initialization.
+pub fn obtain(cache: *const Cache) CacheHash {
+ return CacheHash{
+ .cache = cache,
+ .hash = cache.hash,
+ .manifest_file = null,
+ .manifest_dirty = false,
+ .hex_digest = undefined,
+ };
+}
+
+/// This is 128 bits - Even with 2^54 cache entries, the probably of a collision would be under 10^-6
+pub const bin_digest_len = 16;
+pub const hex_digest_len = bin_digest_len * 2;
+
+const manifest_file_size_max = 50 * 1024 * 1024;
+
+/// The type used for hashing file contents. Currently, this is SipHash128(1, 3), because it
+/// provides enough collision resistance for the CacheHash use cases, while being one of our
+/// fastest options right now.
+pub const Hasher = crypto.auth.siphash.SipHash128(1, 3);
+
+/// Initial state, that can be copied.
+pub const hasher_init: Hasher = Hasher.init(&[_]u8{0} ** Hasher.minimum_key_length);
+
+pub const File = struct {
+ path: ?[]const u8,
+ max_file_size: ?usize,
+ stat: fs.File.Stat,
+ bin_digest: [bin_digest_len]u8,
+ contents: ?[]const u8,
+
+ pub fn deinit(self: *File, allocator: *Allocator) void {
+ if (self.path) |owned_slice| {
+ allocator.free(owned_slice);
+ self.path = null;
+ }
+ if (self.contents) |contents| {
+ allocator.free(contents);
+ self.contents = null;
+ }
+ self.* = undefined;
+ }
+};
+
+pub const HashHelper = struct {
+ hasher: Hasher = hasher_init,
+
+ /// Record a slice of bytes as an dependency of the process being cached
+ pub fn addBytes(hh: *HashHelper, bytes: []const u8) void {
+ hh.hasher.update(mem.asBytes(&bytes.len));
+ hh.hasher.update(bytes);
+ }
+
+ pub fn addOptionalBytes(hh: *HashHelper, optional_bytes: ?[]const u8) void {
+ hh.add(optional_bytes != null);
+ hh.addBytes(optional_bytes orelse return);
+ }
+
+ pub fn addListOfBytes(hh: *HashHelper, list_of_bytes: []const []const u8) void {
+ hh.add(list_of_bytes.len);
+ for (list_of_bytes) |bytes| hh.addBytes(bytes);
+ }
+
+ /// Convert the input value into bytes and record it as a dependency of the process being cached.
+ pub fn add(hh: *HashHelper, x: anytype) void {
+ switch (@TypeOf(x)) {
+ std.builtin.Version => {
+ hh.add(x.major);
+ hh.add(x.minor);
+ hh.add(x.patch);
+ },
+ std.Target.Os.TaggedVersionRange => {
+ switch (x) {
+ .linux => |linux| {
+ hh.add(linux.range.min);
+ hh.add(linux.range.max);
+ hh.add(linux.glibc);
+ },
+ .windows => |windows| {
+ hh.add(windows.min);
+ hh.add(windows.max);
+ },
+ .semver => |semver| {
+ hh.add(semver.min);
+ hh.add(semver.max);
+ },
+ .none => {},
+ }
+ },
+ else => switch (@typeInfo(@TypeOf(x))) {
+ .Bool, .Int, .Enum, .Array => hh.addBytes(mem.asBytes(&x)),
+ else => @compileError("unable to hash type " ++ @typeName(@TypeOf(x))),
+ },
+ }
+ }
+
+ pub fn addOptional(hh: *HashHelper, optional: anytype) void {
+ hh.add(optional != null);
+ hh.add(optional orelse return);
+ }
+
+ /// Returns a hex encoded hash of the inputs, without modifying state.
+ pub fn peek(hh: HashHelper) [hex_digest_len]u8 {
+ var copy = hh;
+ return copy.final();
+ }
+
+ /// Returns a hex encoded hash of the inputs, mutating the state of the hasher.
+ pub fn final(hh: *HashHelper) [hex_digest_len]u8 {
+ var bin_digest: [bin_digest_len]u8 = undefined;
+ hh.hasher.final(&bin_digest);
+
+ var out_digest: [hex_digest_len]u8 = undefined;
+ _ = std.fmt.bufPrint(&out_digest, "{x}", .{bin_digest}) catch unreachable;
+ return out_digest;
+ }
+};
+
+pub const Lock = struct {
+ manifest_file: fs.File,
+
+ pub fn release(lock: *Lock) void {
+ lock.manifest_file.close();
+ lock.* = undefined;
+ }
+};
+
+/// CacheHash manages project-local `zig-cache` directories.
+/// This is not a general-purpose cache.
+/// It is designed to be fast and simple, not to withstand attacks using specially-crafted input.
+pub const CacheHash = struct {
+ cache: *const Cache,
+ /// Current state for incremental hashing.
+ hash: HashHelper,
+ manifest_file: ?fs.File,
+ manifest_dirty: bool,
+ files: std.ArrayListUnmanaged(File) = .{},
+ hex_digest: [hex_digest_len]u8,
+
+ /// Add a file as a dependency of process being cached. When `hit` is
+ /// called, the file's contents will be checked to ensure that it matches
+ /// the contents from previous times.
+ ///
+ /// Max file size will be used to determine the amount of space to the file contents
+ /// are allowed to take up in memory. If max_file_size is null, then the contents
+ /// will not be loaded into memory.
+ ///
+ /// Returns the index of the entry in the `files` array list. You can use it
+ /// to access the contents of the file after calling `hit()` like so:
+ ///
+ /// ```
+ /// var file_contents = cache_hash.files.items[file_index].contents.?;
+ /// ```
+ pub fn addFile(self: *CacheHash, file_path: []const u8, max_file_size: ?usize) !usize {
+ assert(self.manifest_file == null);
+
+ try self.files.ensureCapacity(self.cache.gpa, self.files.items.len + 1);
+ const resolved_path = try fs.path.resolve(self.cache.gpa, &[_][]const u8{file_path});
+
+ const idx = self.files.items.len;
+ self.files.addOneAssumeCapacity().* = .{
+ .path = resolved_path,
+ .contents = null,
+ .max_file_size = max_file_size,
+ .stat = undefined,
+ .bin_digest = undefined,
+ };
+
+ self.hash.addBytes(resolved_path);
+
+ return idx;
+ }
+
+ pub fn addOptionalFile(self: *CacheHash, optional_file_path: ?[]const u8) !void {
+ self.hash.add(optional_file_path != null);
+ const file_path = optional_file_path orelse return;
+ _ = try self.addFile(file_path, null);
+ }
+
+ pub fn addListOfFiles(self: *CacheHash, list_of_files: []const []const u8) !void {
+ self.hash.add(list_of_files.len);
+ for (list_of_files) |file_path| {
+ _ = try self.addFile(file_path, null);
+ }
+ }
+
+ /// Check the cache to see if the input exists in it. If it exists, returns `true`.
+ /// A hex encoding of its hash is available by calling `final`.
+ ///
+ /// This function will also acquire an exclusive lock to the manifest file. This means
+ /// that a process holding a CacheHash will block any other process attempting to
+ /// acquire the lock.
+ ///
+ /// The lock on the manifest file is released when `deinit` is called. As another
+ /// option, one may call `toOwnedLock` to obtain a smaller object which can represent
+ /// the lock. `deinit` is safe to call whether or not `toOwnedLock` has been called.
+ pub fn hit(self: *CacheHash) !bool {
+ assert(self.manifest_file == null);
+
+ const ext = ".txt";
+ var manifest_file_path: [self.hex_digest.len + ext.len]u8 = undefined;
+
+ var bin_digest: [bin_digest_len]u8 = undefined;
+ self.hash.hasher.final(&bin_digest);
+
+ _ = std.fmt.bufPrint(&self.hex_digest, "{x}", .{bin_digest}) catch unreachable;
+
+ self.hash.hasher = hasher_init;
+ self.hash.hasher.update(&bin_digest);
+
+ mem.copy(u8, &manifest_file_path, &self.hex_digest);
+ manifest_file_path[self.hex_digest.len..][0..ext.len].* = ext.*;
+
+ if (self.files.items.len != 0) {
+ self.manifest_file = try self.cache.manifest_dir.createFile(&manifest_file_path, .{
+ .read = true,
+ .truncate = false,
+ .lock = .Exclusive,
+ });
+ } else {
+ // If there are no file inputs, we check if the manifest file exists instead of
+ // comparing the hashes on the files used for the cached item
+ self.manifest_file = self.cache.manifest_dir.openFile(&manifest_file_path, .{
+ .read = true,
+ .write = true,
+ .lock = .Exclusive,
+ }) catch |err| switch (err) {
+ error.FileNotFound => {
+ self.manifest_dirty = true;
+ self.manifest_file = try self.cache.manifest_dir.createFile(&manifest_file_path, .{
+ .read = true,
+ .truncate = false,
+ .lock = .Exclusive,
+ });
+ return false;
+ },
+ else => |e| return e,
+ };
+ }
+
+ const file_contents = try self.manifest_file.?.inStream().readAllAlloc(self.cache.gpa, manifest_file_size_max);
+ defer self.cache.gpa.free(file_contents);
+
+ const input_file_count = self.files.items.len;
+ var any_file_changed = false;
+ var line_iter = mem.tokenize(file_contents, "\n");
+ var idx: usize = 0;
+ while (line_iter.next()) |line| {
+ defer idx += 1;
+
+ const cache_hash_file = if (idx < input_file_count) &self.files.items[idx] else blk: {
+ const new = try self.files.addOne(self.cache.gpa);
+ new.* = .{
+ .path = null,
+ .contents = null,
+ .max_file_size = null,
+ .stat = undefined,
+ .bin_digest = undefined,
+ };
+ break :blk new;
+ };
+
+ var iter = mem.tokenize(line, " ");
+ const size = iter.next() orelse return error.InvalidFormat;
+ const inode = iter.next() orelse return error.InvalidFormat;
+ const mtime_nsec_str = iter.next() orelse return error.InvalidFormat;
+ const digest_str = iter.next() orelse return error.InvalidFormat;
+ const file_path = iter.rest();
+
+ cache_hash_file.stat.size = fmt.parseInt(u64, size, 10) catch return error.InvalidFormat;
+ cache_hash_file.stat.inode = fmt.parseInt(fs.File.INode, inode, 10) catch return error.InvalidFormat;
+ cache_hash_file.stat.mtime = fmt.parseInt(i64, mtime_nsec_str, 10) catch return error.InvalidFormat;
+ std.fmt.hexToBytes(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat;
+
+ if (file_path.len == 0) {
+ return error.InvalidFormat;
+ }
+ if (cache_hash_file.path) |p| {
+ if (!mem.eql(u8, file_path, p)) {
+ return error.InvalidFormat;
+ }
+ }
+
+ if (cache_hash_file.path == null) {
+ cache_hash_file.path = try self.cache.gpa.dupe(u8, file_path);
+ }
+
+ const this_file = fs.cwd().openFile(cache_hash_file.path.?, .{ .read = true }) catch {
+ return error.CacheUnavailable;
+ };
+ defer this_file.close();
+
+ const actual_stat = try this_file.stat();
+ const size_match = actual_stat.size == cache_hash_file.stat.size;
+ const mtime_match = actual_stat.mtime == cache_hash_file.stat.mtime;
+ const inode_match = actual_stat.inode == cache_hash_file.stat.inode;
+
+ if (!size_match or !mtime_match or !inode_match) {
+ self.manifest_dirty = true;
+
+ cache_hash_file.stat = actual_stat;
+
+ if (isProblematicTimestamp(cache_hash_file.stat.mtime)) {
+ cache_hash_file.stat.mtime = 0;
+ cache_hash_file.stat.inode = 0;
+ }
+
+ var actual_digest: [bin_digest_len]u8 = undefined;
+ try hashFile(this_file, &actual_digest);
+
+ if (!mem.eql(u8, &cache_hash_file.bin_digest, &actual_digest)) {
+ cache_hash_file.bin_digest = actual_digest;
+ // keep going until we have the input file digests
+ any_file_changed = true;
+ }
+ }
+
+ if (!any_file_changed) {
+ self.hash.hasher.update(&cache_hash_file.bin_digest);
+ }
+ }
+
+ if (any_file_changed) {
+ // cache miss
+ // keep the manifest file open
+ // reset the hash
+ self.hash.hasher = hasher_init;
+ self.hash.hasher.update(&bin_digest);
+
+ // Remove files not in the initial hash
+ for (self.files.items[input_file_count..]) |*file| {
+ file.deinit(self.cache.gpa);
+ }
+ self.files.shrinkRetainingCapacity(input_file_count);
+
+ for (self.files.items) |file| {
+ self.hash.hasher.update(&file.bin_digest);
+ }
+ return false;
+ }
+
+ if (idx < input_file_count) {
+ self.manifest_dirty = true;
+ while (idx < input_file_count) : (idx += 1) {
+ const ch_file = &self.files.items[idx];
+ try self.populateFileHash(ch_file);
+ }
+ return false;
+ }
+
+ return true;
+ }
+
+ fn populateFileHash(self: *CacheHash, ch_file: *File) !void {
+ const file = try fs.cwd().openFile(ch_file.path.?, .{});
+ defer file.close();
+
+ ch_file.stat = try file.stat();
+
+ if (isProblematicTimestamp(ch_file.stat.mtime)) {
+ ch_file.stat.mtime = 0;
+ ch_file.stat.inode = 0;
+ }
+
+ if (ch_file.max_file_size) |max_file_size| {
+ if (ch_file.stat.size > max_file_size) {
+ return error.FileTooBig;
+ }
+
+ const contents = try self.cache.gpa.alloc(u8, @intCast(usize, ch_file.stat.size));
+ errdefer self.cache.gpa.free(contents);
+
+ // Hash while reading from disk, to keep the contents in the cpu cache while
+ // doing hashing.
+ var hasher = hasher_init;
+ var off: usize = 0;
+ while (true) {
+ // give me everything you've got, captain
+ const bytes_read = try file.read(contents[off..]);
+ if (bytes_read == 0) break;
+ hasher.update(contents[off..][0..bytes_read]);
+ off += bytes_read;
+ }
+ hasher.final(&ch_file.bin_digest);
+
+ ch_file.contents = contents;
+ } else {
+ try hashFile(file, &ch_file.bin_digest);
+ }
+
+ self.hash.hasher.update(&ch_file.bin_digest);
+ }
+
+ /// Add a file as a dependency of process being cached, after the initial hash has been
+ /// calculated. This is useful for processes that don't know the all the files that
+ /// are depended on ahead of time. For example, a source file that can import other files
+ /// will need to be recompiled if the imported file is changed.
+ pub fn addFilePostFetch(self: *CacheHash, file_path: []const u8, max_file_size: usize) ![]const u8 {
+ assert(self.manifest_file != null);
+
+ const resolved_path = try fs.path.resolve(self.cache.gpa, &[_][]const u8{file_path});
+ errdefer self.cache.gpa.free(resolved_path);
+
+ const new_ch_file = try self.files.addOne(self.cache.gpa);
+ new_ch_file.* = .{
+ .path = resolved_path,
+ .max_file_size = max_file_size,
+ .stat = undefined,
+ .bin_digest = undefined,
+ .contents = null,
+ };
+ errdefer self.files.shrinkRetainingCapacity(self.files.items.len - 1);
+
+ try self.populateFileHash(new_ch_file);
+
+ return new_ch_file.contents.?;
+ }
+
+ /// Add a file as a dependency of process being cached, after the initial hash has been
+ /// calculated. This is useful for processes that don't know the all the files that
+ /// are depended on ahead of time. For example, a source file that can import other files
+ /// will need to be recompiled if the imported file is changed.
+ pub fn addFilePost(self: *CacheHash, file_path: []const u8) !void {
+ assert(self.manifest_file != null);
+
+ const resolved_path = try fs.path.resolve(self.cache.gpa, &[_][]const u8{file_path});
+ errdefer self.cache.gpa.free(resolved_path);
+
+ const new_ch_file = try self.files.addOne(self.cache.gpa);
+ new_ch_file.* = .{
+ .path = resolved_path,
+ .max_file_size = null,
+ .stat = undefined,
+ .bin_digest = undefined,
+ .contents = null,
+ };
+ errdefer self.files.shrinkRetainingCapacity(self.files.items.len - 1);
+
+ try self.populateFileHash(new_ch_file);
+ }
+
+ pub fn addDepFilePost(self: *CacheHash, dir: fs.Dir, dep_file_basename: []const u8) !void {
+ assert(self.manifest_file != null);
+
+ const dep_file_contents = try dir.readFileAlloc(self.cache.gpa, dep_file_basename, manifest_file_size_max);
+ defer self.cache.gpa.free(dep_file_contents);
+
+ var error_buf = std.ArrayList(u8).init(self.cache.gpa);
+ defer error_buf.deinit();
+
+ var it: @import("DepTokenizer.zig") = .{ .bytes = dep_file_contents };
+
+ // Skip first token: target.
+ switch (it.next() orelse return) { // Empty dep file OK.
+ .target, .target_must_resolve, .prereq => {},
+ else => |err| {
+ try err.printError(error_buf.writer());
+ std.log.err("failed parsing {}: {}", .{ dep_file_basename, error_buf.items });
+ return error.InvalidDepFile;
+ },
+ }
+ // Process 0+ preqreqs.
+ // Clang is invoked in single-source mode so we never get more targets.
+ while (true) {
+ switch (it.next() orelse return) {
+ .target, .target_must_resolve => return,
+ .prereq => |bytes| try self.addFilePost(bytes),
+ else => |err| {
+ try err.printError(error_buf.writer());
+ std.log.err("failed parsing {}: {}", .{ dep_file_basename, error_buf.items });
+ return error.InvalidDepFile;
+ },
+ }
+ }
+ }
+
+ /// Returns a hex encoded hash of the inputs.
+ pub fn final(self: *CacheHash) [hex_digest_len]u8 {
+ assert(self.manifest_file != null);
+
+ // We don't close the manifest file yet, because we want to
+ // keep it locked until the API user is done using it.
+ // We also don't write out the manifest yet, because until
+ // cache_release is called we still might be working on creating
+ // the artifacts to cache.
+
+ var bin_digest: [bin_digest_len]u8 = undefined;
+ self.hash.hasher.final(&bin_digest);
+
+ var out_digest: [hex_digest_len]u8 = undefined;
+ _ = std.fmt.bufPrint(&out_digest, "{x}", .{bin_digest}) catch unreachable;
+
+ return out_digest;
+ }
+
+ pub fn writeManifest(self: *CacheHash) !void {
+ assert(self.manifest_file != null);
+ if (!self.manifest_dirty) return;
+
+ var encoded_digest: [hex_digest_len]u8 = undefined;
+ var contents = std.ArrayList(u8).init(self.cache.gpa);
+ var writer = contents.writer();
+ defer contents.deinit();
+
+ for (self.files.items) |file| {
+ _ = std.fmt.bufPrint(&encoded_digest, "{x}", .{file.bin_digest}) catch unreachable;
+ try writer.print("{d} {d} {d} {s} {s}\n", .{
+ file.stat.size,
+ file.stat.inode,
+ file.stat.mtime,
+ &encoded_digest,
+ file.path,
+ });
+ }
+
+ try self.manifest_file.?.pwriteAll(contents.items, 0);
+ self.manifest_dirty = false;
+ }
+
+ /// Obtain only the data needed to maintain a lock on the manifest file.
+ /// The `CacheHash` remains safe to deinit.
+ /// Don't forget to call `writeManifest` before this!
+ pub fn toOwnedLock(self: *CacheHash) Lock {
+ const manifest_file = self.manifest_file.?;
+ self.manifest_file = null;
+ return Lock{ .manifest_file = manifest_file };
+ }
+
+ /// Releases the manifest file and frees any memory the CacheHash was using.
+ /// `CacheHash.hit` must be called first.
+ /// Don't forget to call `writeManifest` before this!
+ pub fn deinit(self: *CacheHash) void {
+ if (self.manifest_file) |file| {
+ file.close();
+ }
+ for (self.files.items) |*file| {
+ file.deinit(self.cache.gpa);
+ }
+ self.files.deinit(self.cache.gpa);
+ }
+};
+
+fn hashFile(file: fs.File, bin_digest: []u8) !void {
+ var buf: [1024]u8 = undefined;
+
+ var hasher = hasher_init;
+ while (true) {
+ const bytes_read = try file.read(&buf);
+ if (bytes_read == 0) break;
+ hasher.update(buf[0..bytes_read]);
+ }
+
+ hasher.final(bin_digest);
+}
+
+/// If the wall clock time, rounded to the same precision as the
+/// mtime, is equal to the mtime, then we cannot rely on this mtime
+/// yet. We will instead save an mtime value that indicates the hash
+/// must be unconditionally computed.
+/// This function recognizes the precision of mtime by looking at trailing
+/// zero bits of the seconds and nanoseconds.
+fn isProblematicTimestamp(fs_clock: i128) bool {
+ const wall_clock = std.time.nanoTimestamp();
+
+ // We have to break the nanoseconds into seconds and remainder nanoseconds
+ // to detect precision of seconds, because looking at the zero bits in base
+ // 2 would not detect precision of the seconds value.
+ const fs_sec = @intCast(i64, @divFloor(fs_clock, std.time.ns_per_s));
+ const fs_nsec = @intCast(i64, @mod(fs_clock, std.time.ns_per_s));
+ var wall_sec = @intCast(i64, @divFloor(wall_clock, std.time.ns_per_s));
+ var wall_nsec = @intCast(i64, @mod(wall_clock, std.time.ns_per_s));
+
+ // First make all the least significant zero bits in the fs_clock, also zero bits in the wall clock.
+ if (fs_nsec == 0) {
+ wall_nsec = 0;
+ if (fs_sec == 0) {
+ wall_sec = 0;
+ } else {
+ wall_sec &= @as(i64, -1) << @intCast(u6, @ctz(i64, fs_sec));
+ }
+ } else {
+ wall_nsec &= @as(i64, -1) << @intCast(u6, @ctz(i64, fs_nsec));
+ }
+ return wall_nsec == fs_nsec and wall_sec == fs_sec;
+}
+
+test "cache file and then recall it" {
+ if (std.Target.current.os.tag == .wasi) {
+ // https://github.com/ziglang/zig/issues/5437
+ return error.SkipZigTest;
+ }
+ const cwd = fs.cwd();
+
+ const temp_file = "test.txt";
+ const temp_manifest_dir = "temp_manifest_dir";
+
+ const ts = std.time.nanoTimestamp();
+ try cwd.writeFile(temp_file, "Hello, world!\n");
+
+ while (isProblematicTimestamp(ts)) {
+ std.time.sleep(1);
+ }
+
+ var digest1: [hex_digest_len]u8 = undefined;
+ var digest2: [hex_digest_len]u8 = undefined;
+
+ {
+ var cache = Cache{
+ .gpa = testing.allocator,
+ .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),
+ };
+ defer cache.manifest_dir.close();
+
+ {
+ var ch = cache.obtain();
+ defer ch.deinit();
+
+ ch.hash.add(true);
+ ch.hash.add(@as(u16, 1234));
+ ch.hash.addBytes("1234");
+ _ = try ch.addFile(temp_file, null);
+
+ // There should be nothing in the cache
+ testing.expectEqual(false, try ch.hit());
+
+ digest1 = ch.final();
+ try ch.writeManifest();
+ }
+ {
+ var ch = cache.obtain();
+ defer ch.deinit();
+
+ ch.hash.add(true);
+ ch.hash.add(@as(u16, 1234));
+ ch.hash.addBytes("1234");
+ _ = try ch.addFile(temp_file, null);
+
+ // Cache hit! We just "built" the same file
+ testing.expect(try ch.hit());
+ digest2 = ch.final();
+
+ try ch.writeManifest();
+ }
+
+ testing.expectEqual(digest1, digest2);
+ }
+
+ try cwd.deleteTree(temp_manifest_dir);
+ try cwd.deleteFile(temp_file);
+}
+
+test "give problematic timestamp" {
+ var fs_clock = std.time.nanoTimestamp();
+ // to make it problematic, we make it only accurate to the second
+ fs_clock = @divTrunc(fs_clock, std.time.ns_per_s);
+ fs_clock *= std.time.ns_per_s;
+ testing.expect(isProblematicTimestamp(fs_clock));
+}
+
+test "give nonproblematic timestamp" {
+ testing.expect(!isProblematicTimestamp(std.time.nanoTimestamp() - std.time.ns_per_s));
+}
+
+test "check that changing a file makes cache fail" {
+ if (std.Target.current.os.tag == .wasi) {
+ // https://github.com/ziglang/zig/issues/5437
+ return error.SkipZigTest;
+ }
+ const cwd = fs.cwd();
+
+ const temp_file = "cache_hash_change_file_test.txt";
+ const temp_manifest_dir = "cache_hash_change_file_manifest_dir";
+ const original_temp_file_contents = "Hello, world!\n";
+ const updated_temp_file_contents = "Hello, world; but updated!\n";
+
+ try cwd.deleteTree(temp_manifest_dir);
+ try cwd.deleteTree(temp_file);
+
+ const ts = std.time.nanoTimestamp();
+ try cwd.writeFile(temp_file, original_temp_file_contents);
+
+ while (isProblematicTimestamp(ts)) {
+ std.time.sleep(1);
+ }
+
+ var digest1: [hex_digest_len]u8 = undefined;
+ var digest2: [hex_digest_len]u8 = undefined;
+
+ {
+ var cache = Cache{
+ .gpa = testing.allocator,
+ .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),
+ };
+ defer cache.manifest_dir.close();
+
+ {
+ var ch = cache.obtain();
+ defer ch.deinit();
+
+ ch.hash.addBytes("1234");
+ const temp_file_idx = try ch.addFile(temp_file, 100);
+
+ // There should be nothing in the cache
+ testing.expectEqual(false, try ch.hit());
+
+ testing.expect(mem.eql(u8, original_temp_file_contents, ch.files.items[temp_file_idx].contents.?));
+
+ digest1 = ch.final();
+
+ try ch.writeManifest();
+ }
+
+ try cwd.writeFile(temp_file, updated_temp_file_contents);
+
+ {
+ var ch = cache.obtain();
+ defer ch.deinit();
+
+ ch.hash.addBytes("1234");
+ const temp_file_idx = try ch.addFile(temp_file, 100);
+
+ // A file that we depend on has been updated, so the cache should not contain an entry for it
+ testing.expectEqual(false, try ch.hit());
+
+ // The cache system does not keep the contents of re-hashed input files.
+ testing.expect(ch.files.items[temp_file_idx].contents == null);
+
+ digest2 = ch.final();
+
+ try ch.writeManifest();
+ }
+
+ testing.expect(!mem.eql(u8, digest1[0..], digest2[0..]));
+ }
+
+ try cwd.deleteTree(temp_manifest_dir);
+ try cwd.deleteTree(temp_file);
+}
+
+test "no file inputs" {
+ if (std.Target.current.os.tag == .wasi) {
+ // https://github.com/ziglang/zig/issues/5437
+ return error.SkipZigTest;
+ }
+ const cwd = fs.cwd();
+ const temp_manifest_dir = "no_file_inputs_manifest_dir";
+ defer cwd.deleteTree(temp_manifest_dir) catch {};
+
+ var digest1: [hex_digest_len]u8 = undefined;
+ var digest2: [hex_digest_len]u8 = undefined;
+
+ var cache = Cache{
+ .gpa = testing.allocator,
+ .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),
+ };
+ defer cache.manifest_dir.close();
+
+ {
+ var ch = cache.obtain();
+ defer ch.deinit();
+
+ ch.hash.addBytes("1234");
+
+ // There should be nothing in the cache
+ testing.expectEqual(false, try ch.hit());
+
+ digest1 = ch.final();
+
+ try ch.writeManifest();
+ }
+ {
+ var ch = cache.obtain();
+ defer ch.deinit();
+
+ ch.hash.addBytes("1234");
+
+ testing.expect(try ch.hit());
+ digest2 = ch.final();
+ try ch.writeManifest();
+ }
+
+ testing.expectEqual(digest1, digest2);
+}
+
+test "CacheHashes with files added after initial hash work" {
+ if (std.Target.current.os.tag == .wasi) {
+ // https://github.com/ziglang/zig/issues/5437
+ return error.SkipZigTest;
+ }
+ const cwd = fs.cwd();
+
+ const temp_file1 = "cache_hash_post_file_test1.txt";
+ const temp_file2 = "cache_hash_post_file_test2.txt";
+ const temp_manifest_dir = "cache_hash_post_file_manifest_dir";
+
+ const ts1 = std.time.nanoTimestamp();
+ try cwd.writeFile(temp_file1, "Hello, world!\n");
+ try cwd.writeFile(temp_file2, "Hello world the second!\n");
+
+ while (isProblematicTimestamp(ts1)) {
+ std.time.sleep(1);
+ }
+
+ var digest1: [hex_digest_len]u8 = undefined;
+ var digest2: [hex_digest_len]u8 = undefined;
+ var digest3: [hex_digest_len]u8 = undefined;
+
+ {
+ var cache = Cache{
+ .gpa = testing.allocator,
+ .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}),
+ };
+ defer cache.manifest_dir.close();
+
+ {
+ var ch = cache.obtain();
+ defer ch.deinit();
+
+ ch.hash.addBytes("1234");
+ _ = try ch.addFile(temp_file1, null);
+
+ // There should be nothing in the cache
+ testing.expectEqual(false, try ch.hit());
+
+ _ = try ch.addFilePost(temp_file2);
+
+ digest1 = ch.final();
+ try ch.writeManifest();
+ }
+ {
+ var ch = cache.obtain();
+ defer ch.deinit();
+
+ ch.hash.addBytes("1234");
+ _ = try ch.addFile(temp_file1, null);
+
+ testing.expect(try ch.hit());
+ digest2 = ch.final();
+
+ try ch.writeManifest();
+ }
+ testing.expect(mem.eql(u8, &digest1, &digest2));
+
+ // Modify the file added after initial hash
+ const ts2 = std.time.nanoTimestamp();
+ try cwd.writeFile(temp_file2, "Hello world the second, updated\n");
+
+ while (isProblematicTimestamp(ts2)) {
+ std.time.sleep(1);
+ }
+
+ {
+ var ch = cache.obtain();
+ defer ch.deinit();
+
+ ch.hash.addBytes("1234");
+ _ = try ch.addFile(temp_file1, null);
+
+ // A file that we depend on has been updated, so the cache should not contain an entry for it
+ testing.expectEqual(false, try ch.hit());
+
+ _ = try ch.addFilePost(temp_file2);
+
+ digest3 = ch.final();
+
+ try ch.writeManifest();
+ }
+
+ testing.expect(!mem.eql(u8, &digest1, &digest3));
+ }
+
+ try cwd.deleteTree(temp_manifest_dir);
+ try cwd.deleteFile(temp_file1);
+ try cwd.deleteFile(temp_file2);
+}
diff --git a/src/Compilation.zig b/src/Compilation.zig
new file mode 100644
index 0000000000000000000000000000000000000000..29c6dc36cf9c4cc7e4202e473dcfb4e5cc5c289f
--- /dev/null
+++ b/src/Compilation.zig
@@ -0,0 +1,2246 @@
+const Compilation = @This();
+
+const std = @import("std");
+const mem = std.mem;
+const Allocator = std.mem.Allocator;
+const Value = @import("value.zig").Value;
+const assert = std.debug.assert;
+const log = std.log.scoped(.compilation);
+const Target = std.Target;
+const target_util = @import("target.zig");
+const Package = @import("Package.zig");
+const link = @import("link.zig");
+const trace = @import("tracy.zig").trace;
+const liveness = @import("liveness.zig");
+const build_options = @import("build_options");
+const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
+const glibc = @import("glibc.zig");
+const libunwind = @import("libunwind.zig");
+const fatal = @import("main.zig").fatal;
+const Module = @import("Module.zig");
+const Cache = @import("Cache.zig");
+const stage1 = @import("stage1.zig");
+
+/// General-purpose allocator. Used for both temporary and long-term storage.
+gpa: *Allocator,
+/// Arena-allocated memory used during initialization. Should be untouched until deinit.
+arena_state: std.heap.ArenaAllocator.State,
+bin_file: *link.File,
+c_object_table: std.AutoArrayHashMapUnmanaged(*CObject, void) = .{},
+stage1_lock: ?Cache.Lock = null,
+stage1_cache_hash: *Cache.CacheHash = undefined,
+
+link_error_flags: link.File.ErrorFlags = .{},
+
+work_queue: std.fifo.LinearFifo(Job, .Dynamic),
+
+/// The ErrorMsg memory is owned by the `CObject`, using Compilation's general purpose allocator.
+failed_c_objects: std.AutoArrayHashMapUnmanaged(*CObject, *ErrorMsg) = .{},
+
+keep_source_files_loaded: bool,
+use_clang: bool,
+sanitize_c: bool,
+/// When this is `true` it means invoking clang as a sub-process is expected to inherit
+/// stdin, stdout, stderr, and if it returns non success, to forward the exit code.
+/// Otherwise we attempt to parse the error messages and expose them via the Compilation API.
+/// This is `true` for `zig cc`, `zig c++`, and `zig translate-c`.
+clang_passthrough_mode: bool,
+/// Whether to print clang argvs to stdout.
+verbose_cc: bool,
+verbose_tokenize: bool,
+verbose_ast: bool,
+verbose_ir: bool,
+verbose_llvm_ir: bool,
+verbose_cimport: bool,
+verbose_llvm_cpu_features: bool,
+disable_c_depfile: bool,
+is_test: bool,
+time_report: bool,
+
+c_source_files: []const CSourceFile,
+clang_argv: []const []const u8,
+cache_parent: *Cache,
+/// Path to own executable for invoking `zig clang`.
+self_exe_path: ?[]const u8,
+zig_lib_directory: Directory,
+zig_cache_directory: Directory,
+libc_include_dir_list: []const []const u8,
+rand: *std.rand.Random,
+
+/// Populated when we build the libc++ static library. A Job to build this is placed in the queue
+/// and resolved before calling linker.flush().
+libcxx_static_lib: ?[]const u8 = null,
+/// Populated when we build the libc++abi static library. A Job to build this is placed in the queue
+/// and resolved before calling linker.flush().
+libcxxabi_static_lib: ?[]const u8 = null,
+/// Populated when we build the libunwind static library. A Job to build this is placed in the queue
+/// and resolved before calling linker.flush().
+libunwind_static_lib: ?CRTFile = null,
+/// Populated when we build the libc static library. A Job to build this is placed in the queue
+/// and resolved before calling linker.flush().
+libc_static_lib: ?CRTFile = null,
+/// Populated when we build the libcompiler_rt static library. A Job to build this is placed in the queue
+/// and resolved before calling linker.flush().
+compiler_rt_static_lib: ?CRTFile = null,
+
+glibc_so_files: ?glibc.BuiltSharedObjects = null,
+
+/// For example `Scrt1.o` and `libc_nonshared.a`. These are populated after building libc from source,
+/// The set of needed CRT (C runtime) files differs depending on the target and compilation settings.
+/// The key is the basename, and the value is the absolute path to the completed build artifact.
+crt_files: std.StringHashMapUnmanaged(CRTFile) = .{},
+
+/// Keeping track of this possibly open resource so we can close it later.
+owned_link_dir: ?std.fs.Dir,
+
+/// This is for stage1 and should be deleted upon completion of self-hosting.
+/// Don't use this for anything other than stage1 compatibility.
+color: @import("main.zig").Color = .Auto,
+
+pub const InnerError = Module.InnerError;
+
+pub const CRTFile = struct {
+ lock: Cache.Lock,
+ full_object_path: []const u8,
+
+ fn deinit(self: *CRTFile, gpa: *Allocator) void {
+ self.lock.release();
+ gpa.free(self.full_object_path);
+ self.* = undefined;
+ }
+};
+
+/// For passing to a C compiler.
+pub const CSourceFile = struct {
+ src_path: []const u8,
+ extra_flags: []const []const u8 = &[0][]const u8{},
+};
+
+const Job = union(enum) {
+ /// Write the machine code for a Decl to the output file.
+ codegen_decl: *Module.Decl,
+ /// The Decl needs to be analyzed and possibly export itself.
+ /// It may have already be analyzed, or it may have been determined
+ /// to be outdated; in this case perform semantic analysis again.
+ analyze_decl: *Module.Decl,
+ /// The source file containing the Decl has been updated, and so the
+ /// Decl may need its line number information updated in the debug info.
+ update_line_number: *Module.Decl,
+ /// Invoke the Clang compiler to create an object file, which gets linked
+ /// with the Compilation.
+ c_object: *CObject,
+
+ /// one of the glibc static objects
+ glibc_crt_file: glibc.CRTFile,
+ /// all of the glibc shared objects
+ glibc_shared_objects,
+ /// libunwind.a, usually needed when linking libc
+ libunwind: void,
+ /// needed when producing a dynamic library or executable
+ libcompiler_rt: void,
+ /// needed when not linking libc and using LLVM for code generation because it generates
+ /// calls to, for example, memcpy and memset.
+ zig_libc: void,
+
+ /// Generate builtin.zig source code and write it into the correct place.
+ generate_builtin_zig: void,
+ /// Use stage1 C++ code to compile zig code into an object file.
+ stage1_module: void,
+};
+
+pub const CObject = struct {
+ /// Relative to cwd. Owned by arena.
+ src: CSourceFile,
+ status: union(enum) {
+ new,
+ success: struct {
+ /// The outputted result. Owned by gpa.
+ object_path: []u8,
+ /// This is a file system lock on the cache hash manifest representing this
+ /// object. It prevents other invocations of the Zig compiler from interfering
+ /// with this object until released.
+ lock: Cache.Lock,
+ },
+ /// There will be a corresponding ErrorMsg in Compilation.failed_c_objects.
+ failure,
+ },
+
+ /// Returns if there was failure.
+ pub fn clearStatus(self: *CObject, gpa: *Allocator) bool {
+ switch (self.status) {
+ .new => return false,
+ .failure => {
+ self.status = .new;
+ return true;
+ },
+ .success => |*success| {
+ gpa.free(success.object_path);
+ success.lock.release();
+ self.status = .new;
+ return false;
+ },
+ }
+ }
+
+ pub fn destroy(self: *CObject, gpa: *Allocator) void {
+ _ = self.clearStatus(gpa);
+ gpa.destroy(self);
+ }
+};
+
+pub const AllErrors = struct {
+ arena: std.heap.ArenaAllocator.State,
+ list: []const Message,
+
+ pub const Message = struct {
+ src_path: []const u8,
+ line: usize,
+ column: usize,
+ byte_offset: usize,
+ msg: []const u8,
+
+ pub fn renderToStdErr(self: Message) void {
+ std.debug.print("{}:{}:{}: error: {}\n", .{
+ self.src_path,
+ self.line + 1,
+ self.column + 1,
+ self.msg,
+ });
+ }
+ };
+
+ pub fn deinit(self: *AllErrors, gpa: *Allocator) void {
+ self.arena.promote(gpa).deinit();
+ }
+
+ fn add(
+ arena: *std.heap.ArenaAllocator,
+ errors: *std.ArrayList(Message),
+ sub_file_path: []const u8,
+ source: []const u8,
+ simple_err_msg: ErrorMsg,
+ ) !void {
+ const loc = std.zig.findLineColumn(source, simple_err_msg.byte_offset);
+ try errors.append(.{
+ .src_path = try arena.allocator.dupe(u8, sub_file_path),
+ .msg = try arena.allocator.dupe(u8, simple_err_msg.msg),
+ .byte_offset = simple_err_msg.byte_offset,
+ .line = loc.line,
+ .column = loc.column,
+ });
+ }
+};
+
+pub const Directory = struct {
+ /// This field is redundant for operations that can act on the open directory handle
+ /// directly, but it is needed when passing the directory to a child process.
+ /// `null` means cwd.
+ path: ?[]const u8,
+ handle: std.fs.Dir,
+
+ pub fn join(self: Directory, allocator: *Allocator, paths: []const []const u8) ![]u8 {
+ if (self.path) |p| {
+ // TODO clean way to do this with only 1 allocation
+ const part2 = try std.fs.path.join(allocator, paths);
+ defer allocator.free(part2);
+ return std.fs.path.join(allocator, &[_][]const u8{ p, part2 });
+ } else {
+ return std.fs.path.join(allocator, paths);
+ }
+ }
+};
+
+pub const EmitLoc = struct {
+ /// If this is `null` it means the file will be output to the cache directory.
+ /// When provided, both the open file handle and the path name must outlive the `Compilation`.
+ directory: ?Compilation.Directory,
+ /// This may not have sub-directories in it.
+ basename: []const u8,
+};
+
+pub const InitOptions = struct {
+ zig_lib_directory: Directory,
+ zig_cache_directory: Directory,
+ target: Target,
+ root_name: []const u8,
+ root_pkg: ?*Package,
+ output_mode: std.builtin.OutputMode,
+ rand: *std.rand.Random,
+ dynamic_linker: ?[]const u8 = null,
+ /// `null` means to not emit a binary file.
+ emit_bin: ?EmitLoc,
+ /// `null` means to not emit a C header file.
+ emit_h: ?EmitLoc = null,
+ link_mode: ?std.builtin.LinkMode = null,
+ dll_export_fns: ?bool = false,
+ object_format: ?std.builtin.ObjectFormat = null,
+ optimize_mode: std.builtin.Mode = .Debug,
+ keep_source_files_loaded: bool = false,
+ clang_argv: []const []const u8 = &[0][]const u8{},
+ lld_argv: []const []const u8 = &[0][]const u8{},
+ lib_dirs: []const []const u8 = &[0][]const u8{},
+ rpath_list: []const []const u8 = &[0][]const u8{},
+ c_source_files: []const CSourceFile = &[0]CSourceFile{},
+ link_objects: []const []const u8 = &[0][]const u8{},
+ framework_dirs: []const []const u8 = &[0][]const u8{},
+ frameworks: []const []const u8 = &[0][]const u8{},
+ system_libs: []const []const u8 = &[0][]const u8{},
+ link_libc: bool = false,
+ link_libcpp: bool = false,
+ want_pic: ?bool = null,
+ want_sanitize_c: ?bool = null,
+ want_stack_check: ?bool = null,
+ want_valgrind: ?bool = null,
+ use_llvm: ?bool = null,
+ use_lld: ?bool = null,
+ use_clang: ?bool = null,
+ rdynamic: bool = false,
+ strip: bool = false,
+ single_threaded: bool = false,
+ is_native_os: bool,
+ time_report: bool = false,
+ link_eh_frame_hdr: bool = false,
+ linker_script: ?[]const u8 = null,
+ version_script: ?[]const u8 = null,
+ override_soname: ?[]const u8 = null,
+ linker_gc_sections: ?bool = null,
+ function_sections: ?bool = null,
+ linker_allow_shlib_undefined: ?bool = null,
+ linker_bind_global_refs_locally: ?bool = null,
+ disable_c_depfile: bool = false,
+ linker_z_nodelete: bool = false,
+ linker_z_defs: bool = false,
+ clang_passthrough_mode: bool = false,
+ verbose_cc: bool = false,
+ verbose_link: bool = false,
+ verbose_tokenize: bool = false,
+ verbose_ast: bool = false,
+ verbose_ir: bool = false,
+ verbose_llvm_ir: bool = false,
+ verbose_cimport: bool = false,
+ verbose_llvm_cpu_features: bool = false,
+ is_test: bool = false,
+ stack_size_override: ?u64 = null,
+ self_exe_path: ?[]const u8 = null,
+ version: ?std.builtin.Version = null,
+ libc_installation: ?*const LibCInstallation = null,
+ machine_code_model: std.builtin.CodeModel = .default,
+ /// This is for stage1 and should be deleted upon completion of self-hosting.
+ color: @import("main.zig").Color = .Auto,
+};
+
+pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
+ const is_dyn_lib = switch (options.output_mode) {
+ .Obj, .Exe => false,
+ .Lib => (options.link_mode orelse .Static) == .Dynamic,
+ };
+ const is_exe_or_dyn_lib = switch (options.output_mode) {
+ .Obj => false,
+ .Lib => is_dyn_lib,
+ .Exe => true,
+ };
+ const comp: *Compilation = comp: {
+ // For allocations that have the same lifetime as Compilation. This arena is used only during this
+ // initialization and then is freed in deinit().
+ var arena_allocator = std.heap.ArenaAllocator.init(gpa);
+ errdefer arena_allocator.deinit();
+ const arena = &arena_allocator.allocator;
+
+ // We put the `Compilation` itself in the arena. Freeing the arena will free the module.
+ // It's initialized later after we prepare the initialization options.
+ const comp = try arena.create(Compilation);
+ const root_name = try arena.dupe(u8, options.root_name);
+
+ const ofmt = options.object_format orelse options.target.getObjectFormat();
+
+ // Make a decision on whether to use LLD or our own linker.
+ const use_lld = if (options.use_lld) |explicit| explicit else blk: {
+ if (!build_options.have_llvm)
+ break :blk false;
+
+ if (ofmt == .c)
+ break :blk false;
+
+ // Our linker can't handle objects or most advanced options yet.
+ if (options.link_objects.len != 0 or
+ options.c_source_files.len != 0 or
+ options.frameworks.len != 0 or
+ options.system_libs.len != 0 or
+ options.link_libc or options.link_libcpp or
+ options.link_eh_frame_hdr or
+ options.output_mode == .Lib or
+ options.lld_argv.len != 0 or
+ options.linker_script != null or options.version_script != null)
+ {
+ break :blk true;
+ }
+
+ if (build_options.is_stage1) {
+ // If stage1 generates an object file, self-hosted linker is not
+ // yet sophisticated enough to handle that.
+ break :blk options.root_pkg != null;
+ }
+
+ break :blk false;
+ };
+
+ // Make a decision on whether to use LLVM or our own backend.
+ const use_llvm = if (options.use_llvm) |explicit| explicit else blk: {
+ // If we have no zig code to compile, no need for LLVM.
+ if (options.root_pkg == null)
+ break :blk false;
+
+ // If we are the stage1 compiler, we depend on the stage1 c++ llvm backend
+ // to compile zig code.
+ if (build_options.is_stage1)
+ break :blk true;
+
+ // We would want to prefer LLVM for release builds when it is available, however
+ // we don't have an LLVM backend yet :)
+ // We would also want to prefer LLVM for architectures that we don't have self-hosted support for too.
+ break :blk false;
+ };
+ if (!use_llvm and options.machine_code_model != .default) {
+ return error.MachineCodeModelNotSupported;
+ }
+
+ const must_dynamic_link = dl: {
+ if (target_util.cannotDynamicLink(options.target))
+ break :dl false;
+ if (target_util.osRequiresLibC(options.target))
+ break :dl true;
+ if (is_exe_or_dyn_lib and options.link_libc and options.target.isGnuLibC())
+ break :dl true;
+ if (options.system_libs.len != 0)
+ break :dl true;
+
+ break :dl false;
+ };
+ const default_link_mode: std.builtin.LinkMode = if (must_dynamic_link) .Dynamic else .Static;
+ const link_mode: std.builtin.LinkMode = if (options.link_mode) |lm| blk: {
+ if (lm == .Static and must_dynamic_link) {
+ return error.UnableToStaticLink;
+ }
+ break :blk lm;
+ } else default_link_mode;
+
+ const dll_export_fns = if (options.dll_export_fns) |explicit| explicit else is_dyn_lib;
+
+ const libc_dirs = try detectLibCIncludeDirs(
+ arena,
+ options.zig_lib_directory.path.?,
+ options.target,
+ options.is_native_os,
+ options.link_libc,
+ options.libc_installation,
+ );
+
+ const must_pic: bool = b: {
+ if (target_util.requiresPIC(options.target, options.link_libc))
+ break :b true;
+ break :b link_mode == .Dynamic;
+ };
+ const pic = if (options.want_pic) |explicit| pic: {
+ if (!explicit and must_pic) {
+ return error.TargetRequiresPIC;
+ }
+ break :pic explicit;
+ } else must_pic;
+
+ if (options.emit_h != null) fatal("-femit-h not supported yet", .{}); // TODO
+
+ const emit_bin = options.emit_bin orelse fatal("-fno-emit-bin not supported yet", .{}); // TODO
+
+ // Make a decision on whether to use Clang for translate-c and compiling C files.
+ const use_clang = if (options.use_clang) |explicit| explicit else blk: {
+ if (build_options.have_llvm) {
+ // Can't use it if we don't have it!
+ break :blk false;
+ }
+ // It's not planned to do our own translate-c or C compilation.
+ break :blk true;
+ };
+
+ const is_safe_mode = switch (options.optimize_mode) {
+ .Debug, .ReleaseSafe => true,
+ .ReleaseFast, .ReleaseSmall => false,
+ };
+
+ const sanitize_c = options.want_sanitize_c orelse is_safe_mode;
+
+ const stack_check: bool = b: {
+ if (!target_util.supportsStackProbing(options.target))
+ break :b false;
+ break :b options.want_stack_check orelse is_safe_mode;
+ };
+
+ const valgrind: bool = b: {
+ if (!target_util.hasValgrindSupport(options.target))
+ break :b false;
+ break :b options.want_valgrind orelse (options.optimize_mode == .Debug);
+ };
+
+ const single_threaded = options.single_threaded or target_util.isSingleThreaded(options.target);
+ const function_sections = options.function_sections orelse false;
+
+ const llvm_cpu_features: ?[*:0]const u8 = if (build_options.have_llvm and use_llvm) blk: {
+ var buf = std.ArrayList(u8).init(arena);
+ for (options.target.cpu.arch.allFeaturesList()) |feature, index_usize| {
+ const index = @intCast(Target.Cpu.Feature.Set.Index, index_usize);
+ const is_enabled = options.target.cpu.features.isEnabled(index);
+
+ if (feature.llvm_name) |llvm_name| {
+ const plus_or_minus = "-+"[@boolToInt(is_enabled)];
+ try buf.ensureCapacity(buf.items.len + 2 + llvm_name.len);
+ buf.appendAssumeCapacity(plus_or_minus);
+ buf.appendSliceAssumeCapacity(llvm_name);
+ buf.appendSliceAssumeCapacity(",");
+ }
+ }
+ assert(mem.endsWith(u8, buf.items, ","));
+ buf.items[buf.items.len - 1] = 0;
+ buf.shrink(buf.items.len);
+ break :blk buf.items[0 .. buf.items.len - 1 :0].ptr;
+ } else null;
+
+ // We put everything into the cache hash that *cannot be modified during an incremental update*.
+ // For example, one cannot change the target between updates, but one can change source files,
+ // so the target goes into the cache hash, but source files do not. This is so that we can
+ // find the same binary and incrementally update it even if there are modified source files.
+ // We do this even if outputting to the current directory because we need somewhere to store
+ // incremental compilation metadata.
+ const cache = try arena.create(Cache);
+ cache.* = .{
+ .gpa = gpa,
+ .manifest_dir = try options.zig_cache_directory.handle.makeOpenPath("h", .{}),
+ };
+ errdefer cache.manifest_dir.close();
+
+ // This is shared hasher state common to zig source and all C source files.
+ cache.hash.addBytes(build_options.version);
+ cache.hash.addBytes(options.zig_lib_directory.path orelse ".");
+ cache.hash.add(options.optimize_mode);
+ cache.hash.add(options.target.cpu.arch);
+ cache.hash.addBytes(options.target.cpu.model.name);
+ cache.hash.add(options.target.cpu.features.ints);
+ cache.hash.add(options.target.os.tag);
+ cache.hash.add(options.is_native_os);
+ cache.hash.add(options.target.abi);
+ cache.hash.add(ofmt);
+ cache.hash.add(pic);
+ cache.hash.add(stack_check);
+ cache.hash.add(link_mode);
+ cache.hash.add(function_sections);
+ cache.hash.add(options.strip);
+ cache.hash.add(options.link_libc);
+ cache.hash.add(options.link_libcpp);
+ cache.hash.add(options.output_mode);
+ cache.hash.add(options.machine_code_model);
+ // TODO audit this and make sure everything is in it
+
+ const module: ?*Module = if (options.root_pkg) |root_pkg| blk: {
+ // Options that are specific to zig source files, that cannot be
+ // modified between incremental updates.
+ var hash = cache.hash;
+
+ // Here we put the root source file path name, but *not* with addFile. We want the
+ // hash to be the same regardless of the contents of the source file, because
+ // incremental compilation will handle it, but we do want to namespace different
+ // source file names because they are likely different compilations and therefore this
+ // would be likely to cause cache hits.
+ hash.addBytes(root_pkg.root_src_path);
+ hash.addOptionalBytes(root_pkg.root_src_directory.path);
+ hash.add(valgrind);
+ hash.add(single_threaded);
+ hash.add(options.target.os.getVersionRange());
+ hash.add(dll_export_fns);
+
+ const digest = hash.final();
+ const artifact_sub_dir = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest });
+ var artifact_dir = try options.zig_cache_directory.handle.makeOpenPath(artifact_sub_dir, .{});
+ errdefer artifact_dir.close();
+ const zig_cache_artifact_directory: Directory = .{
+ .handle = artifact_dir,
+ .path = if (options.zig_cache_directory.path) |p|
+ try std.fs.path.join(arena, &[_][]const u8{ p, artifact_sub_dir })
+ else
+ artifact_sub_dir,
+ };
+
+ // TODO when we implement serialization and deserialization of incremental compilation metadata,
+ // this is where we would load it. We have open a handle to the directory where
+ // the output either already is, or will be.
+ // However we currently do not have serialization of such metadata, so for now
+ // we set up an empty Module that does the entire compilation fresh.
+
+ const root_scope = rs: {
+ if (mem.endsWith(u8, root_pkg.root_src_path, ".zig")) {
+ const root_scope = try gpa.create(Module.Scope.File);
+ root_scope.* = .{
+ .sub_file_path = root_pkg.root_src_path,
+ .source = .{ .unloaded = {} },
+ .contents = .{ .not_available = {} },
+ .status = .never_loaded,
+ .root_container = .{
+ .file_scope = root_scope,
+ .decls = .{},
+ },
+ };
+ break :rs &root_scope.base;
+ } else if (mem.endsWith(u8, root_pkg.root_src_path, ".zir")) {
+ const root_scope = try gpa.create(Module.Scope.ZIRModule);
+ root_scope.* = .{
+ .sub_file_path = root_pkg.root_src_path,
+ .source = .{ .unloaded = {} },
+ .contents = .{ .not_available = {} },
+ .status = .never_loaded,
+ .decls = .{},
+ };
+ break :rs &root_scope.base;
+ } else {
+ unreachable;
+ }
+ };
+
+ const module = try arena.create(Module);
+ module.* = .{
+ .gpa = gpa,
+ .comp = comp,
+ .root_pkg = root_pkg,
+ .root_scope = root_scope,
+ .zig_cache_artifact_directory = zig_cache_artifact_directory,
+ };
+ break :blk module;
+ } else null;
+ errdefer if (module) |zm| zm.deinit();
+
+ // For resource management purposes.
+ var owned_link_dir: ?std.fs.Dir = null;
+ errdefer if (owned_link_dir) |*dir| dir.close();
+
+ const bin_directory = emit_bin.directory orelse blk: {
+ if (module) |zm| break :blk zm.zig_cache_artifact_directory;
+
+ // We could use the cache hash as is no problem, however, we increase
+ // the likelihood of cache hits by adding the first C source file
+ // path name (not contents) to the hash. This way if the user is compiling
+ // foo.c and bar.c as separate compilations, they get different cache
+ // directories.
+ var hash = cache.hash;
+ if (options.c_source_files.len >= 1) {
+ hash.addBytes(options.c_source_files[0].src_path);
+ } else if (options.link_objects.len >= 1) {
+ hash.addBytes(options.link_objects[0]);
+ }
+
+ const digest = hash.final();
+ const artifact_sub_dir = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest });
+ var artifact_dir = try options.zig_cache_directory.handle.makeOpenPath(artifact_sub_dir, .{});
+ owned_link_dir = artifact_dir;
+ const link_artifact_directory: Directory = .{
+ .handle = artifact_dir,
+ .path = try options.zig_cache_directory.join(arena, &[_][]const u8{artifact_sub_dir}),
+ };
+ break :blk link_artifact_directory;
+ };
+
+ const error_return_tracing = !options.strip and switch (options.optimize_mode) {
+ .Debug, .ReleaseSafe => true,
+ .ReleaseFast, .ReleaseSmall => false,
+ };
+
+ const bin_file = try link.File.openPath(gpa, .{
+ .directory = bin_directory,
+ .sub_path = emit_bin.basename,
+ .root_name = root_name,
+ .module = module,
+ .target = options.target,
+ .dynamic_linker = options.dynamic_linker,
+ .output_mode = options.output_mode,
+ .link_mode = link_mode,
+ .object_format = ofmt,
+ .optimize_mode = options.optimize_mode,
+ .use_lld = use_lld,
+ .use_llvm = use_llvm,
+ .link_libc = options.link_libc,
+ .link_libcpp = options.link_libcpp,
+ .objects = options.link_objects,
+ .frameworks = options.frameworks,
+ .framework_dirs = options.framework_dirs,
+ .system_libs = options.system_libs,
+ .lib_dirs = options.lib_dirs,
+ .rpath_list = options.rpath_list,
+ .strip = options.strip,
+ .is_native_os = options.is_native_os,
+ .function_sections = options.function_sections orelse false,
+ .allow_shlib_undefined = options.linker_allow_shlib_undefined,
+ .bind_global_refs_locally = options.linker_bind_global_refs_locally orelse false,
+ .z_nodelete = options.linker_z_nodelete,
+ .z_defs = options.linker_z_defs,
+ .stack_size_override = options.stack_size_override,
+ .linker_script = options.linker_script,
+ .version_script = options.version_script,
+ .gc_sections = options.linker_gc_sections,
+ .eh_frame_hdr = options.link_eh_frame_hdr,
+ .rdynamic = options.rdynamic,
+ .extra_lld_args = options.lld_argv,
+ .override_soname = options.override_soname,
+ .version = options.version,
+ .libc_installation = libc_dirs.libc_installation,
+ .pic = pic,
+ .valgrind = valgrind,
+ .stack_check = stack_check,
+ .single_threaded = single_threaded,
+ .verbose_link = options.verbose_link,
+ .machine_code_model = options.machine_code_model,
+ .dll_export_fns = dll_export_fns,
+ .error_return_tracing = error_return_tracing,
+ .llvm_cpu_features = llvm_cpu_features,
+ });
+ errdefer bin_file.destroy();
+
+ comp.* = .{
+ .gpa = gpa,
+ .arena_state = arena_allocator.state,
+ .zig_lib_directory = options.zig_lib_directory,
+ .zig_cache_directory = options.zig_cache_directory,
+ .bin_file = bin_file,
+ .work_queue = std.fifo.LinearFifo(Job, .Dynamic).init(gpa),
+ .keep_source_files_loaded = options.keep_source_files_loaded,
+ .use_clang = use_clang,
+ .clang_argv = options.clang_argv,
+ .c_source_files = options.c_source_files,
+ .cache_parent = cache,
+ .self_exe_path = options.self_exe_path,
+ .libc_include_dir_list = libc_dirs.libc_include_dir_list,
+ .sanitize_c = sanitize_c,
+ .rand = options.rand,
+ .clang_passthrough_mode = options.clang_passthrough_mode,
+ .verbose_cc = options.verbose_cc,
+ .verbose_tokenize = options.verbose_tokenize,
+ .verbose_ast = options.verbose_ast,
+ .verbose_ir = options.verbose_ir,
+ .verbose_llvm_ir = options.verbose_llvm_ir,
+ .verbose_cimport = options.verbose_cimport,
+ .verbose_llvm_cpu_features = options.verbose_llvm_cpu_features,
+ .disable_c_depfile = options.disable_c_depfile,
+ .owned_link_dir = owned_link_dir,
+ .is_test = options.is_test,
+ .color = options.color,
+ .time_report = options.time_report,
+ };
+ break :comp comp;
+ };
+ errdefer comp.destroy();
+
+ if (comp.bin_file.options.module) |mod| {
+ try comp.work_queue.writeItem(.{ .generate_builtin_zig = {} });
+ }
+
+ // Add a `CObject` for each `c_source_files`.
+ try comp.c_object_table.ensureCapacity(gpa, options.c_source_files.len);
+ for (options.c_source_files) |c_source_file| {
+ const c_object = try gpa.create(CObject);
+ errdefer gpa.destroy(c_object);
+
+ c_object.* = .{
+ .status = .{ .new = {} },
+ .src = c_source_file,
+ };
+ comp.c_object_table.putAssumeCapacityNoClobber(c_object, {});
+ }
+
+ // If we need to build glibc for the target, add work items for it.
+ // We go through the work queue so that building can be done in parallel.
+ if (comp.wantBuildGLibCFromSource()) {
+ try comp.addBuildingGLibCJobs();
+ }
+ if (comp.wantBuildLibUnwindFromSource()) {
+ try comp.work_queue.writeItem(.{ .libunwind = {} });
+ }
+ if (build_options.is_stage1 and comp.bin_file.options.use_llvm) {
+ try comp.work_queue.writeItem(.{ .stage1_module = {} });
+ }
+ if (is_exe_or_dyn_lib) {
+ try comp.work_queue.writeItem(.{ .libcompiler_rt = {} });
+ if (!comp.bin_file.options.link_libc) {
+ try comp.work_queue.writeItem(.{ .zig_libc = {} });
+ }
+ }
+
+ return comp;
+}
+
+fn releaseStage1Lock(comp: *Compilation) void {
+ if (comp.stage1_lock) |*lock| {
+ lock.release();
+ comp.stage1_lock = null;
+ }
+}
+
+pub fn destroy(self: *Compilation) void {
+ const optional_module = self.bin_file.options.module;
+ self.bin_file.destroy();
+ if (optional_module) |module| module.deinit();
+
+ self.releaseStage1Lock();
+
+ const gpa = self.gpa;
+ self.work_queue.deinit();
+
+ {
+ var it = self.crt_files.iterator();
+ while (it.next()) |entry| {
+ entry.value.deinit(gpa);
+ }
+ self.crt_files.deinit(gpa);
+ }
+
+ if (self.libunwind_static_lib) |*crt_file| {
+ crt_file.deinit(gpa);
+ }
+ if (self.compiler_rt_static_lib) |*crt_file| {
+ crt_file.deinit(gpa);
+ }
+ if (self.libc_static_lib) |*crt_file| {
+ crt_file.deinit(gpa);
+ }
+
+ for (self.c_object_table.items()) |entry| {
+ entry.key.destroy(gpa);
+ }
+ self.c_object_table.deinit(gpa);
+
+ for (self.failed_c_objects.items()) |entry| {
+ entry.value.destroy(gpa);
+ }
+ self.failed_c_objects.deinit(gpa);
+
+ self.cache_parent.manifest_dir.close();
+ if (self.owned_link_dir) |*dir| dir.close();
+
+ // This destroys `self`.
+ self.arena_state.promote(gpa).deinit();
+}
+
+pub fn getTarget(self: Compilation) Target {
+ return self.bin_file.options.target;
+}
+
+/// Detect changes to source files, perform semantic analysis, and update the output files.
+pub fn update(self: *Compilation) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ // For compiling C objects, we rely on the cache hash system to avoid duplicating work.
+ // Add a Job for each C object.
+ try self.work_queue.ensureUnusedCapacity(self.c_object_table.items().len);
+ for (self.c_object_table.items()) |entry| {
+ self.work_queue.writeItemAssumeCapacity(.{ .c_object = entry.key });
+ }
+
+ const use_stage1 = build_options.is_stage1 and self.bin_file.options.use_llvm;
+ if (!use_stage1) {
+ if (self.bin_file.options.module) |module| {
+ module.generation += 1;
+
+ // TODO Detect which source files changed.
+ // Until then we simulate a full cache miss. Source files could have been loaded for any reason;
+ // to force a refresh we unload now.
+ if (module.root_scope.cast(Module.Scope.File)) |zig_file| {
+ zig_file.unload(module.gpa);
+ module.analyzeContainer(&zig_file.root_container) catch |err| switch (err) {
+ error.AnalysisFail => {
+ assert(self.totalErrorCount() != 0);
+ },
+ else => |e| return e,
+ };
+ } else if (module.root_scope.cast(Module.Scope.ZIRModule)) |zir_module| {
+ zir_module.unload(module.gpa);
+ module.analyzeRootZIRModule(zir_module) catch |err| switch (err) {
+ error.AnalysisFail => {
+ assert(self.totalErrorCount() != 0);
+ },
+ else => |e| return e,
+ };
+ }
+ }
+ }
+
+ try self.performAllTheWork();
+
+ if (!use_stage1) {
+ if (self.bin_file.options.module) |module| {
+ // Process the deletion set.
+ while (module.deletion_set.popOrNull()) |decl| {
+ if (decl.dependants.items().len != 0) {
+ decl.deletion_flag = false;
+ continue;
+ }
+ try module.deleteDecl(decl);
+ }
+ }
+ }
+
+ if (self.totalErrorCount() != 0) {
+ // Skip flushing.
+ self.link_error_flags = .{};
+ return;
+ }
+
+ // This is needed before reading the error flags.
+ try self.bin_file.flush(self);
+
+ self.link_error_flags = self.bin_file.errorFlags();
+
+ // If there are any errors, we anticipate the source files being loaded
+ // to report error messages. Otherwise we unload all source files to save memory.
+ if (self.totalErrorCount() == 0 and !self.keep_source_files_loaded) {
+ if (self.bin_file.options.module) |module| {
+ module.root_scope.unload(self.gpa);
+ }
+ }
+}
+
+/// Having the file open for writing is problematic as far as executing the
+/// binary is concerned. This will remove the write flag, or close the file,
+/// or whatever is needed so that it can be executed.
+/// After this, one must call` makeFileWritable` before calling `update`.
+pub fn makeBinFileExecutable(self: *Compilation) !void {
+ return self.bin_file.makeExecutable();
+}
+
+pub fn makeBinFileWritable(self: *Compilation) !void {
+ return self.bin_file.makeWritable();
+}
+
+pub fn totalErrorCount(self: *Compilation) usize {
+ var total: usize = self.failed_c_objects.items().len;
+
+ if (self.bin_file.options.module) |module| {
+ total += module.failed_decls.items().len +
+ module.failed_exports.items().len +
+ module.failed_files.items().len;
+ }
+
+ // The "no entry point found" error only counts if there are no other errors.
+ if (total == 0) {
+ return @boolToInt(self.link_error_flags.no_entry_point_found);
+ }
+
+ return total;
+}
+
+pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
+ var arena = std.heap.ArenaAllocator.init(self.gpa);
+ errdefer arena.deinit();
+
+ var errors = std.ArrayList(AllErrors.Message).init(self.gpa);
+ defer errors.deinit();
+
+ for (self.failed_c_objects.items()) |entry| {
+ const c_object = entry.key;
+ const err_msg = entry.value;
+ try AllErrors.add(&arena, &errors, c_object.src.src_path, "", err_msg.*);
+ }
+ if (self.bin_file.options.module) |module| {
+ for (module.failed_files.items()) |entry| {
+ const scope = entry.key;
+ const err_msg = entry.value;
+ const source = try scope.getSource(module);
+ try AllErrors.add(&arena, &errors, scope.subFilePath(), source, err_msg.*);
+ }
+ for (module.failed_decls.items()) |entry| {
+ const decl = entry.key;
+ const err_msg = entry.value;
+ const source = try decl.scope.getSource(module);
+ try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*);
+ }
+ for (module.failed_exports.items()) |entry| {
+ const decl = entry.key.owner_decl;
+ const err_msg = entry.value;
+ const source = try decl.scope.getSource(module);
+ try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*);
+ }
+ }
+
+ if (errors.items.len == 0 and self.link_error_flags.no_entry_point_found) {
+ const global_err_src_path = blk: {
+ if (self.bin_file.options.module) |module| break :blk module.root_pkg.root_src_path;
+ if (self.c_source_files.len != 0) break :blk self.c_source_files[0].src_path;
+ if (self.bin_file.options.objects.len != 0) break :blk self.bin_file.options.objects[0];
+ break :blk "(no file)";
+ };
+ try errors.append(.{
+ .src_path = global_err_src_path,
+ .line = 0,
+ .column = 0,
+ .byte_offset = 0,
+ .msg = try std.fmt.allocPrint(&arena.allocator, "no entry point found", .{}),
+ });
+ }
+
+ assert(errors.items.len == self.totalErrorCount());
+
+ return AllErrors{
+ .list = try arena.allocator.dupe(AllErrors.Message, errors.items),
+ .arena = arena.state,
+ };
+}
+
+pub fn performAllTheWork(self: *Compilation) error{OutOfMemory}!void {
+ while (self.work_queue.readItem()) |work_item| switch (work_item) {
+ .codegen_decl => |decl| switch (decl.analysis) {
+ .unreferenced => unreachable,
+ .in_progress => unreachable,
+ .outdated => unreachable,
+
+ .sema_failure,
+ .codegen_failure,
+ .dependency_failure,
+ .sema_failure_retryable,
+ => continue,
+
+ .complete, .codegen_failure_retryable => {
+ const module = self.bin_file.options.module.?;
+ if (decl.typed_value.most_recent.typed_value.val.cast(Value.Payload.Function)) |payload| {
+ switch (payload.func.analysis) {
+ .queued => module.analyzeFnBody(decl, payload.func) catch |err| switch (err) {
+ error.AnalysisFail => {
+ assert(payload.func.analysis != .in_progress);
+ continue;
+ },
+ error.OutOfMemory => return error.OutOfMemory,
+ },
+ .in_progress => unreachable,
+ .sema_failure, .dependency_failure => continue,
+ .success => {},
+ }
+ // Here we tack on additional allocations to the Decl's arena. The allocations are
+ // lifetime annotations in the ZIR.
+ var decl_arena = decl.typed_value.most_recent.arena.?.promote(module.gpa);
+ defer decl.typed_value.most_recent.arena.?.* = decl_arena.state;
+ log.debug("analyze liveness of {}\n", .{decl.name});
+ try liveness.analyze(module.gpa, &decl_arena.allocator, payload.func.analysis.success);
+ }
+
+ assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits());
+
+ self.bin_file.updateDecl(module, decl) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ error.AnalysisFail => {
+ decl.analysis = .dependency_failure;
+ },
+ else => {
+ try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1);
+ module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
+ module.gpa,
+ decl.src(),
+ "unable to codegen: {}",
+ .{@errorName(err)},
+ ));
+ decl.analysis = .codegen_failure_retryable;
+ },
+ };
+ },
+ },
+ .analyze_decl => |decl| {
+ const module = self.bin_file.options.module.?;
+ module.ensureDeclAnalyzed(decl) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ error.AnalysisFail => continue,
+ };
+ },
+ .update_line_number => |decl| {
+ const module = self.bin_file.options.module.?;
+ self.bin_file.updateDeclLineNumber(module, decl) catch |err| {
+ try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1);
+ module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
+ module.gpa,
+ decl.src(),
+ "unable to update line number: {}",
+ .{@errorName(err)},
+ ));
+ decl.analysis = .codegen_failure_retryable;
+ };
+ },
+ .c_object => |c_object| {
+ self.updateCObject(c_object) catch |err| switch (err) {
+ error.AnalysisFail => continue,
+ else => {
+ try self.failed_c_objects.ensureCapacity(self.gpa, self.failed_c_objects.items().len + 1);
+ self.failed_c_objects.putAssumeCapacityNoClobber(c_object, try ErrorMsg.create(
+ self.gpa,
+ 0,
+ "unable to build C object: {}",
+ .{@errorName(err)},
+ ));
+ c_object.status = .{ .failure = {} };
+ },
+ };
+ },
+ .glibc_crt_file => |crt_file| {
+ glibc.buildCRTFile(self, crt_file) catch |err| {
+ // TODO Expose this as a normal compile error rather than crashing here.
+ fatal("unable to build glibc CRT file: {}", .{@errorName(err)});
+ };
+ },
+ .glibc_shared_objects => {
+ glibc.buildSharedObjects(self) catch |err| {
+ // TODO Expose this as a normal compile error rather than crashing here.
+ fatal("unable to build glibc shared objects: {}", .{@errorName(err)});
+ };
+ },
+ .libunwind => {
+ libunwind.buildStaticLib(self) catch |err| {
+ // TODO Expose this as a normal compile error rather than crashing here.
+ fatal("unable to build libunwind: {}", .{@errorName(err)});
+ };
+ },
+ .libcompiler_rt => {
+ self.buildStaticLibFromZig("compiler_rt.zig", &self.compiler_rt_static_lib) catch |err| {
+ // TODO Expose this as a normal compile error rather than crashing here.
+ fatal("unable to build compiler_rt: {}", .{@errorName(err)});
+ };
+ },
+ .zig_libc => {
+ self.buildStaticLibFromZig("c.zig", &self.libc_static_lib) catch |err| {
+ // TODO Expose this as a normal compile error rather than crashing here.
+ fatal("unable to build zig's multitarget libc: {}", .{@errorName(err)});
+ };
+ },
+ .generate_builtin_zig => {
+ // This Job is only queued up if there is a zig module.
+ self.updateBuiltinZigFile(self.bin_file.options.module.?) catch |err| {
+ // TODO Expose this as a normal compile error rather than crashing here.
+ fatal("unable to update builtin.zig file: {}", .{@errorName(err)});
+ };
+ },
+ .stage1_module => {
+ self.updateStage1Module() catch |err| {
+ fatal("unable to build stage1 zig object: {}", .{@errorName(err)});
+ };
+ },
+ };
+}
+
+fn updateCObject(comp: *Compilation, c_object: *CObject) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ if (!build_options.have_llvm) {
+ return comp.failCObj(c_object, "clang not available: compiler built without LLVM extensions", .{});
+ }
+ const self_exe_path = comp.self_exe_path orelse
+ return comp.failCObj(c_object, "clang compilation disabled", .{});
+
+ if (c_object.clearStatus(comp.gpa)) {
+ // There was previous failure.
+ comp.failed_c_objects.removeAssertDiscard(c_object);
+ }
+
+ var ch = comp.cache_parent.obtain();
+ defer ch.deinit();
+
+ ch.hash.add(comp.sanitize_c);
+ ch.hash.addListOfBytes(comp.clang_argv);
+ ch.hash.add(comp.bin_file.options.link_libcpp);
+ ch.hash.addListOfBytes(comp.libc_include_dir_list);
+ _ = try ch.addFile(c_object.src.src_path, null);
+ {
+ // Hash the extra flags, with special care to call addFile for file parameters.
+ // TODO this logic can likely be improved by utilizing clang_options_data.zig.
+ const file_args = [_][]const u8{"-include"};
+ var arg_i: usize = 0;
+ while (arg_i < c_object.src.extra_flags.len) : (arg_i += 1) {
+ const arg = c_object.src.extra_flags[arg_i];
+ ch.hash.addBytes(arg);
+ for (file_args) |file_arg| {
+ if (mem.eql(u8, file_arg, arg) and arg_i + 1 < c_object.src.extra_flags.len) {
+ arg_i += 1;
+ _ = try ch.addFile(c_object.src.extra_flags[arg_i], null);
+ }
+ }
+ }
+ }
+
+ var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
+ defer arena_allocator.deinit();
+ const arena = &arena_allocator.allocator;
+
+ const c_source_basename = std.fs.path.basename(c_object.src.src_path);
+ // Special case when doing build-obj for just one C file. When there are more than one object
+ // file and building an object we need to link them together, but with just one it should go
+ // directly to the output file.
+ const direct_o = comp.c_source_files.len == 1 and comp.bin_file.options.module == null and
+ comp.bin_file.options.output_mode == .Obj and comp.bin_file.options.objects.len == 0;
+ const o_basename_noext = if (direct_o)
+ comp.bin_file.options.root_name
+ else
+ mem.split(c_source_basename, ".").next().?;
+ const o_basename = try std.fmt.allocPrint(arena, "{}{}", .{ o_basename_noext, comp.getTarget().oFileExt() });
+
+ const digest = if ((try ch.hit()) and !comp.disable_c_depfile) ch.final() else blk: {
+ var argv = std.ArrayList([]const u8).init(comp.gpa);
+ defer argv.deinit();
+
+ // We can't know the digest until we do the C compiler invocation, so we need a temporary filename.
+ const out_obj_path = try comp.tmpFilePath(arena, o_basename);
+ var zig_cache_tmp_dir = try comp.zig_cache_directory.handle.makeOpenPath("tmp", .{});
+ defer zig_cache_tmp_dir.close();
+
+ try argv.appendSlice(&[_][]const u8{ self_exe_path, "clang", "-c" });
+
+ const ext = classifyFileExt(c_object.src.src_path);
+ const out_dep_path: ?[]const u8 = if (comp.disable_c_depfile or !ext.clangSupportsDepFile())
+ null
+ else
+ try std.fmt.allocPrint(arena, "{}.d", .{out_obj_path});
+ try comp.addCCArgs(arena, &argv, ext, false, out_dep_path);
+
+ try argv.append("-o");
+ try argv.append(out_obj_path);
+
+ try argv.append(c_object.src.src_path);
+ try argv.appendSlice(c_object.src.extra_flags);
+
+ if (comp.verbose_cc) {
+ dump_argv(argv.items);
+ }
+
+ const child = try std.ChildProcess.init(argv.items, arena);
+ defer child.deinit();
+
+ if (comp.clang_passthrough_mode) {
+ child.stdin_behavior = .Inherit;
+ child.stdout_behavior = .Inherit;
+ child.stderr_behavior = .Inherit;
+
+ const term = child.spawnAndWait() catch |err| {
+ return comp.failCObj(c_object, "unable to spawn {}: {}", .{ argv.items[0], @errorName(err) });
+ };
+ switch (term) {
+ .Exited => |code| {
+ if (code != 0) {
+ // TODO https://github.com/ziglang/zig/issues/6342
+ std.process.exit(1);
+ }
+ },
+ else => std.process.exit(1),
+ }
+ } else {
+ child.stdin_behavior = .Ignore;
+ child.stdout_behavior = .Pipe;
+ child.stderr_behavior = .Pipe;
+
+ try child.spawn();
+
+ const stdout_reader = child.stdout.?.reader();
+ const stderr_reader = child.stderr.?.reader();
+
+ // TODO https://github.com/ziglang/zig/issues/6343
+ const stdout = try stdout_reader.readAllAlloc(arena, std.math.maxInt(u32));
+ const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);
+
+ const term = child.wait() catch |err| {
+ return comp.failCObj(c_object, "unable to spawn {}: {}", .{ argv.items[0], @errorName(err) });
+ };
+
+ switch (term) {
+ .Exited => |code| {
+ if (code != 0) {
+ // TODO parse clang stderr and turn it into an error message
+ // and then call failCObjWithOwnedErrorMsg
+ std.log.err("clang failed with stderr: {}", .{stderr});
+ return comp.failCObj(c_object, "clang exited with code {}", .{code});
+ }
+ },
+ else => {
+ std.log.err("clang terminated with stderr: {}", .{stderr});
+ return comp.failCObj(c_object, "clang terminated unexpectedly", .{});
+ },
+ }
+ }
+
+ if (out_dep_path) |dep_file_path| {
+ const dep_basename = std.fs.path.basename(dep_file_path);
+ // Add the files depended on to the cache system.
+ try ch.addDepFilePost(zig_cache_tmp_dir, dep_basename);
+ // Just to save disk space, we delete the file because it is never needed again.
+ zig_cache_tmp_dir.deleteFile(dep_basename) catch |err| {
+ std.log.warn("failed to delete '{}': {}", .{ dep_file_path, @errorName(err) });
+ };
+ }
+
+ // Rename into place.
+ const digest = ch.final();
+ const o_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest });
+ var o_dir = try comp.zig_cache_directory.handle.makeOpenPath(o_sub_path, .{});
+ defer o_dir.close();
+ // TODO https://github.com/ziglang/zig/issues/6344
+ const tmp_basename = std.fs.path.basename(out_obj_path);
+ try std.os.renameat(zig_cache_tmp_dir.fd, tmp_basename, o_dir.fd, o_basename);
+
+ ch.writeManifest() catch |err| {
+ std.log.warn("failed to write cache manifest when compiling '{}': {}", .{ c_object.src.src_path, @errorName(err) });
+ };
+ break :blk digest;
+ };
+
+ const components = if (comp.zig_cache_directory.path) |p|
+ &[_][]const u8{ p, "o", &digest, o_basename }
+ else
+ &[_][]const u8{ "o", &digest, o_basename };
+
+ c_object.status = .{
+ .success = .{
+ .object_path = try std.fs.path.join(comp.gpa, components),
+ .lock = ch.toOwnedLock(),
+ },
+ };
+}
+
+fn tmpFilePath(comp: *Compilation, arena: *Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 {
+ const s = std.fs.path.sep_str;
+ const rand_int = comp.rand.int(u64);
+ if (comp.zig_cache_directory.path) |p| {
+ return std.fmt.allocPrint(arena, "{}" ++ s ++ "tmp" ++ s ++ "{x}-{s}", .{ p, rand_int, suffix });
+ } else {
+ return std.fmt.allocPrint(arena, "tmp" ++ s ++ "{x}-{s}", .{ rand_int, suffix });
+ }
+}
+
+/// Add common C compiler args between translate-c and C object compilation.
+pub fn addCCArgs(
+ comp: *Compilation,
+ arena: *Allocator,
+ argv: *std.ArrayList([]const u8),
+ ext: FileExt,
+ translate_c: bool,
+ out_dep_path: ?[]const u8,
+) !void {
+ const target = comp.getTarget();
+
+ if (translate_c) {
+ try argv.appendSlice(&[_][]const u8{ "-x", "c" });
+ }
+
+ if (ext == .cpp) {
+ try argv.append("-nostdinc++");
+ }
+
+ // We don't ever put `-fcolor-diagnostics` or `-fno-color-diagnostics` because in passthrough mode
+ // we want Clang to infer it, and in normal mode we always want it off, which will be true since
+ // clang will detect stderr as a pipe rather than a terminal.
+ if (!comp.clang_passthrough_mode) {
+ // Make stderr more easily parseable.
+ try argv.append("-fno-caret-diagnostics");
+ }
+
+ if (comp.bin_file.options.function_sections) {
+ try argv.append("-ffunction-sections");
+ }
+
+ try argv.ensureCapacity(argv.items.len + comp.bin_file.options.framework_dirs.len * 2);
+ for (comp.bin_file.options.framework_dirs) |framework_dir| {
+ argv.appendAssumeCapacity("-iframework");
+ argv.appendAssumeCapacity(framework_dir);
+ }
+
+ if (comp.bin_file.options.link_libcpp) {
+ const libcxx_include_path = try std.fs.path.join(arena, &[_][]const u8{
+ comp.zig_lib_directory.path.?, "libcxx", "include",
+ });
+ const libcxxabi_include_path = try std.fs.path.join(arena, &[_][]const u8{
+ comp.zig_lib_directory.path.?, "libcxxabi", "include",
+ });
+
+ try argv.append("-isystem");
+ try argv.append(libcxx_include_path);
+
+ try argv.append("-isystem");
+ try argv.append(libcxxabi_include_path);
+
+ if (target.abi.isMusl()) {
+ try argv.append("-D_LIBCPP_HAS_MUSL_LIBC");
+ }
+ try argv.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS");
+ try argv.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS");
+ }
+
+ const llvm_triple = try @import("codegen/llvm.zig").targetTriple(arena, target);
+ try argv.appendSlice(&[_][]const u8{ "-target", llvm_triple });
+
+ switch (ext) {
+ .c, .cpp, .h => {
+ try argv.appendSlice(&[_][]const u8{
+ "-nostdinc",
+ "-fno-spell-checking",
+ });
+
+ // According to Rich Felker libc headers are supposed to go before C language headers.
+ // However as noted by @dimenus, appending libc headers before c_headers breaks intrinsics
+ // and other compiler specific items.
+ const c_headers_dir = try std.fs.path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, "include" });
+ try argv.append("-isystem");
+ try argv.append(c_headers_dir);
+
+ for (comp.libc_include_dir_list) |include_dir| {
+ try argv.append("-isystem");
+ try argv.append(include_dir);
+ }
+
+ if (target.cpu.model.llvm_name) |llvm_name| {
+ try argv.appendSlice(&[_][]const u8{
+ "-Xclang", "-target-cpu", "-Xclang", llvm_name,
+ });
+ }
+
+ // It would be really nice if there was a more compact way to communicate this info to Clang.
+ const all_features_list = target.cpu.arch.allFeaturesList();
+ try argv.ensureCapacity(argv.items.len + all_features_list.len * 4);
+ for (all_features_list) |feature, index_usize| {
+ const index = @intCast(std.Target.Cpu.Feature.Set.Index, index_usize);
+ const is_enabled = target.cpu.features.isEnabled(index);
+
+ if (feature.llvm_name) |llvm_name| {
+ argv.appendSliceAssumeCapacity(&[_][]const u8{ "-Xclang", "-target-feature", "-Xclang" });
+ const plus_or_minus = "-+"[@boolToInt(is_enabled)];
+ const arg = try std.fmt.allocPrint(arena, "{c}{s}", .{ plus_or_minus, llvm_name });
+ argv.appendAssumeCapacity(arg);
+ }
+ }
+ const mcmodel = comp.bin_file.options.machine_code_model;
+ if (mcmodel != .default) {
+ try argv.append(try std.fmt.allocPrint(arena, "-mcmodel={}", .{@tagName(mcmodel)}));
+ }
+ if (translate_c) {
+ // This gives us access to preprocessing entities, presumably at the cost of performance.
+ try argv.append("-Xclang");
+ try argv.append("-detailed-preprocessing-record");
+ }
+
+ // windows.h has files such as pshpack1.h which do #pragma packing, triggering a clang warning.
+ // So for this target, we disable this warning.
+ if (target.os.tag == .windows and target.abi.isGnu()) {
+ try argv.append("-Wno-pragma-pack");
+ }
+
+ if (!comp.bin_file.options.strip) {
+ try argv.append("-g");
+ }
+
+ if (comp.haveFramePointer()) {
+ try argv.append("-fno-omit-frame-pointer");
+ } else {
+ try argv.append("-fomit-frame-pointer");
+ }
+
+ if (comp.sanitize_c) {
+ try argv.append("-fsanitize=undefined");
+ try argv.append("-fsanitize-trap=undefined");
+ }
+
+ switch (comp.bin_file.options.optimize_mode) {
+ .Debug => {
+ // windows c runtime requires -D_DEBUG if using debug libraries
+ try argv.append("-D_DEBUG");
+ try argv.append("-Og");
+
+ if (comp.bin_file.options.link_libc) {
+ try argv.append("-fstack-protector-strong");
+ try argv.append("--param");
+ try argv.append("ssp-buffer-size=4");
+ } else {
+ try argv.append("-fno-stack-protector");
+ }
+ },
+ .ReleaseSafe => {
+ // See the comment in the BuildModeFastRelease case for why we pass -O2 rather
+ // than -O3 here.
+ try argv.append("-O2");
+ if (comp.bin_file.options.link_libc) {
+ try argv.append("-D_FORTIFY_SOURCE=2");
+ try argv.append("-fstack-protector-strong");
+ try argv.append("--param");
+ try argv.append("ssp-buffer-size=4");
+ } else {
+ try argv.append("-fno-stack-protector");
+ }
+ },
+ .ReleaseFast => {
+ try argv.append("-DNDEBUG");
+ // Here we pass -O2 rather than -O3 because, although we do the equivalent of
+ // -O3 in Zig code, the justification for the difference here is that Zig
+ // has better detection and prevention of undefined behavior, so -O3 is safer for
+ // Zig code than it is for C code. Also, C programmers are used to their code
+ // running in -O2 and thus the -O3 path has been tested less.
+ try argv.append("-O2");
+ try argv.append("-fno-stack-protector");
+ },
+ .ReleaseSmall => {
+ try argv.append("-DNDEBUG");
+ try argv.append("-Os");
+ try argv.append("-fno-stack-protector");
+ },
+ }
+
+ if (target_util.supports_fpic(target) and comp.bin_file.options.pic) {
+ try argv.append("-fPIC");
+ }
+ },
+ .shared_library, .assembly, .ll, .bc, .unknown, .static_library, .object, .zig, .zir => {},
+ }
+ if (out_dep_path) |p| {
+ try argv.appendSlice(&[_][]const u8{ "-MD", "-MV", "-MF", p });
+ }
+ // Argh, why doesn't the assembler accept the list of CPU features?!
+ // I don't see a way to do this other than hard coding everything.
+ switch (target.cpu.arch) {
+ .riscv32, .riscv64 => {
+ if (std.Target.riscv.featureSetHas(target.cpu.features, .relax)) {
+ try argv.append("-mrelax");
+ } else {
+ try argv.append("-mno-relax");
+ }
+ },
+ else => {
+ // TODO
+ },
+ }
+
+ if (target.os.tag == .freestanding) {
+ try argv.append("-ffreestanding");
+ }
+
+ try argv.appendSlice(comp.clang_argv);
+}
+
+fn failCObj(comp: *Compilation, c_object: *CObject, comptime format: []const u8, args: anytype) InnerError {
+ @setCold(true);
+ const err_msg = try ErrorMsg.create(comp.gpa, 0, "unable to build C object: " ++ format, args);
+ return comp.failCObjWithOwnedErrorMsg(c_object, err_msg);
+}
+
+fn failCObjWithOwnedErrorMsg(comp: *Compilation, c_object: *CObject, err_msg: *ErrorMsg) InnerError {
+ {
+ errdefer err_msg.destroy(comp.gpa);
+ try comp.failed_c_objects.ensureCapacity(comp.gpa, comp.failed_c_objects.items().len + 1);
+ }
+ comp.failed_c_objects.putAssumeCapacityNoClobber(c_object, err_msg);
+ c_object.status = .failure;
+ return error.AnalysisFail;
+}
+
+pub const ErrorMsg = struct {
+ byte_offset: usize,
+ msg: []const u8,
+
+ pub fn create(gpa: *Allocator, byte_offset: usize, comptime format: []const u8, args: anytype) !*ErrorMsg {
+ const self = try gpa.create(ErrorMsg);
+ errdefer gpa.destroy(self);
+ self.* = try init(gpa, byte_offset, format, args);
+ return self;
+ }
+
+ /// Assumes the ErrorMsg struct and msg were both allocated with allocator.
+ pub fn destroy(self: *ErrorMsg, gpa: *Allocator) void {
+ self.deinit(gpa);
+ gpa.destroy(self);
+ }
+
+ pub fn init(gpa: *Allocator, byte_offset: usize, comptime format: []const u8, args: anytype) !ErrorMsg {
+ return ErrorMsg{
+ .byte_offset = byte_offset,
+ .msg = try std.fmt.allocPrint(gpa, format, args),
+ };
+ }
+
+ pub fn deinit(self: *ErrorMsg, gpa: *Allocator) void {
+ gpa.free(self.msg);
+ self.* = undefined;
+ }
+};
+
+pub const FileExt = enum {
+ c,
+ cpp,
+ h,
+ ll,
+ bc,
+ assembly,
+ shared_library,
+ object,
+ static_library,
+ zig,
+ zir,
+ unknown,
+
+ pub fn clangSupportsDepFile(ext: FileExt) bool {
+ return switch (ext) {
+ .c, .cpp, .h => true,
+
+ .ll,
+ .bc,
+ .assembly,
+ .shared_library,
+ .object,
+ .static_library,
+ .zig,
+ .zir,
+ .unknown,
+ => false,
+ };
+ }
+};
+
+pub fn hasObjectExt(filename: []const u8) bool {
+ return mem.endsWith(u8, filename, ".o") or mem.endsWith(u8, filename, ".obj");
+}
+
+pub fn hasStaticLibraryExt(filename: []const u8) bool {
+ return mem.endsWith(u8, filename, ".a") or mem.endsWith(u8, filename, ".lib");
+}
+
+pub fn hasCExt(filename: []const u8) bool {
+ return mem.endsWith(u8, filename, ".c");
+}
+
+pub fn hasCppExt(filename: []const u8) bool {
+ return mem.endsWith(u8, filename, ".C") or
+ mem.endsWith(u8, filename, ".cc") or
+ mem.endsWith(u8, filename, ".cpp") or
+ mem.endsWith(u8, filename, ".cxx");
+}
+
+pub fn hasAsmExt(filename: []const u8) bool {
+ return mem.endsWith(u8, filename, ".s") or mem.endsWith(u8, filename, ".S");
+}
+
+pub fn hasSharedLibraryExt(filename: []const u8) bool {
+ if (mem.endsWith(u8, filename, ".so") or
+ mem.endsWith(u8, filename, ".dll") or
+ mem.endsWith(u8, filename, ".dylib"))
+ {
+ return true;
+ }
+ // Look for .so.X, .so.X.Y, .so.X.Y.Z
+ var it = mem.split(filename, ".");
+ _ = it.next().?;
+ var so_txt = it.next() orelse return false;
+ while (!mem.eql(u8, so_txt, "so")) {
+ so_txt = it.next() orelse return false;
+ }
+ const n1 = it.next() orelse return false;
+ const n2 = it.next();
+ const n3 = it.next();
+
+ _ = std.fmt.parseInt(u32, n1, 10) catch return false;
+ if (n2) |x| _ = std.fmt.parseInt(u32, x, 10) catch return false;
+ if (n3) |x| _ = std.fmt.parseInt(u32, x, 10) catch return false;
+ if (it.next() != null) return false;
+
+ return true;
+}
+
+pub fn classifyFileExt(filename: []const u8) FileExt {
+ if (hasCExt(filename)) {
+ return .c;
+ } else if (hasCppExt(filename)) {
+ return .cpp;
+ } else if (mem.endsWith(u8, filename, ".ll")) {
+ return .ll;
+ } else if (mem.endsWith(u8, filename, ".bc")) {
+ return .bc;
+ } else if (hasAsmExt(filename)) {
+ return .assembly;
+ } else if (mem.endsWith(u8, filename, ".h")) {
+ return .h;
+ } else if (mem.endsWith(u8, filename, ".zig")) {
+ return .zig;
+ } else if (mem.endsWith(u8, filename, ".zir")) {
+ return .zir;
+ } else if (hasSharedLibraryExt(filename)) {
+ return .shared_library;
+ } else if (hasStaticLibraryExt(filename)) {
+ return .static_library;
+ } else if (hasObjectExt(filename)) {
+ return .object;
+ } else {
+ return .unknown;
+ }
+}
+
+test "classifyFileExt" {
+ std.testing.expectEqual(FileExt.cpp, classifyFileExt("foo.cc"));
+ std.testing.expectEqual(FileExt.unknown, classifyFileExt("foo.nim"));
+ std.testing.expectEqual(FileExt.so, classifyFileExt("foo.so"));
+ std.testing.expectEqual(FileExt.so, classifyFileExt("foo.so.1"));
+ std.testing.expectEqual(FileExt.so, classifyFileExt("foo.so.1.2"));
+ std.testing.expectEqual(FileExt.so, classifyFileExt("foo.so.1.2.3"));
+ std.testing.expectEqual(FileExt.unknown, classifyFileExt("foo.so.1.2.3~"));
+}
+
+fn haveFramePointer(comp: *Compilation) bool {
+ // If you complicate this logic make sure you update the parent cache hash.
+ // Right now it's not in the cache hash because the value depends on optimize_mode
+ // and strip which are both already part of the hash.
+ return switch (comp.bin_file.options.optimize_mode) {
+ .Debug, .ReleaseSafe => !comp.bin_file.options.strip,
+ .ReleaseSmall, .ReleaseFast => false,
+ };
+}
+
+const LibCDirs = struct {
+ libc_include_dir_list: []const []const u8,
+ libc_installation: ?*const LibCInstallation,
+};
+
+fn detectLibCIncludeDirs(
+ arena: *Allocator,
+ zig_lib_dir: []const u8,
+ target: Target,
+ is_native_os: bool,
+ link_libc: bool,
+ libc_installation: ?*const LibCInstallation,
+) !LibCDirs {
+ if (!link_libc) {
+ return LibCDirs{
+ .libc_include_dir_list = &[0][]u8{},
+ .libc_installation = null,
+ };
+ }
+
+ if (libc_installation) |lci| {
+ return detectLibCFromLibCInstallation(arena, target, lci);
+ }
+
+ if (target_util.canBuildLibC(target)) {
+ const generic_name = target_util.libCGenericName(target);
+ // Some architectures are handled by the same set of headers.
+ const arch_name = if (target.abi.isMusl()) target_util.archMuslName(target.cpu.arch) else @tagName(target.cpu.arch);
+ const os_name = @tagName(target.os.tag);
+ // Musl's headers are ABI-agnostic and so they all have the "musl" ABI name.
+ const abi_name = if (target.abi.isMusl()) "musl" else @tagName(target.abi);
+ const s = std.fs.path.sep_str;
+ const arch_include_dir = try std.fmt.allocPrint(
+ arena,
+ "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-{}-{}",
+ .{ zig_lib_dir, arch_name, os_name, abi_name },
+ );
+ const generic_include_dir = try std.fmt.allocPrint(
+ arena,
+ "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "generic-{}",
+ .{ zig_lib_dir, generic_name },
+ );
+ const arch_os_include_dir = try std.fmt.allocPrint(
+ arena,
+ "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-{}-any",
+ .{ zig_lib_dir, @tagName(target.cpu.arch), os_name },
+ );
+ const generic_os_include_dir = try std.fmt.allocPrint(
+ arena,
+ "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-{}-any",
+ .{ zig_lib_dir, os_name },
+ );
+
+ const list = try arena.alloc([]const u8, 4);
+ list[0] = arch_include_dir;
+ list[1] = generic_include_dir;
+ list[2] = arch_os_include_dir;
+ list[3] = generic_os_include_dir;
+ return LibCDirs{
+ .libc_include_dir_list = list,
+ .libc_installation = null,
+ };
+ }
+
+ if (is_native_os) {
+ const libc = try arena.create(LibCInstallation);
+ libc.* = try LibCInstallation.findNative(.{ .allocator = arena });
+ return detectLibCFromLibCInstallation(arena, target, libc);
+ }
+
+ return LibCDirs{
+ .libc_include_dir_list = &[0][]u8{},
+ .libc_installation = null,
+ };
+}
+
+fn detectLibCFromLibCInstallation(arena: *Allocator, target: Target, lci: *const LibCInstallation) !LibCDirs {
+ var list = std.ArrayList([]const u8).init(arena);
+ try list.ensureCapacity(4);
+
+ list.appendAssumeCapacity(lci.include_dir.?);
+
+ const is_redundant = mem.eql(u8, lci.sys_include_dir.?, lci.include_dir.?);
+ if (!is_redundant) list.appendAssumeCapacity(lci.sys_include_dir.?);
+
+ if (target.os.tag == .windows) {
+ if (std.fs.path.dirname(lci.include_dir.?)) |include_dir_parent| {
+ const um_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_parent, "um" });
+ list.appendAssumeCapacity(um_dir);
+
+ const shared_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_parent, "shared" });
+ list.appendAssumeCapacity(shared_dir);
+ }
+ }
+ return LibCDirs{
+ .libc_include_dir_list = list.items,
+ .libc_installation = lci,
+ };
+}
+
+pub fn get_libc_crt_file(comp: *Compilation, arena: *Allocator, basename: []const u8) ![]const u8 {
+ if (comp.wantBuildGLibCFromSource()) {
+ return comp.crt_files.get(basename).?.full_object_path;
+ }
+ const lci = comp.bin_file.options.libc_installation orelse return error.LibCInstallationNotAvailable;
+ const crt_dir_path = lci.crt_dir orelse return error.LibCInstallationMissingCRTDir;
+ const full_path = try std.fs.path.join(arena, &[_][]const u8{ crt_dir_path, basename });
+ return full_path;
+}
+
+fn addBuildingGLibCJobs(comp: *Compilation) !void {
+ try comp.work_queue.write(&[_]Job{
+ .{ .glibc_crt_file = .crti_o },
+ .{ .glibc_crt_file = .crtn_o },
+ .{ .glibc_crt_file = .scrt1_o },
+ .{ .glibc_crt_file = .libc_nonshared_a },
+ .{ .glibc_shared_objects = {} },
+ });
+}
+
+fn wantBuildGLibCFromSource(comp: *Compilation) bool {
+ const is_exe_or_dyn_lib = switch (comp.bin_file.options.output_mode) {
+ .Obj => false,
+ .Lib => comp.bin_file.options.link_mode == .Dynamic,
+ .Exe => true,
+ };
+ return comp.bin_file.options.link_libc and is_exe_or_dyn_lib and
+ comp.bin_file.options.libc_installation == null and
+ comp.bin_file.options.target.isGnuLibC();
+}
+
+fn wantBuildLibUnwindFromSource(comp: *Compilation) bool {
+ const is_exe_or_dyn_lib = switch (comp.bin_file.options.output_mode) {
+ .Obj => false,
+ .Lib => comp.bin_file.options.link_mode == .Dynamic,
+ .Exe => true,
+ };
+ return comp.bin_file.options.link_libc and is_exe_or_dyn_lib and
+ comp.bin_file.options.libc_installation == null;
+}
+
+fn updateBuiltinZigFile(comp: *Compilation, mod: *Module) !void {
+ const source = try comp.generateBuiltinZigSource(comp.gpa);
+ defer comp.gpa.free(source);
+ try mod.zig_cache_artifact_directory.handle.writeFile("builtin.zig", source);
+}
+
+pub fn dump_argv(argv: []const []const u8) void {
+ for (argv[0 .. argv.len - 1]) |arg| {
+ std.debug.print("{} ", .{arg});
+ }
+ std.debug.print("{}\n", .{argv[argv.len - 1]});
+}
+
+pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8 {
+ var buffer = std.ArrayList(u8).init(allocator);
+ defer buffer.deinit();
+
+ const target = comp.getTarget();
+ const generic_arch_name = target.cpu.arch.genericName();
+
+ @setEvalBranchQuota(4000);
+ try buffer.writer().print(
+ \\usingnamespace @import("std").builtin;
+ \\/// Deprecated
+ \\pub const arch = Target.current.cpu.arch;
+ \\/// Deprecated
+ \\pub const endian = Target.current.cpu.arch.endian();
+ \\pub const output_mode = OutputMode.{};
+ \\pub const link_mode = LinkMode.{};
+ \\pub const is_test = {};
+ \\pub const single_threaded = {};
+ \\pub const abi = Abi.{};
+ \\pub const cpu: Cpu = Cpu{{
+ \\ .arch = .{},
+ \\ .model = &Target.{}.cpu.{},
+ \\ .features = Target.{}.featureSet(&[_]Target.{}.Feature{{
+ \\
+ , .{
+ @tagName(comp.bin_file.options.output_mode),
+ @tagName(comp.bin_file.options.link_mode),
+ comp.is_test,
+ comp.bin_file.options.single_threaded,
+ @tagName(target.abi),
+ @tagName(target.cpu.arch),
+ generic_arch_name,
+ target.cpu.model.name,
+ generic_arch_name,
+ generic_arch_name,
+ });
+
+ for (target.cpu.arch.allFeaturesList()) |feature, index_usize| {
+ const index = @intCast(std.Target.Cpu.Feature.Set.Index, index_usize);
+ const is_enabled = target.cpu.features.isEnabled(index);
+ if (is_enabled) {
+ // TODO some kind of "zig identifier escape" function rather than
+ // unconditionally using @"" syntax
+ try buffer.appendSlice(" .@\"");
+ try buffer.appendSlice(feature.name);
+ try buffer.appendSlice("\",\n");
+ }
+ }
+
+ try buffer.writer().print(
+ \\ }}),
+ \\}};
+ \\pub const os = Os{{
+ \\ .tag = .{},
+ \\ .version_range = .{{
+ ,
+ .{@tagName(target.os.tag)},
+ );
+
+ switch (target.os.getVersionRange()) {
+ .none => try buffer.appendSlice(" .none = {} }\n"),
+ .semver => |semver| try buffer.outStream().print(
+ \\ .semver = .{{
+ \\ .min = .{{
+ \\ .major = {},
+ \\ .minor = {},
+ \\ .patch = {},
+ \\ }},
+ \\ .max = .{{
+ \\ .major = {},
+ \\ .minor = {},
+ \\ .patch = {},
+ \\ }},
+ \\ }}}},
+ \\
+ , .{
+ semver.min.major,
+ semver.min.minor,
+ semver.min.patch,
+
+ semver.max.major,
+ semver.max.minor,
+ semver.max.patch,
+ }),
+ .linux => |linux| try buffer.outStream().print(
+ \\ .linux = .{{
+ \\ .range = .{{
+ \\ .min = .{{
+ \\ .major = {},
+ \\ .minor = {},
+ \\ .patch = {},
+ \\ }},
+ \\ .max = .{{
+ \\ .major = {},
+ \\ .minor = {},
+ \\ .patch = {},
+ \\ }},
+ \\ }},
+ \\ .glibc = .{{
+ \\ .major = {},
+ \\ .minor = {},
+ \\ .patch = {},
+ \\ }},
+ \\ }}}},
+ \\
+ , .{
+ linux.range.min.major,
+ linux.range.min.minor,
+ linux.range.min.patch,
+
+ linux.range.max.major,
+ linux.range.max.minor,
+ linux.range.max.patch,
+
+ linux.glibc.major,
+ linux.glibc.minor,
+ linux.glibc.patch,
+ }),
+ .windows => |windows| try buffer.outStream().print(
+ \\ .windows = .{{
+ \\ .min = {s},
+ \\ .max = {s},
+ \\ }}}},
+ \\
+ ,
+ .{ windows.min, windows.max },
+ ),
+ }
+ try buffer.appendSlice("};\n");
+ try buffer.writer().print(
+ \\pub const object_format = ObjectFormat.{};
+ \\pub const mode = Mode.{};
+ \\pub const link_libc = {};
+ \\pub const link_libcpp = {};
+ \\pub const have_error_return_tracing = {};
+ \\pub const valgrind_support = {};
+ \\pub const position_independent_code = {};
+ \\pub const strip_debug_info = {};
+ \\pub const code_model = CodeModel.{};
+ \\
+ , .{
+ @tagName(comp.bin_file.options.object_format),
+ @tagName(comp.bin_file.options.optimize_mode),
+ comp.bin_file.options.link_libc,
+ comp.bin_file.options.link_libcpp,
+ comp.bin_file.options.error_return_tracing,
+ comp.bin_file.options.valgrind,
+ comp.bin_file.options.pic,
+ comp.bin_file.options.strip,
+ @tagName(comp.bin_file.options.machine_code_model),
+ });
+ return buffer.toOwnedSlice();
+}
+
+pub fn updateSubCompilation(sub_compilation: *Compilation) !void {
+ try sub_compilation.update();
+
+ // Look for compilation errors in this sub_compilation
+ var errors = try sub_compilation.getAllErrorsAlloc();
+ defer errors.deinit(sub_compilation.gpa);
+
+ if (errors.list.len != 0) {
+ for (errors.list) |full_err_msg| {
+ std.log.err("{}:{}:{}: {}\n", .{
+ full_err_msg.src_path,
+ full_err_msg.line + 1,
+ full_err_msg.column + 1,
+ full_err_msg.msg,
+ });
+ }
+ return error.BuildingLibCObjectFailed;
+ }
+}
+
+fn buildStaticLibFromZig(comp: *Compilation, basename: []const u8, out: *?CRTFile) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const special_sub = "std" ++ std.fs.path.sep_str ++ "special";
+ const special_path = try comp.zig_lib_directory.join(comp.gpa, &[_][]const u8{special_sub});
+ defer comp.gpa.free(special_path);
+
+ var special_dir = try comp.zig_lib_directory.handle.openDir(special_sub, .{});
+ defer special_dir.close();
+
+ var root_pkg: Package = .{
+ .root_src_directory = .{
+ .path = special_path,
+ .handle = special_dir,
+ },
+ .root_src_path = basename,
+ };
+
+ const emit_bin = Compilation.EmitLoc{
+ .directory = null, // Put it in the cache directory.
+ .basename = basename,
+ };
+ const optimize_mode: std.builtin.Mode = blk: {
+ if (comp.is_test)
+ break :blk comp.bin_file.options.optimize_mode;
+ switch (comp.bin_file.options.optimize_mode) {
+ .Debug, .ReleaseFast, .ReleaseSafe => break :blk .ReleaseFast,
+ .ReleaseSmall => break :blk .ReleaseSmall,
+ }
+ };
+ const sub_compilation = try Compilation.create(comp.gpa, .{
+ // TODO use the global cache directory here
+ .zig_cache_directory = comp.zig_cache_directory,
+ .zig_lib_directory = comp.zig_lib_directory,
+ .target = comp.getTarget(),
+ .root_name = mem.split(basename, ".").next().?,
+ .root_pkg = &root_pkg,
+ .output_mode = .Lib,
+ .rand = comp.rand,
+ .libc_installation = comp.bin_file.options.libc_installation,
+ .emit_bin = emit_bin,
+ .optimize_mode = optimize_mode,
+ .link_mode = .Static,
+ .function_sections = true,
+ .want_sanitize_c = false,
+ .want_stack_check = false,
+ .want_valgrind = false,
+ .want_pic = comp.bin_file.options.pic,
+ .emit_h = null,
+ .strip = comp.bin_file.options.strip,
+ .is_native_os = comp.bin_file.options.is_native_os,
+ .self_exe_path = comp.self_exe_path,
+ .verbose_cc = comp.verbose_cc,
+ .verbose_link = comp.bin_file.options.verbose_link,
+ .verbose_tokenize = comp.verbose_tokenize,
+ .verbose_ast = comp.verbose_ast,
+ .verbose_ir = comp.verbose_ir,
+ .verbose_llvm_ir = comp.verbose_llvm_ir,
+ .verbose_cimport = comp.verbose_cimport,
+ .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
+ .clang_passthrough_mode = comp.clang_passthrough_mode,
+ });
+ defer sub_compilation.destroy();
+
+ try sub_compilation.updateSubCompilation();
+
+ assert(out.* == null);
+ out.* = Compilation.CRTFile{
+ .full_object_path = try sub_compilation.bin_file.options.directory.join(comp.gpa, &[_][]const u8{basename}),
+ .lock = sub_compilation.bin_file.toOwnedLock(),
+ };
+}
+
+fn updateStage1Module(comp: *Compilation) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
+ defer arena_allocator.deinit();
+ const arena = &arena_allocator.allocator;
+
+ // Here we use the legacy stage1 C++ compiler to compile Zig code.
+ const mod = comp.bin_file.options.module.?;
+ const directory = mod.zig_cache_artifact_directory; // Just an alias to make it shorter to type.
+ const main_zig_file = try mod.root_pkg.root_src_directory.join(arena, &[_][]const u8{
+ mod.root_pkg.root_src_path,
+ });
+ const zig_lib_dir = comp.zig_lib_directory.path.?;
+ const builtin_zig_path = try directory.join(arena, &[_][]const u8{"builtin.zig"});
+ const target = comp.getTarget();
+ const id_symlink_basename = "stage1.id";
+
+ // We are about to obtain this lock, so here we give other processes a chance first.
+ comp.releaseStage1Lock();
+
+ // Unlike with the self-hosted Zig module, stage1 does not support incremental compilation,
+ // so we input all the zig source files into the cache hash system. We're going to keep
+ // the artifact directory the same, however, so we take the same strategy as linking
+ // does where we have a file which specifies the hash of the output directory so that we can
+ // skip the expensive compilation step if the hash matches.
+ var ch = comp.cache_parent.obtain();
+ defer ch.deinit();
+
+ _ = try ch.addFile(main_zig_file, null);
+ ch.hash.add(comp.bin_file.options.valgrind);
+ ch.hash.add(comp.bin_file.options.single_threaded);
+ ch.hash.add(target.os.getVersionRange());
+ ch.hash.add(comp.bin_file.options.dll_export_fns);
+ ch.hash.add(comp.bin_file.options.function_sections);
+
+ if (try ch.hit()) {
+ const digest = ch.final();
+
+ var prev_digest_buf: [digest.len]u8 = undefined;
+ const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: {
+ // Handle this as a cache miss.
+ break :blk prev_digest_buf[0..0];
+ };
+ if (mem.eql(u8, prev_digest, &digest)) {
+ comp.stage1_lock = ch.toOwnedLock();
+ return;
+ }
+ }
+
+ const stage2_target = try arena.create(stage1.Stage2Target);
+ stage2_target.* = .{
+ .arch = @enumToInt(target.cpu.arch) + 1, // skip over ZigLLVM_UnknownArch
+ .os = @enumToInt(target.os.tag),
+ .abi = @enumToInt(target.abi),
+ .is_native_os = comp.bin_file.options.is_native_os,
+ .is_native_cpu = false, // Only true when bootstrapping the compiler.
+ .llvm_cpu_name = if (target.cpu.model.llvm_name) |s| s.ptr else null,
+ .llvm_cpu_features = comp.bin_file.options.llvm_cpu_features.?,
+ };
+ var progress: std.Progress = .{};
+ var main_progress_node = try progress.start("", 100);
+ defer main_progress_node.end();
+ if (comp.color == .Off) progress.terminal = null;
+
+ comp.stage1_cache_hash = &ch;
+
+ const stage1_module = stage1.create(
+ @enumToInt(comp.bin_file.options.optimize_mode),
+ undefined,
+ 0, // TODO --main-pkg-path
+ main_zig_file.ptr,
+ main_zig_file.len,
+ zig_lib_dir.ptr,
+ zig_lib_dir.len,
+ stage2_target,
+ comp.is_test,
+ ) orelse return error.OutOfMemory;
+
+ const stage1_pkg = try arena.create(stage1.Pkg);
+ stage1_pkg.* = .{
+ .name_ptr = undefined,
+ .name_len = 0,
+ .path_ptr = undefined,
+ .path_len = 0,
+ .children_ptr = undefined,
+ .children_len = 0,
+ .parent = null,
+ };
+ const output_dir = comp.bin_file.options.directory.path orelse ".";
+ stage1_module.* = .{
+ .root_name_ptr = comp.bin_file.options.root_name.ptr,
+ .root_name_len = comp.bin_file.options.root_name.len,
+ .output_dir_ptr = output_dir.ptr,
+ .output_dir_len = output_dir.len,
+ .builtin_zig_path_ptr = builtin_zig_path.ptr,
+ .builtin_zig_path_len = builtin_zig_path.len,
+ .test_filter_ptr = "",
+ .test_filter_len = 0,
+ .test_name_prefix_ptr = "",
+ .test_name_prefix_len = 0,
+ .userdata = @ptrToInt(comp),
+ .root_pkg = stage1_pkg,
+ .code_model = @enumToInt(comp.bin_file.options.machine_code_model),
+ .subsystem = stage1.TargetSubsystem.Auto,
+ .err_color = @enumToInt(comp.color),
+ .pic = comp.bin_file.options.pic,
+ .link_libc = comp.bin_file.options.link_libc,
+ .link_libcpp = comp.bin_file.options.link_libcpp,
+ .strip = comp.bin_file.options.strip,
+ .is_single_threaded = comp.bin_file.options.single_threaded,
+ .dll_export_fns = comp.bin_file.options.dll_export_fns,
+ .link_mode_dynamic = comp.bin_file.options.link_mode == .Dynamic,
+ .valgrind_enabled = comp.bin_file.options.valgrind,
+ .function_sections = comp.bin_file.options.function_sections,
+ .enable_stack_probing = comp.bin_file.options.stack_check,
+ .enable_time_report = comp.time_report,
+ .enable_stack_report = false,
+ .dump_analysis = false,
+ .enable_doc_generation = false,
+ .emit_bin = true,
+ .emit_asm = false,
+ .emit_llvm_ir = false,
+ .test_is_evented = false,
+ .verbose_tokenize = comp.verbose_tokenize,
+ .verbose_ast = comp.verbose_ast,
+ .verbose_ir = comp.verbose_ir,
+ .verbose_llvm_ir = comp.verbose_llvm_ir,
+ .verbose_cimport = comp.verbose_cimport,
+ .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
+ .main_progress_node = main_progress_node,
+ };
+ stage1_module.build_object();
+ stage1_module.destroy();
+
+ const digest = ch.final();
+
+ // Update the dangling symlink with the digest. If it fails we can continue; it only
+ // means that the next invocation will have an unnecessary cache miss.
+ directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| {
+ std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)});
+ };
+ // Again failure here only means an unnecessary cache miss.
+ ch.writeManifest() catch |err| {
+ std.log.warn("failed to write cache manifest when linking: {}", .{@errorName(err)});
+ };
+ // We hang on to this lock so that the output file path can be used without
+ // other processes clobbering it.
+ comp.stage1_lock = ch.toOwnedLock();
+}
diff --git a/src/DepTokenizer.zig b/src/DepTokenizer.zig
new file mode 100644
index 0000000000000000000000000000000000000000..cc2211a1aa1bb90ecbdc825b1633229886bbc408
--- /dev/null
+++ b/src/DepTokenizer.zig
@@ -0,0 +1,1064 @@
+const Tokenizer = @This();
+
+index: usize = 0,
+bytes: []const u8,
+state: State = .lhs,
+
+const std = @import("std");
+const testing = std.testing;
+const assert = std.debug.assert;
+
+pub fn next(self: *Tokenizer) ?Token {
+ var start = self.index;
+ var must_resolve = false;
+ while (self.index < self.bytes.len) {
+ const char = self.bytes[self.index];
+ switch (self.state) {
+ .lhs => switch (char) {
+ '\t', '\n', '\r', ' ' => {
+ // silently ignore whitespace
+ self.index += 1;
+ },
+ else => {
+ start = self.index;
+ self.state = .target;
+ },
+ },
+ .target => switch (char) {
+ '\t', '\n', '\r', ' ' => {
+ return errorIllegalChar(.invalid_target, self.index, char);
+ },
+ '$' => {
+ self.state = .target_dollar_sign;
+ self.index += 1;
+ },
+ '\\' => {
+ self.state = .target_reverse_solidus;
+ self.index += 1;
+ },
+ ':' => {
+ self.state = .target_colon;
+ self.index += 1;
+ },
+ else => {
+ self.index += 1;
+ },
+ },
+ .target_reverse_solidus => switch (char) {
+ '\t', '\n', '\r' => {
+ return errorIllegalChar(.bad_target_escape, self.index, char);
+ },
+ ' ', '#', '\\' => {
+ must_resolve = true;
+ self.state = .target;
+ self.index += 1;
+ },
+ '$' => {
+ self.state = .target_dollar_sign;
+ self.index += 1;
+ },
+ else => {
+ self.state = .target;
+ self.index += 1;
+ },
+ },
+ .target_dollar_sign => switch (char) {
+ '$' => {
+ must_resolve = true;
+ self.state = .target;
+ self.index += 1;
+ },
+ else => {
+ return errorIllegalChar(.expected_dollar_sign, self.index, char);
+ },
+ },
+ .target_colon => switch (char) {
+ '\n', '\r' => {
+ const bytes = self.bytes[start .. self.index - 1];
+ if (bytes.len != 0) {
+ self.state = .lhs;
+ return finishTarget(must_resolve, bytes);
+ }
+ // silently ignore null target
+ self.state = .lhs;
+ },
+ '\\' => {
+ self.state = .target_colon_reverse_solidus;
+ self.index += 1;
+ },
+ else => {
+ const bytes = self.bytes[start .. self.index - 1];
+ if (bytes.len != 0) {
+ self.state = .rhs;
+ return finishTarget(must_resolve, bytes);
+ }
+ // silently ignore null target
+ self.state = .lhs;
+ },
+ },
+ .target_colon_reverse_solidus => switch (char) {
+ '\n', '\r' => {
+ const bytes = self.bytes[start .. self.index - 2];
+ if (bytes.len != 0) {
+ self.state = .lhs;
+ return finishTarget(must_resolve, bytes);
+ }
+ // silently ignore null target
+ self.state = .lhs;
+ },
+ else => {
+ self.state = .target;
+ },
+ },
+ .rhs => switch (char) {
+ '\t', ' ' => {
+ // silently ignore horizontal whitespace
+ self.index += 1;
+ },
+ '\n', '\r' => {
+ self.state = .lhs;
+ },
+ '\\' => {
+ self.state = .rhs_continuation;
+ self.index += 1;
+ },
+ '"' => {
+ self.state = .prereq_quote;
+ self.index += 1;
+ start = self.index;
+ },
+ else => {
+ start = self.index;
+ self.state = .prereq;
+ },
+ },
+ .rhs_continuation => switch (char) {
+ '\n' => {
+ self.state = .rhs;
+ self.index += 1;
+ },
+ '\r' => {
+ self.state = .rhs_continuation_linefeed;
+ self.index += 1;
+ },
+ else => {
+ return errorIllegalChar(.continuation_eol, self.index, char);
+ },
+ },
+ .rhs_continuation_linefeed => switch (char) {
+ '\n' => {
+ self.state = .rhs;
+ self.index += 1;
+ },
+ else => {
+ return errorIllegalChar(.continuation_eol, self.index, char);
+ },
+ },
+ .prereq_quote => switch (char) {
+ '"' => {
+ self.index += 1;
+ self.state = .rhs;
+ return Token{ .prereq = self.bytes[start .. self.index - 1] };
+ },
+ else => {
+ self.index += 1;
+ },
+ },
+ .prereq => switch (char) {
+ '\t', ' ' => {
+ self.state = .rhs;
+ return Token{ .prereq = self.bytes[start..self.index] };
+ },
+ '\n', '\r' => {
+ self.state = .lhs;
+ return Token{ .prereq = self.bytes[start..self.index] };
+ },
+ '\\' => {
+ self.state = .prereq_continuation;
+ self.index += 1;
+ },
+ else => {
+ self.index += 1;
+ },
+ },
+ .prereq_continuation => switch (char) {
+ '\n' => {
+ self.index += 1;
+ self.state = .rhs;
+ return Token{ .prereq = self.bytes[start .. self.index - 2] };
+ },
+ '\r' => {
+ self.state = .prereq_continuation_linefeed;
+ self.index += 1;
+ },
+ else => {
+ // not continuation
+ self.state = .prereq;
+ self.index += 1;
+ },
+ },
+ .prereq_continuation_linefeed => switch (char) {
+ '\n' => {
+ self.index += 1;
+ self.state = .rhs;
+ return Token{ .prereq = self.bytes[start .. self.index - 1] };
+ },
+ else => {
+ return errorIllegalChar(.continuation_eol, self.index, char);
+ },
+ },
+ }
+ } else {
+ switch (self.state) {
+ .lhs,
+ .rhs,
+ .rhs_continuation,
+ .rhs_continuation_linefeed,
+ => return null,
+ .target => {
+ return errorPosition(.incomplete_target, start, self.bytes[start..]);
+ },
+ .target_reverse_solidus,
+ .target_dollar_sign,
+ => {
+ const idx = self.index - 1;
+ return errorIllegalChar(.incomplete_escape, idx, self.bytes[idx]);
+ },
+ .target_colon => {
+ const bytes = self.bytes[start .. self.index - 1];
+ if (bytes.len != 0) {
+ self.index += 1;
+ self.state = .rhs;
+ return finishTarget(must_resolve, bytes);
+ }
+ // silently ignore null target
+ self.state = .lhs;
+ return null;
+ },
+ .target_colon_reverse_solidus => {
+ const bytes = self.bytes[start .. self.index - 2];
+ if (bytes.len != 0) {
+ self.index += 1;
+ self.state = .rhs;
+ return finishTarget(must_resolve, bytes);
+ }
+ // silently ignore null target
+ self.state = .lhs;
+ return null;
+ },
+ .prereq_quote => {
+ return errorPosition(.incomplete_quoted_prerequisite, start, self.bytes[start..]);
+ },
+ .prereq => {
+ self.state = .lhs;
+ return Token{ .prereq = self.bytes[start..] };
+ },
+ .prereq_continuation => {
+ self.state = .lhs;
+ return Token{ .prereq = self.bytes[start .. self.index - 1] };
+ },
+ .prereq_continuation_linefeed => {
+ self.state = .lhs;
+ return Token{ .prereq = self.bytes[start .. self.index - 2] };
+ },
+ }
+ }
+ unreachable;
+}
+
+fn errorPosition(comptime id: @TagType(Token), index: usize, bytes: []const u8) Token {
+ return @unionInit(Token, @tagName(id), .{ .index = index, .bytes = bytes });
+}
+
+fn errorIllegalChar(comptime id: @TagType(Token), index: usize, char: u8) Token {
+ return @unionInit(Token, @tagName(id), .{ .index = index, .char = char });
+}
+
+fn finishTarget(must_resolve: bool, bytes: []const u8) Token {
+ return if (must_resolve)
+ .{ .target_must_resolve = bytes }
+ else
+ .{ .target = bytes };
+}
+
+const State = enum {
+ lhs,
+ target,
+ target_reverse_solidus,
+ target_dollar_sign,
+ target_colon,
+ target_colon_reverse_solidus,
+ rhs,
+ rhs_continuation,
+ rhs_continuation_linefeed,
+ prereq_quote,
+ prereq,
+ prereq_continuation,
+ prereq_continuation_linefeed,
+};
+
+pub const Token = union(enum) {
+ target: []const u8,
+ target_must_resolve: []const u8,
+ prereq: []const u8,
+
+ incomplete_quoted_prerequisite: IndexAndBytes,
+ incomplete_target: IndexAndBytes,
+
+ invalid_target: IndexAndChar,
+ bad_target_escape: IndexAndChar,
+ expected_dollar_sign: IndexAndChar,
+ continuation_eol: IndexAndChar,
+ incomplete_escape: IndexAndChar,
+
+ pub const IndexAndChar = struct {
+ index: usize,
+ char: u8,
+ };
+
+ pub const IndexAndBytes = struct {
+ index: usize,
+ bytes: []const u8,
+ };
+
+ /// Resolve escapes in target. Only valid with .target_must_resolve.
+ pub fn resolve(self: Token, writer: anytype) @TypeOf(writer).Error!void {
+ const bytes = self.target_must_resolve; // resolve called on incorrect token
+
+ var state: enum { start, escape, dollar } = .start;
+ for (bytes) |c| {
+ switch (state) {
+ .start => {
+ switch (c) {
+ '\\' => state = .escape,
+ '$' => state = .dollar,
+ else => try writer.writeByte(c),
+ }
+ },
+ .escape => {
+ switch (c) {
+ ' ', '#', '\\' => {},
+ '$' => {
+ try writer.writeByte('\\');
+ state = .dollar;
+ continue;
+ },
+ else => try writer.writeByte('\\'),
+ }
+ try writer.writeByte(c);
+ state = .start;
+ },
+ .dollar => {
+ try writer.writeByte('$');
+ switch (c) {
+ '$' => {},
+ else => try writer.writeByte(c),
+ }
+ state = .start;
+ },
+ }
+ }
+ }
+
+ pub fn printError(self: Token, writer: anytype) @TypeOf(writer).Error!void {
+ switch (self) {
+ .target, .target_must_resolve, .prereq => unreachable, // not an error
+ .incomplete_quoted_prerequisite,
+ .incomplete_target,
+ => |index_and_bytes| {
+ try writer.print("{} '", .{self.errStr()});
+ if (self == .incomplete_target) {
+ const tmp = Token{ .target_must_resolve = index_and_bytes.bytes };
+ try tmp.resolve(writer);
+ } else {
+ try printCharValues(writer, index_and_bytes.bytes);
+ }
+ try writer.print("' at position {}", .{index_and_bytes.index});
+ },
+ .invalid_target,
+ .bad_target_escape,
+ .expected_dollar_sign,
+ .continuation_eol,
+ .incomplete_escape,
+ => |index_and_char| {
+ try writer.writeAll("illegal char ");
+ try printUnderstandableChar(writer, index_and_char.char);
+ try writer.print(" at position {}: {}", .{ index_and_char.index, self.errStr() });
+ },
+ }
+ }
+
+ fn errStr(self: Token) []const u8 {
+ return switch (self) {
+ .target, .target_must_resolve, .prereq => unreachable, // not an error
+ .incomplete_quoted_prerequisite => "incomplete quoted prerequisite",
+ .incomplete_target => "incomplete target",
+ .invalid_target => "invalid target",
+ .bad_target_escape => "bad target escape",
+ .expected_dollar_sign => "expecting '$'",
+ .continuation_eol => "continuation expecting end-of-line",
+ .incomplete_escape => "incomplete escape",
+ };
+ }
+};
+
+test "empty file" {
+ try depTokenizer("", "");
+}
+
+test "empty whitespace" {
+ try depTokenizer("\n", "");
+ try depTokenizer("\r", "");
+ try depTokenizer("\r\n", "");
+ try depTokenizer(" ", "");
+}
+
+test "empty colon" {
+ try depTokenizer(":", "");
+ try depTokenizer("\n:", "");
+ try depTokenizer("\r:", "");
+ try depTokenizer("\r\n:", "");
+ try depTokenizer(" :", "");
+}
+
+test "empty target" {
+ try depTokenizer("foo.o:", "target = {foo.o}");
+ try depTokenizer(
+ \\foo.o:
+ \\bar.o:
+ \\abcd.o:
+ ,
+ \\target = {foo.o}
+ \\target = {bar.o}
+ \\target = {abcd.o}
+ );
+}
+
+test "whitespace empty target" {
+ try depTokenizer("\nfoo.o:", "target = {foo.o}");
+ try depTokenizer("\rfoo.o:", "target = {foo.o}");
+ try depTokenizer("\r\nfoo.o:", "target = {foo.o}");
+ try depTokenizer(" foo.o:", "target = {foo.o}");
+}
+
+test "escape empty target" {
+ try depTokenizer("\\ foo.o:", "target = { foo.o}");
+ try depTokenizer("\\#foo.o:", "target = {#foo.o}");
+ try depTokenizer("\\\\foo.o:", "target = {\\foo.o}");
+ try depTokenizer("$$foo.o:", "target = {$foo.o}");
+}
+
+test "empty target linefeeds" {
+ try depTokenizer("\n", "");
+ try depTokenizer("\r\n", "");
+
+ const expect = "target = {foo.o}";
+ try depTokenizer(
+ \\foo.o:
+ , expect);
+ try depTokenizer(
+ \\foo.o:
+ \\
+ , expect);
+ try depTokenizer(
+ \\foo.o:
+ , expect);
+ try depTokenizer(
+ \\foo.o:
+ \\
+ , expect);
+}
+
+test "empty target linefeeds + continuations" {
+ const expect = "target = {foo.o}";
+ try depTokenizer(
+ \\foo.o:\
+ , expect);
+ try depTokenizer(
+ \\foo.o:\
+ \\
+ , expect);
+ try depTokenizer(
+ \\foo.o:\
+ , expect);
+ try depTokenizer(
+ \\foo.o:\
+ \\
+ , expect);
+}
+
+test "empty target linefeeds + hspace + continuations" {
+ const expect = "target = {foo.o}";
+ try depTokenizer(
+ \\foo.o: \
+ , expect);
+ try depTokenizer(
+ \\foo.o: \
+ \\
+ , expect);
+ try depTokenizer(
+ \\foo.o: \
+ , expect);
+ try depTokenizer(
+ \\foo.o: \
+ \\
+ , expect);
+}
+
+test "prereq" {
+ const expect =
+ \\target = {foo.o}
+ \\prereq = {foo.c}
+ ;
+ try depTokenizer("foo.o: foo.c", expect);
+ try depTokenizer(
+ \\foo.o: \
+ \\foo.c
+ , expect);
+ try depTokenizer(
+ \\foo.o: \
+ \\ foo.c
+ , expect);
+ try depTokenizer(
+ \\foo.o: \
+ \\ foo.c
+ , expect);
+}
+
+test "prereq continuation" {
+ const expect =
+ \\target = {foo.o}
+ \\prereq = {foo.h}
+ \\prereq = {bar.h}
+ ;
+ try depTokenizer(
+ \\foo.o: foo.h\
+ \\bar.h
+ , expect);
+ try depTokenizer(
+ \\foo.o: foo.h\
+ \\bar.h
+ , expect);
+}
+
+test "multiple prereqs" {
+ const expect =
+ \\target = {foo.o}
+ \\prereq = {foo.c}
+ \\prereq = {foo.h}
+ \\prereq = {bar.h}
+ ;
+ try depTokenizer("foo.o: foo.c foo.h bar.h", expect);
+ try depTokenizer(
+ \\foo.o: \
+ \\foo.c foo.h bar.h
+ , expect);
+ try depTokenizer(
+ \\foo.o: foo.c foo.h bar.h\
+ , expect);
+ try depTokenizer(
+ \\foo.o: foo.c foo.h bar.h\
+ \\
+ , expect);
+ try depTokenizer(
+ \\foo.o: \
+ \\foo.c \
+ \\ foo.h\
+ \\bar.h
+ \\
+ , expect);
+ try depTokenizer(
+ \\foo.o: \
+ \\foo.c \
+ \\ foo.h\
+ \\bar.h\
+ \\
+ , expect);
+ try depTokenizer(
+ \\foo.o: \
+ \\foo.c \
+ \\ foo.h\
+ \\bar.h\
+ , expect);
+}
+
+test "multiple targets and prereqs" {
+ try depTokenizer(
+ \\foo.o: foo.c
+ \\bar.o: bar.c a.h b.h c.h
+ \\abc.o: abc.c \
+ \\ one.h two.h \
+ \\ three.h four.h
+ ,
+ \\target = {foo.o}
+ \\prereq = {foo.c}
+ \\target = {bar.o}
+ \\prereq = {bar.c}
+ \\prereq = {a.h}
+ \\prereq = {b.h}
+ \\prereq = {c.h}
+ \\target = {abc.o}
+ \\prereq = {abc.c}
+ \\prereq = {one.h}
+ \\prereq = {two.h}
+ \\prereq = {three.h}
+ \\prereq = {four.h}
+ );
+ try depTokenizer(
+ \\ascii.o: ascii.c
+ \\base64.o: base64.c stdio.h
+ \\elf.o: elf.c a.h b.h c.h
+ \\macho.o: \
+ \\ macho.c\
+ \\ a.h b.h c.h
+ ,
+ \\target = {ascii.o}
+ \\prereq = {ascii.c}
+ \\target = {base64.o}
+ \\prereq = {base64.c}
+ \\prereq = {stdio.h}
+ \\target = {elf.o}
+ \\prereq = {elf.c}
+ \\prereq = {a.h}
+ \\prereq = {b.h}
+ \\prereq = {c.h}
+ \\target = {macho.o}
+ \\prereq = {macho.c}
+ \\prereq = {a.h}
+ \\prereq = {b.h}
+ \\prereq = {c.h}
+ );
+ try depTokenizer(
+ \\a$$scii.o: ascii.c
+ \\\\base64.o: "\base64.c" "s t#dio.h"
+ \\e\\lf.o: "e\lf.c" "a.h$$" "$$b.h c.h$$"
+ \\macho.o: \
+ \\ "macho!.c" \
+ \\ a.h b.h c.h
+ ,
+ \\target = {a$scii.o}
+ \\prereq = {ascii.c}
+ \\target = {\base64.o}
+ \\prereq = {\base64.c}
+ \\prereq = {s t#dio.h}
+ \\target = {e\lf.o}
+ \\prereq = {e\lf.c}
+ \\prereq = {a.h$$}
+ \\prereq = {$$b.h c.h$$}
+ \\target = {macho.o}
+ \\prereq = {macho!.c}
+ \\prereq = {a.h}
+ \\prereq = {b.h}
+ \\prereq = {c.h}
+ );
+}
+
+test "windows quoted prereqs" {
+ try depTokenizer(
+ \\c:\foo.o: "C:\Program Files (x86)\Microsoft Visual Studio\foo.c"
+ \\c:\foo2.o: "C:\Program Files (x86)\Microsoft Visual Studio\foo2.c" \
+ \\ "C:\Program Files (x86)\Microsoft Visual Studio\foo1.h" \
+ \\ "C:\Program Files (x86)\Microsoft Visual Studio\foo2.h"
+ ,
+ \\target = {c:\foo.o}
+ \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo.c}
+ \\target = {c:\foo2.o}
+ \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo2.c}
+ \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo1.h}
+ \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo2.h}
+ );
+}
+
+test "windows mixed prereqs" {
+ try depTokenizer(
+ \\cimport.o: \
+ \\ C:\msys64\home\anon\project\zig\master\zig-cache\o\qhvhbUo7GU5iKyQ5mpA8TcQpncCYaQu0wwvr3ybiSTj_Dtqi1Nmcb70kfODJ2Qlg\cimport.h \
+ \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\stdio.h" \
+ \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt.h" \
+ \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime.h" \
+ \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\sal.h" \
+ \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\concurrencysal.h" \
+ \\ C:\msys64\opt\zig\lib\zig\include\vadefs.h \
+ \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vadefs.h" \
+ \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstdio.h" \
+ \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_stdio_config.h" \
+ \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\string.h" \
+ \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memory.h" \
+ \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memcpy_s.h" \
+ \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\errno.h" \
+ \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime_string.h" \
+ \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstring.h"
+ ,
+ \\target = {cimport.o}
+ \\prereq = {C:\msys64\home\anon\project\zig\master\zig-cache\o\qhvhbUo7GU5iKyQ5mpA8TcQpncCYaQu0wwvr3ybiSTj_Dtqi1Nmcb70kfODJ2Qlg\cimport.h}
+ \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\stdio.h}
+ \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt.h}
+ \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime.h}
+ \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\sal.h}
+ \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\concurrencysal.h}
+ \\prereq = {C:\msys64\opt\zig\lib\zig\include\vadefs.h}
+ \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vadefs.h}
+ \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstdio.h}
+ \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_stdio_config.h}
+ \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\string.h}
+ \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memory.h}
+ \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memcpy_s.h}
+ \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\errno.h}
+ \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime_string.h}
+ \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstring.h}
+ );
+}
+
+test "funky targets" {
+ try depTokenizer(
+ \\C:\Users\anon\foo.o:
+ \\C:\Users\anon\foo\ .o:
+ \\C:\Users\anon\foo\#.o:
+ \\C:\Users\anon\foo$$.o:
+ \\C:\Users\anon\\\ foo.o:
+ \\C:\Users\anon\\#foo.o:
+ \\C:\Users\anon\$$foo.o:
+ \\C:\Users\anon\\\ \ \ \ \ foo.o:
+ ,
+ \\target = {C:\Users\anon\foo.o}
+ \\target = {C:\Users\anon\foo .o}
+ \\target = {C:\Users\anon\foo#.o}
+ \\target = {C:\Users\anon\foo$.o}
+ \\target = {C:\Users\anon\ foo.o}
+ \\target = {C:\Users\anon\#foo.o}
+ \\target = {C:\Users\anon\$foo.o}
+ \\target = {C:\Users\anon\ foo.o}
+ );
+}
+
+test "error incomplete escape - reverse_solidus" {
+ try depTokenizer("\\",
+ \\ERROR: illegal char '\' at position 0: incomplete escape
+ );
+ try depTokenizer("\t\\",
+ \\ERROR: illegal char '\' at position 1: incomplete escape
+ );
+ try depTokenizer("\n\\",
+ \\ERROR: illegal char '\' at position 1: incomplete escape
+ );
+ try depTokenizer("\r\\",
+ \\ERROR: illegal char '\' at position 1: incomplete escape
+ );
+ try depTokenizer("\r\n\\",
+ \\ERROR: illegal char '\' at position 2: incomplete escape
+ );
+ try depTokenizer(" \\",
+ \\ERROR: illegal char '\' at position 1: incomplete escape
+ );
+}
+
+test "error incomplete escape - dollar_sign" {
+ try depTokenizer("$",
+ \\ERROR: illegal char '$' at position 0: incomplete escape
+ );
+ try depTokenizer("\t$",
+ \\ERROR: illegal char '$' at position 1: incomplete escape
+ );
+ try depTokenizer("\n$",
+ \\ERROR: illegal char '$' at position 1: incomplete escape
+ );
+ try depTokenizer("\r$",
+ \\ERROR: illegal char '$' at position 1: incomplete escape
+ );
+ try depTokenizer("\r\n$",
+ \\ERROR: illegal char '$' at position 2: incomplete escape
+ );
+ try depTokenizer(" $",
+ \\ERROR: illegal char '$' at position 1: incomplete escape
+ );
+}
+
+test "error incomplete target" {
+ try depTokenizer("foo.o",
+ \\ERROR: incomplete target 'foo.o' at position 0
+ );
+ try depTokenizer("\tfoo.o",
+ \\ERROR: incomplete target 'foo.o' at position 1
+ );
+ try depTokenizer("\nfoo.o",
+ \\ERROR: incomplete target 'foo.o' at position 1
+ );
+ try depTokenizer("\rfoo.o",
+ \\ERROR: incomplete target 'foo.o' at position 1
+ );
+ try depTokenizer("\r\nfoo.o",
+ \\ERROR: incomplete target 'foo.o' at position 2
+ );
+ try depTokenizer(" foo.o",
+ \\ERROR: incomplete target 'foo.o' at position 1
+ );
+
+ try depTokenizer("\\ foo.o",
+ \\ERROR: incomplete target ' foo.o' at position 0
+ );
+ try depTokenizer("\\#foo.o",
+ \\ERROR: incomplete target '#foo.o' at position 0
+ );
+ try depTokenizer("\\\\foo.o",
+ \\ERROR: incomplete target '\foo.o' at position 0
+ );
+ try depTokenizer("$$foo.o",
+ \\ERROR: incomplete target '$foo.o' at position 0
+ );
+}
+
+test "error illegal char at position - bad target escape" {
+ try depTokenizer("\\\t",
+ \\ERROR: illegal char \x09 at position 1: bad target escape
+ );
+ try depTokenizer("\\\n",
+ \\ERROR: illegal char \x0A at position 1: bad target escape
+ );
+ try depTokenizer("\\\r",
+ \\ERROR: illegal char \x0D at position 1: bad target escape
+ );
+ try depTokenizer("\\\r\n",
+ \\ERROR: illegal char \x0D at position 1: bad target escape
+ );
+}
+
+test "error illegal char at position - execting dollar_sign" {
+ try depTokenizer("$\t",
+ \\ERROR: illegal char \x09 at position 1: expecting '$'
+ );
+ try depTokenizer("$\n",
+ \\ERROR: illegal char \x0A at position 1: expecting '$'
+ );
+ try depTokenizer("$\r",
+ \\ERROR: illegal char \x0D at position 1: expecting '$'
+ );
+ try depTokenizer("$\r\n",
+ \\ERROR: illegal char \x0D at position 1: expecting '$'
+ );
+}
+
+test "error illegal char at position - invalid target" {
+ try depTokenizer("foo\t.o",
+ \\ERROR: illegal char \x09 at position 3: invalid target
+ );
+ try depTokenizer("foo\n.o",
+ \\ERROR: illegal char \x0A at position 3: invalid target
+ );
+ try depTokenizer("foo\r.o",
+ \\ERROR: illegal char \x0D at position 3: invalid target
+ );
+ try depTokenizer("foo\r\n.o",
+ \\ERROR: illegal char \x0D at position 3: invalid target
+ );
+}
+
+test "error target - continuation expecting end-of-line" {
+ try depTokenizer("foo.o: \\\t",
+ \\target = {foo.o}
+ \\ERROR: illegal char \x09 at position 8: continuation expecting end-of-line
+ );
+ try depTokenizer("foo.o: \\ ",
+ \\target = {foo.o}
+ \\ERROR: illegal char \x20 at position 8: continuation expecting end-of-line
+ );
+ try depTokenizer("foo.o: \\x",
+ \\target = {foo.o}
+ \\ERROR: illegal char 'x' at position 8: continuation expecting end-of-line
+ );
+ try depTokenizer("foo.o: \\\x0dx",
+ \\target = {foo.o}
+ \\ERROR: illegal char 'x' at position 9: continuation expecting end-of-line
+ );
+}
+
+test "error prereq - continuation expecting end-of-line" {
+ try depTokenizer("foo.o: foo.h\\\x0dx",
+ \\target = {foo.o}
+ \\ERROR: illegal char 'x' at position 14: continuation expecting end-of-line
+ );
+}
+
+// - tokenize input, emit textual representation, and compare to expect
+fn depTokenizer(input: []const u8, expect: []const u8) !void {
+ var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);
+ const arena = &arena_allocator.allocator;
+ defer arena_allocator.deinit();
+
+ var it: Tokenizer = .{ .bytes = input };
+ var buffer = try std.ArrayListSentineled(u8, 0).initSize(arena, 0);
+ var resolve_buf = std.ArrayList(u8).init(arena);
+ var i: usize = 0;
+ while (it.next()) |token| {
+ if (i != 0) try buffer.appendSlice("\n");
+ switch (token) {
+ .target, .prereq => |bytes| {
+ try buffer.appendSlice(@tagName(token));
+ try buffer.appendSlice(" = {");
+ for (bytes) |b| {
+ try buffer.append(printable_char_tab[b]);
+ }
+ try buffer.appendSlice("}");
+ },
+ .target_must_resolve => {
+ try buffer.appendSlice("target = {");
+ try token.resolve(resolve_buf.writer());
+ for (resolve_buf.items) |b| {
+ try buffer.append(printable_char_tab[b]);
+ }
+ resolve_buf.items.len = 0;
+ try buffer.appendSlice("}");
+ },
+ else => {
+ try buffer.appendSlice("ERROR: ");
+ try token.printError(buffer.outStream());
+ break;
+ },
+ }
+ i += 1;
+ }
+ const got: []const u8 = buffer.span();
+
+ if (std.mem.eql(u8, expect, got)) {
+ testing.expect(true);
+ return;
+ }
+
+ const out = std.io.getStdErr().writer();
+
+ try out.writeAll("\n");
+ try printSection(out, "<<<< input", input);
+ try printSection(out, "==== expect", expect);
+ try printSection(out, ">>>> got", got);
+ try printRuler(out);
+
+ testing.expect(false);
+}
+
+fn printSection(out: anytype, label: []const u8, bytes: []const u8) !void {
+ try printLabel(out, label, bytes);
+ try hexDump(out, bytes);
+ try printRuler(out);
+ try out.writeAll(bytes);
+ try out.writeAll("\n");
+}
+
+fn printLabel(out: anytype, label: []const u8, bytes: []const u8) !void {
+ var buf: [80]u8 = undefined;
+ var text = try std.fmt.bufPrint(buf[0..], "{} {} bytes ", .{ label, bytes.len });
+ try out.writeAll(text);
+ var i: usize = text.len;
+ const end = 79;
+ while (i < 79) : (i += 1) {
+ try out.writeAll(&[_]u8{label[0]});
+ }
+ try out.writeAll("\n");
+}
+
+fn printRuler(out: anytype) !void {
+ var i: usize = 0;
+ const end = 79;
+ while (i < 79) : (i += 1) {
+ try out.writeAll("-");
+ }
+ try out.writeAll("\n");
+}
+
+fn hexDump(out: anytype, bytes: []const u8) !void {
+ const n16 = bytes.len >> 4;
+ var line: usize = 0;
+ var offset: usize = 0;
+ while (line < n16) : (line += 1) {
+ try hexDump16(out, offset, bytes[offset .. offset + 16]);
+ offset += 16;
+ }
+
+ const n = bytes.len & 0x0f;
+ if (n > 0) {
+ try printDecValue(out, offset, 8);
+ try out.writeAll(":");
+ try out.writeAll(" ");
+ var end1 = std.math.min(offset + n, offset + 8);
+ for (bytes[offset..end1]) |b| {
+ try out.writeAll(" ");
+ try printHexValue(out, b, 2);
+ }
+ var end2 = offset + n;
+ if (end2 > end1) {
+ try out.writeAll(" ");
+ for (bytes[end1..end2]) |b| {
+ try out.writeAll(" ");
+ try printHexValue(out, b, 2);
+ }
+ }
+ const short = 16 - n;
+ var i: usize = 0;
+ while (i < short) : (i += 1) {
+ try out.writeAll(" ");
+ }
+ if (end2 > end1) {
+ try out.writeAll(" |");
+ } else {
+ try out.writeAll(" |");
+ }
+ try printCharValues(out, bytes[offset..end2]);
+ try out.writeAll("|\n");
+ offset += n;
+ }
+
+ try printDecValue(out, offset, 8);
+ try out.writeAll(":");
+ try out.writeAll("\n");
+}
+
+fn hexDump16(out: anytype, offset: usize, bytes: []const u8) !void {
+ try printDecValue(out, offset, 8);
+ try out.writeAll(":");
+ try out.writeAll(" ");
+ for (bytes[0..8]) |b| {
+ try out.writeAll(" ");
+ try printHexValue(out, b, 2);
+ }
+ try out.writeAll(" ");
+ for (bytes[8..16]) |b| {
+ try out.writeAll(" ");
+ try printHexValue(out, b, 2);
+ }
+ try out.writeAll(" |");
+ try printCharValues(out, bytes);
+ try out.writeAll("|\n");
+}
+
+fn printDecValue(out: anytype, value: u64, width: u8) !void {
+ var buffer: [20]u8 = undefined;
+ const len = std.fmt.formatIntBuf(buffer[0..], value, 10, false, .{ .width = width, .fill = '0' });
+ try out.writeAll(buffer[0..len]);
+}
+
+fn printHexValue(out: anytype, value: u64, width: u8) !void {
+ var buffer: [16]u8 = undefined;
+ const len = std.fmt.formatIntBuf(buffer[0..], value, 16, false, .{ .width = width, .fill = '0' });
+ try out.writeAll(buffer[0..len]);
+}
+
+fn printCharValues(out: anytype, bytes: []const u8) !void {
+ for (bytes) |b| {
+ try out.writeAll(&[_]u8{printable_char_tab[b]});
+ }
+}
+
+fn printUnderstandableChar(out: anytype, char: u8) !void {
+ if (!std.ascii.isPrint(char) or char == ' ') {
+ try out.print("\\x{X:0>2}", .{char});
+ } else {
+ try out.print("'{c}'", .{printable_char_tab[char]});
+ }
+}
+
+// zig fmt: off
+const printable_char_tab: [256]u8 = (
+ "................................ !\"#$%&'()*+,-./0123456789:;<=>?" ++
+ "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~." ++
+ "................................................................" ++
+ "................................................................"
+).*;
+
diff --git a/src/Module.zig b/src/Module.zig
new file mode 100644
index 0000000000000000000000000000000000000000..ebe7cdfb1e15e3209884153440d04dd655b3a014
--- /dev/null
+++ b/src/Module.zig
@@ -0,0 +1,3235 @@
+const Module = @This();
+const std = @import("std");
+const Compilation = @import("Compilation.zig");
+const mem = std.mem;
+const Allocator = std.mem.Allocator;
+const ArrayListUnmanaged = std.ArrayListUnmanaged;
+const Value = @import("value.zig").Value;
+const Type = @import("type.zig").Type;
+const TypedValue = @import("TypedValue.zig");
+const assert = std.debug.assert;
+const log = std.log.scoped(.module);
+const BigIntConst = std.math.big.int.Const;
+const BigIntMutable = std.math.big.int.Mutable;
+const Target = std.Target;
+const Package = @import("Package.zig");
+const link = @import("link.zig");
+const ir = @import("ir.zig");
+const zir = @import("zir.zig");
+const Inst = ir.Inst;
+const Body = ir.Body;
+const ast = std.zig.ast;
+const trace = @import("tracy.zig").trace;
+const astgen = @import("astgen.zig");
+const zir_sema = @import("zir_sema.zig");
+
+/// General-purpose allocator. Used for both temporary and long-term storage.
+gpa: *Allocator,
+comp: *Compilation,
+
+/// Where our incremental compilation metadata serialization will go.
+zig_cache_artifact_directory: Compilation.Directory,
+/// Pointer to externally managed resource. `null` if there is no zig file being compiled.
+root_pkg: *Package,
+/// Module owns this resource.
+/// The `Scope` is either a `Scope.ZIRModule` or `Scope.File`.
+root_scope: *Scope,
+/// It's rare for a decl to be exported, so we save memory by having a sparse map of
+/// Decl pointers to details about them being exported.
+/// The Export memory is owned by the `export_owners` table; the slice itself is owned by this table.
+decl_exports: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{},
+/// We track which export is associated with the given symbol name for quick
+/// detection of symbol collisions.
+symbol_exports: std.StringArrayHashMapUnmanaged(*Export) = .{},
+/// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl
+/// is modified. Note that the key of this table is not the Decl being exported, but the Decl that
+/// is performing the export of another Decl.
+/// This table owns the Export memory.
+export_owners: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{},
+/// Maps fully qualified namespaced names to the Decl struct for them.
+decl_table: std.ArrayHashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql, false) = .{},
+/// We optimize memory usage for a compilation with no compile errors by storing the
+/// error messages and mapping outside of `Decl`.
+/// The ErrorMsg memory is owned by the decl, using Module's general purpose allocator.
+/// Note that a Decl can succeed but the Fn it represents can fail. In this case,
+/// a Decl can have a failed_decls entry but have analysis status of success.
+failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *Compilation.ErrorMsg) = .{},
+/// Using a map here for consistency with the other fields here.
+/// The ErrorMsg memory is owned by the `Scope`, using Module's general purpose allocator.
+failed_files: std.AutoArrayHashMapUnmanaged(*Scope, *Compilation.ErrorMsg) = .{},
+/// Using a map here for consistency with the other fields here.
+/// The ErrorMsg memory is owned by the `Export`, using Module's general purpose allocator.
+failed_exports: std.AutoArrayHashMapUnmanaged(*Export, *Compilation.ErrorMsg) = .{},
+
+next_anon_name_index: usize = 0,
+
+/// Candidates for deletion. After a semantic analysis update completes, this list
+/// contains Decls that need to be deleted if they end up having no references to them.
+deletion_set: ArrayListUnmanaged(*Decl) = .{},
+
+/// Error tags and their values, tag names are duped with mod.gpa.
+global_error_set: std.StringHashMapUnmanaged(u16) = .{},
+
+/// Incrementing integer used to compare against the corresponding Decl
+/// field to determine whether a Decl's status applies to an ongoing update, or a
+/// previous analysis.
+generation: u32 = 0,
+
+pub const Export = struct {
+ options: std.builtin.ExportOptions,
+ /// Byte offset into the file that contains the export directive.
+ src: usize,
+ /// Represents the position of the export, if any, in the output file.
+ link: link.File.Elf.Export,
+ /// The Decl that performs the export. Note that this is *not* the Decl being exported.
+ owner_decl: *Decl,
+ /// The Decl being exported. Note this is *not* the Decl performing the export.
+ exported_decl: *Decl,
+ status: enum {
+ in_progress,
+ failed,
+ /// Indicates that the failure was due to a temporary issue, such as an I/O error
+ /// when writing to the output file. Retrying the export may succeed.
+ failed_retryable,
+ complete,
+ },
+};
+
+pub const Decl = struct {
+ /// This name is relative to the containing namespace of the decl. It uses a null-termination
+ /// to save bytes, since there can be a lot of decls in a compilation. The null byte is not allowed
+ /// in symbol names, because executable file formats use null-terminated strings for symbol names.
+ /// All Decls have names, even values that are not bound to a zig namespace. This is necessary for
+ /// mapping them to an address in the output file.
+ /// Memory owned by this decl, using Module's allocator.
+ name: [*:0]const u8,
+ /// The direct parent container of the Decl. This is either a `Scope.Container` or `Scope.ZIRModule`.
+ /// Reference to externally owned memory.
+ scope: *Scope,
+ /// The AST Node decl index or ZIR Inst index that contains this declaration.
+ /// Must be recomputed when the corresponding source file is modified.
+ src_index: usize,
+ /// The most recent value of the Decl after a successful semantic analysis.
+ typed_value: union(enum) {
+ never_succeeded: void,
+ most_recent: TypedValue.Managed,
+ },
+ /// Represents the "shallow" analysis status. For example, for decls that are functions,
+ /// the function type is analyzed with this set to `in_progress`, however, the semantic
+ /// analysis of the function body is performed with this value set to `success`. Functions
+ /// have their own analysis status field.
+ analysis: enum {
+ /// This Decl corresponds to an AST Node that has not been referenced yet, and therefore
+ /// because of Zig's lazy declaration analysis, it will remain unanalyzed until referenced.
+ unreferenced,
+ /// Semantic analysis for this Decl is running right now. This state detects dependency loops.
+ in_progress,
+ /// This Decl might be OK but it depends on another one which did not successfully complete
+ /// semantic analysis.
+ dependency_failure,
+ /// Semantic analysis failure.
+ /// There will be a corresponding ErrorMsg in Module.failed_decls.
+ sema_failure,
+ /// There will be a corresponding ErrorMsg in Module.failed_decls.
+ /// This indicates the failure was something like running out of disk space,
+ /// and attempting semantic analysis again may succeed.
+ sema_failure_retryable,
+ /// There will be a corresponding ErrorMsg in Module.failed_decls.
+ codegen_failure,
+ /// There will be a corresponding ErrorMsg in Module.failed_decls.
+ /// This indicates the failure was something like running out of disk space,
+ /// and attempting codegen again may succeed.
+ codegen_failure_retryable,
+ /// Everything is done. During an update, this Decl may be out of date, depending
+ /// on its dependencies. The `generation` field can be used to determine if this
+ /// completion status occurred before or after a given update.
+ complete,
+ /// A Module update is in progress, and this Decl has been flagged as being known
+ /// to require re-analysis.
+ outdated,
+ },
+ /// This flag is set when this Decl is added to a check_for_deletion set, and cleared
+ /// when removed.
+ deletion_flag: bool,
+ /// Whether the corresponding AST decl has a `pub` keyword.
+ is_pub: bool,
+
+ /// An integer that can be checked against the corresponding incrementing
+ /// generation field of Module. This is used to determine whether `complete` status
+ /// represents pre- or post- re-analysis.
+ generation: u32,
+
+ /// Represents the position of the code in the output file.
+ /// This is populated regardless of semantic analysis and code generation.
+ link: link.File.LinkBlock,
+
+ /// Represents the function in the linked output file, if the `Decl` is a function.
+ /// This is stored here and not in `Fn` because `Decl` survives across updates but
+ /// `Fn` does not.
+ /// TODO Look into making `Fn` a longer lived structure and moving this field there
+ /// to save on memory usage.
+ fn_link: link.File.LinkFn,
+
+ contents_hash: std.zig.SrcHash,
+
+ /// The shallow set of other decls whose typed_value could possibly change if this Decl's
+ /// typed_value is modified.
+ dependants: DepsTable = .{},
+ /// The shallow set of other decls whose typed_value changing indicates that this Decl's
+ /// typed_value may need to be regenerated.
+ dependencies: DepsTable = .{},
+
+ /// The reason this is not `std.AutoArrayHashMapUnmanaged` is a workaround for
+ /// stage1 compiler giving me: `error: struct 'Module.Decl' depends on itself`
+ pub const DepsTable = std.ArrayHashMapUnmanaged(*Decl, void, std.array_hash_map.getAutoHashFn(*Decl), std.array_hash_map.getAutoEqlFn(*Decl), false);
+
+ pub fn destroy(self: *Decl, gpa: *Allocator) void {
+ gpa.free(mem.spanZ(self.name));
+ if (self.typedValueManaged()) |tvm| {
+ tvm.deinit(gpa);
+ }
+ self.dependants.deinit(gpa);
+ self.dependencies.deinit(gpa);
+ gpa.destroy(self);
+ }
+
+ pub fn src(self: Decl) usize {
+ switch (self.scope.tag) {
+ .container => {
+ const container = @fieldParentPtr(Scope.Container, "base", self.scope);
+ const tree = container.file_scope.contents.tree;
+ // TODO Container should have it's own decls()
+ const decl_node = tree.root_node.decls()[self.src_index];
+ return tree.token_locs[decl_node.firstToken()].start;
+ },
+ .zir_module => {
+ const zir_module = @fieldParentPtr(Scope.ZIRModule, "base", self.scope);
+ const module = zir_module.contents.module;
+ const src_decl = module.decls[self.src_index];
+ return src_decl.inst.src;
+ },
+ .file, .block => unreachable,
+ .gen_zir => unreachable,
+ .local_val => unreachable,
+ .local_ptr => unreachable,
+ .decl => unreachable,
+ }
+ }
+
+ pub fn fullyQualifiedNameHash(self: Decl) Scope.NameHash {
+ return self.scope.fullyQualifiedNameHash(mem.spanZ(self.name));
+ }
+
+ pub fn typedValue(self: *Decl) error{AnalysisFail}!TypedValue {
+ const tvm = self.typedValueManaged() orelse return error.AnalysisFail;
+ return tvm.typed_value;
+ }
+
+ pub fn value(self: *Decl) error{AnalysisFail}!Value {
+ return (try self.typedValue()).val;
+ }
+
+ pub fn dump(self: *Decl) void {
+ const loc = std.zig.findLineColumn(self.scope.source.bytes, self.src);
+ std.debug.print("{}:{}:{} name={} status={}", .{
+ self.scope.sub_file_path,
+ loc.line + 1,
+ loc.column + 1,
+ mem.spanZ(self.name),
+ @tagName(self.analysis),
+ });
+ if (self.typedValueManaged()) |tvm| {
+ std.debug.print(" ty={} val={}", .{ tvm.typed_value.ty, tvm.typed_value.val });
+ }
+ std.debug.print("\n", .{});
+ }
+
+ pub fn typedValueManaged(self: *Decl) ?*TypedValue.Managed {
+ switch (self.typed_value) {
+ .most_recent => |*x| return x,
+ .never_succeeded => return null,
+ }
+ }
+
+ fn removeDependant(self: *Decl, other: *Decl) void {
+ self.dependants.removeAssertDiscard(other);
+ }
+
+ fn removeDependency(self: *Decl, other: *Decl) void {
+ self.dependencies.removeAssertDiscard(other);
+ }
+};
+
+/// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
+pub const Fn = struct {
+ /// This memory owned by the Decl's TypedValue.Managed arena allocator.
+ analysis: union(enum) {
+ queued: *ZIR,
+ in_progress,
+ /// There will be a corresponding ErrorMsg in Module.failed_decls
+ sema_failure,
+ /// This Fn might be OK but it depends on another Decl which did not successfully complete
+ /// semantic analysis.
+ dependency_failure,
+ success: Body,
+ },
+ owner_decl: *Decl,
+
+ /// This memory is temporary and points to stack memory for the duration
+ /// of Fn analysis.
+ pub const Analysis = struct {
+ inner_block: Scope.Block,
+ };
+
+ /// Contains un-analyzed ZIR instructions generated from Zig source AST.
+ pub const ZIR = struct {
+ body: zir.Module.Body,
+ arena: std.heap.ArenaAllocator.State,
+ };
+
+ /// For debugging purposes.
+ pub fn dump(self: *Fn, mod: Module) void {
+ std.debug.print("Module.Function(name={}) ", .{self.owner_decl.name});
+ switch (self.analysis) {
+ .queued => {
+ std.debug.print("queued\n", .{});
+ },
+ .in_progress => {
+ std.debug.print("in_progress\n", .{});
+ },
+ else => {
+ std.debug.print("\n", .{});
+ zir.dumpFn(mod, self);
+ },
+ }
+ }
+};
+
+pub const Var = struct {
+ /// if is_extern == true this is undefined
+ init: Value,
+ owner_decl: *Decl,
+
+ is_extern: bool,
+ is_mutable: bool,
+ is_threadlocal: bool,
+};
+
+pub const Scope = struct {
+ tag: Tag,
+
+ pub const NameHash = [16]u8;
+
+ pub fn cast(base: *Scope, comptime T: type) ?*T {
+ if (base.tag != T.base_tag)
+ return null;
+
+ return @fieldParentPtr(T, "base", base);
+ }
+
+ /// Asserts the scope has a parent which is a DeclAnalysis and
+ /// returns the arena Allocator.
+ pub fn arena(self: *Scope) *Allocator {
+ switch (self.tag) {
+ .block => return self.cast(Block).?.arena,
+ .decl => return &self.cast(DeclAnalysis).?.arena.allocator,
+ .gen_zir => return self.cast(GenZIR).?.arena,
+ .local_val => return self.cast(LocalVal).?.gen_zir.arena,
+ .local_ptr => return self.cast(LocalPtr).?.gen_zir.arena,
+ .zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator,
+ .file => unreachable,
+ .container => unreachable,
+ }
+ }
+
+ /// If the scope has a parent which is a `DeclAnalysis`,
+ /// returns the `Decl`, otherwise returns `null`.
+ pub fn decl(self: *Scope) ?*Decl {
+ return switch (self.tag) {
+ .block => self.cast(Block).?.decl,
+ .gen_zir => self.cast(GenZIR).?.decl,
+ .local_val => self.cast(LocalVal).?.gen_zir.decl,
+ .local_ptr => self.cast(LocalPtr).?.gen_zir.decl,
+ .decl => self.cast(DeclAnalysis).?.decl,
+ .zir_module => null,
+ .file => null,
+ .container => null,
+ };
+ }
+
+ /// Asserts the scope has a parent which is a ZIRModule or Container and
+ /// returns it.
+ pub fn namespace(self: *Scope) *Scope {
+ switch (self.tag) {
+ .block => return self.cast(Block).?.decl.scope,
+ .gen_zir => return self.cast(GenZIR).?.decl.scope,
+ .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope,
+ .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope,
+ .decl => return self.cast(DeclAnalysis).?.decl.scope,
+ .file => return &self.cast(File).?.root_container.base,
+ .zir_module, .container => return self,
+ }
+ }
+
+ /// Must generate unique bytes with no collisions with other decls.
+ /// The point of hashing here is only to limit the number of bytes of
+ /// the unique identifier to a fixed size (16 bytes).
+ pub fn fullyQualifiedNameHash(self: *Scope, name: []const u8) NameHash {
+ switch (self.tag) {
+ .block => unreachable,
+ .gen_zir => unreachable,
+ .local_val => unreachable,
+ .local_ptr => unreachable,
+ .decl => unreachable,
+ .file => unreachable,
+ .zir_module => return self.cast(ZIRModule).?.fullyQualifiedNameHash(name),
+ .container => return self.cast(Container).?.fullyQualifiedNameHash(name),
+ }
+ }
+
+ /// Asserts the scope is a child of a File and has an AST tree and returns the tree.
+ pub fn tree(self: *Scope) *ast.Tree {
+ switch (self.tag) {
+ .file => return self.cast(File).?.contents.tree,
+ .zir_module => unreachable,
+ .decl => return self.cast(DeclAnalysis).?.decl.scope.cast(Container).?.file_scope.contents.tree,
+ .block => return self.cast(Block).?.decl.scope.cast(Container).?.file_scope.contents.tree,
+ .gen_zir => return self.cast(GenZIR).?.decl.scope.cast(Container).?.file_scope.contents.tree,
+ .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope.cast(Container).?.file_scope.contents.tree,
+ .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope.cast(Container).?.file_scope.contents.tree,
+ .container => return self.cast(Container).?.file_scope.contents.tree,
+ }
+ }
+
+ /// Asserts the scope is a child of a `GenZIR` and returns it.
+ pub fn getGenZIR(self: *Scope) *GenZIR {
+ return switch (self.tag) {
+ .block => unreachable,
+ .gen_zir => self.cast(GenZIR).?,
+ .local_val => return self.cast(LocalVal).?.gen_zir,
+ .local_ptr => return self.cast(LocalPtr).?.gen_zir,
+ .decl => unreachable,
+ .zir_module => unreachable,
+ .file => unreachable,
+ .container => unreachable,
+ };
+ }
+
+ /// Asserts the scope has a parent which is a ZIRModule, Contaienr or File and
+ /// returns the sub_file_path field.
+ pub fn subFilePath(base: *Scope) []const u8 {
+ switch (base.tag) {
+ .container => return @fieldParentPtr(Container, "base", base).file_scope.sub_file_path,
+ .file => return @fieldParentPtr(File, "base", base).sub_file_path,
+ .zir_module => return @fieldParentPtr(ZIRModule, "base", base).sub_file_path,
+ .block => unreachable,
+ .gen_zir => unreachable,
+ .local_val => unreachable,
+ .local_ptr => unreachable,
+ .decl => unreachable,
+ }
+ }
+
+ pub fn unload(base: *Scope, gpa: *Allocator) void {
+ switch (base.tag) {
+ .file => return @fieldParentPtr(File, "base", base).unload(gpa),
+ .zir_module => return @fieldParentPtr(ZIRModule, "base", base).unload(gpa),
+ .block => unreachable,
+ .gen_zir => unreachable,
+ .local_val => unreachable,
+ .local_ptr => unreachable,
+ .decl => unreachable,
+ .container => unreachable,
+ }
+ }
+
+ pub fn getSource(base: *Scope, module: *Module) ![:0]const u8 {
+ switch (base.tag) {
+ .container => return @fieldParentPtr(Container, "base", base).file_scope.getSource(module),
+ .file => return @fieldParentPtr(File, "base", base).getSource(module),
+ .zir_module => return @fieldParentPtr(ZIRModule, "base", base).getSource(module),
+ .gen_zir => unreachable,
+ .local_val => unreachable,
+ .local_ptr => unreachable,
+ .block => unreachable,
+ .decl => unreachable,
+ }
+ }
+
+ /// Asserts the scope is a namespace Scope and removes the Decl from the namespace.
+ pub fn removeDecl(base: *Scope, child: *Decl) void {
+ switch (base.tag) {
+ .container => return @fieldParentPtr(Container, "base", base).removeDecl(child),
+ .zir_module => return @fieldParentPtr(ZIRModule, "base", base).removeDecl(child),
+ .file => unreachable,
+ .block => unreachable,
+ .gen_zir => unreachable,
+ .local_val => unreachable,
+ .local_ptr => unreachable,
+ .decl => unreachable,
+ }
+ }
+
+ /// Asserts the scope is a File or ZIRModule and deinitializes it, then deallocates it.
+ pub fn destroy(base: *Scope, gpa: *Allocator) void {
+ switch (base.tag) {
+ .file => {
+ const scope_file = @fieldParentPtr(File, "base", base);
+ scope_file.deinit(gpa);
+ gpa.destroy(scope_file);
+ },
+ .zir_module => {
+ const scope_zir_module = @fieldParentPtr(ZIRModule, "base", base);
+ scope_zir_module.deinit(gpa);
+ gpa.destroy(scope_zir_module);
+ },
+ .block => unreachable,
+ .gen_zir => unreachable,
+ .local_val => unreachable,
+ .local_ptr => unreachable,
+ .decl => unreachable,
+ .container => unreachable,
+ }
+ }
+
+ fn name_hash_hash(x: NameHash) u32 {
+ return @truncate(u32, @bitCast(u128, x));
+ }
+
+ fn name_hash_eql(a: NameHash, b: NameHash) bool {
+ return @bitCast(u128, a) == @bitCast(u128, b);
+ }
+
+ pub const Tag = enum {
+ /// .zir source code.
+ zir_module,
+ /// .zig source code.
+ file,
+ /// struct, enum or union, every .file contains one of these.
+ container,
+ block,
+ decl,
+ gen_zir,
+ local_val,
+ local_ptr,
+ };
+
+ pub const Container = struct {
+ pub const base_tag: Tag = .container;
+ base: Scope = Scope{ .tag = base_tag },
+
+ file_scope: *Scope.File,
+
+ /// Direct children of the file.
+ decls: std.AutoArrayHashMapUnmanaged(*Decl, void),
+
+ // TODO implement container types and put this in a status union
+ // ty: Type
+
+ pub fn deinit(self: *Container, gpa: *Allocator) void {
+ self.decls.deinit(gpa);
+ self.* = undefined;
+ }
+
+ pub fn removeDecl(self: *Container, child: *Decl) void {
+ _ = self.decls.remove(child);
+ }
+
+ pub fn fullyQualifiedNameHash(self: *Container, name: []const u8) NameHash {
+ // TODO container scope qualified names.
+ return std.zig.hashSrc(name);
+ }
+ };
+
+ pub const File = struct {
+ pub const base_tag: Tag = .file;
+ base: Scope = Scope{ .tag = base_tag },
+
+ /// Relative to the owning package's root_src_dir.
+ /// Reference to external memory, not owned by File.
+ sub_file_path: []const u8,
+ source: union(enum) {
+ unloaded: void,
+ bytes: [:0]const u8,
+ },
+ contents: union {
+ not_available: void,
+ tree: *ast.Tree,
+ },
+ status: enum {
+ never_loaded,
+ unloaded_success,
+ unloaded_parse_failure,
+ loaded_success,
+ },
+
+ root_container: Container,
+
+ pub fn unload(self: *File, gpa: *Allocator) void {
+ switch (self.status) {
+ .never_loaded,
+ .unloaded_parse_failure,
+ .unloaded_success,
+ => {},
+
+ .loaded_success => {
+ self.contents.tree.deinit();
+ self.status = .unloaded_success;
+ },
+ }
+ switch (self.source) {
+ .bytes => |bytes| {
+ gpa.free(bytes);
+ self.source = .{ .unloaded = {} };
+ },
+ .unloaded => {},
+ }
+ }
+
+ pub fn deinit(self: *File, gpa: *Allocator) void {
+ self.root_container.deinit(gpa);
+ self.unload(gpa);
+ self.* = undefined;
+ }
+
+ pub fn dumpSrc(self: *File, src: usize) void {
+ const loc = std.zig.findLineColumn(self.source.bytes, src);
+ std.debug.print("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });
+ }
+
+ pub fn getSource(self: *File, module: *Module) ![:0]const u8 {
+ switch (self.source) {
+ .unloaded => {
+ const source = try module.root_pkg.root_src_directory.handle.readFileAllocOptions(
+ module.gpa,
+ self.sub_file_path,
+ std.math.maxInt(u32),
+ null,
+ 1,
+ 0,
+ );
+ self.source = .{ .bytes = source };
+ return source;
+ },
+ .bytes => |bytes| return bytes,
+ }
+ }
+ };
+
+ pub const ZIRModule = struct {
+ pub const base_tag: Tag = .zir_module;
+ base: Scope = Scope{ .tag = base_tag },
+ /// Relative to the owning package's root_src_dir.
+ /// Reference to external memory, not owned by ZIRModule.
+ sub_file_path: []const u8,
+ source: union(enum) {
+ unloaded: void,
+ bytes: [:0]const u8,
+ },
+ contents: union {
+ not_available: void,
+ module: *zir.Module,
+ },
+ status: enum {
+ never_loaded,
+ unloaded_success,
+ unloaded_parse_failure,
+ unloaded_sema_failure,
+
+ loaded_sema_failure,
+ loaded_success,
+ },
+
+ /// Even though .zir files only have 1 module, this set is still needed
+ /// because of anonymous Decls, which can exist in the global set, but
+ /// not this one.
+ decls: ArrayListUnmanaged(*Decl),
+
+ pub fn unload(self: *ZIRModule, gpa: *Allocator) void {
+ switch (self.status) {
+ .never_loaded,
+ .unloaded_parse_failure,
+ .unloaded_sema_failure,
+ .unloaded_success,
+ => {},
+
+ .loaded_success => {
+ self.contents.module.deinit(gpa);
+ gpa.destroy(self.contents.module);
+ self.contents = .{ .not_available = {} };
+ self.status = .unloaded_success;
+ },
+ .loaded_sema_failure => {
+ self.contents.module.deinit(gpa);
+ gpa.destroy(self.contents.module);
+ self.contents = .{ .not_available = {} };
+ self.status = .unloaded_sema_failure;
+ },
+ }
+ switch (self.source) {
+ .bytes => |bytes| {
+ gpa.free(bytes);
+ self.source = .{ .unloaded = {} };
+ },
+ .unloaded => {},
+ }
+ }
+
+ pub fn deinit(self: *ZIRModule, gpa: *Allocator) void {
+ self.decls.deinit(gpa);
+ self.unload(gpa);
+ self.* = undefined;
+ }
+
+ pub fn removeDecl(self: *ZIRModule, child: *Decl) void {
+ for (self.decls.items) |item, i| {
+ if (item == child) {
+ _ = self.decls.swapRemove(i);
+ return;
+ }
+ }
+ }
+
+ pub fn dumpSrc(self: *ZIRModule, src: usize) void {
+ const loc = std.zig.findLineColumn(self.source.bytes, src);
+ std.debug.print("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });
+ }
+
+ pub fn getSource(self: *ZIRModule, module: *Module) ![:0]const u8 {
+ switch (self.source) {
+ .unloaded => {
+ const source = try module.root_pkg.root_src_directory.handle.readFileAllocOptions(
+ module.gpa,
+ self.sub_file_path,
+ std.math.maxInt(u32),
+ null,
+ 1,
+ 0,
+ );
+ self.source = .{ .bytes = source };
+ return source;
+ },
+ .bytes => |bytes| return bytes,
+ }
+ }
+
+ pub fn fullyQualifiedNameHash(self: *ZIRModule, name: []const u8) NameHash {
+ // ZIR modules only have 1 file with all decls global in the same namespace.
+ return std.zig.hashSrc(name);
+ }
+ };
+
+ /// This is a temporary structure, references to it are valid only
+ /// during semantic analysis of the block.
+ pub const Block = struct {
+ pub const base_tag: Tag = .block;
+ base: Scope = Scope{ .tag = base_tag },
+ parent: ?*Block,
+ func: ?*Fn,
+ decl: *Decl,
+ instructions: ArrayListUnmanaged(*Inst),
+ /// Points to the arena allocator of DeclAnalysis
+ arena: *Allocator,
+ label: ?Label = null,
+ is_comptime: bool,
+
+ pub const Label = struct {
+ zir_block: *zir.Inst.Block,
+ results: ArrayListUnmanaged(*Inst),
+ block_inst: *Inst.Block,
+ };
+ };
+
+ /// This is a temporary structure, references to it are valid only
+ /// during semantic analysis of the decl.
+ pub const DeclAnalysis = struct {
+ pub const base_tag: Tag = .decl;
+ base: Scope = Scope{ .tag = base_tag },
+ decl: *Decl,
+ arena: std.heap.ArenaAllocator,
+ };
+
+ /// This is a temporary structure, references to it are valid only
+ /// during semantic analysis of the decl.
+ pub const GenZIR = struct {
+ pub const base_tag: Tag = .gen_zir;
+ base: Scope = Scope{ .tag = base_tag },
+ /// Parents can be: `GenZIR`, `ZIRModule`, `File`
+ parent: *Scope,
+ decl: *Decl,
+ arena: *Allocator,
+ /// The first N instructions in a function body ZIR are arg instructions.
+ instructions: std.ArrayListUnmanaged(*zir.Inst) = .{},
+ label: ?Label = null,
+
+ pub const Label = struct {
+ token: ast.TokenIndex,
+ block_inst: *zir.Inst.Block,
+ result_loc: astgen.ResultLoc,
+ };
+ };
+
+ /// This is always a `const` local and importantly the `inst` is a value type, not a pointer.
+ /// This structure lives as long as the AST generation of the Block
+ /// node that contains the variable.
+ pub const LocalVal = struct {
+ pub const base_tag: Tag = .local_val;
+ base: Scope = Scope{ .tag = base_tag },
+ /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`.
+ parent: *Scope,
+ gen_zir: *GenZIR,
+ name: []const u8,
+ inst: *zir.Inst,
+ };
+
+ /// This could be a `const` or `var` local. It has a pointer instead of a value.
+ /// This structure lives as long as the AST generation of the Block
+ /// node that contains the variable.
+ pub const LocalPtr = struct {
+ pub const base_tag: Tag = .local_ptr;
+ base: Scope = Scope{ .tag = base_tag },
+ /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`.
+ parent: *Scope,
+ gen_zir: *GenZIR,
+ name: []const u8,
+ ptr: *zir.Inst,
+ };
+};
+
+pub const InnerError = error{ OutOfMemory, AnalysisFail };
+
+pub fn deinit(self: *Module) void {
+ const gpa = self.gpa;
+
+ self.zig_cache_artifact_directory.handle.close();
+
+ self.deletion_set.deinit(gpa);
+
+ for (self.decl_table.items()) |entry| {
+ entry.value.destroy(gpa);
+ }
+ self.decl_table.deinit(gpa);
+
+ for (self.failed_decls.items()) |entry| {
+ entry.value.destroy(gpa);
+ }
+ self.failed_decls.deinit(gpa);
+
+ for (self.failed_files.items()) |entry| {
+ entry.value.destroy(gpa);
+ }
+ self.failed_files.deinit(gpa);
+
+ for (self.failed_exports.items()) |entry| {
+ entry.value.destroy(gpa);
+ }
+ self.failed_exports.deinit(gpa);
+
+ for (self.decl_exports.items()) |entry| {
+ const export_list = entry.value;
+ gpa.free(export_list);
+ }
+ self.decl_exports.deinit(gpa);
+
+ for (self.export_owners.items()) |entry| {
+ freeExportList(gpa, entry.value);
+ }
+ self.export_owners.deinit(gpa);
+
+ self.symbol_exports.deinit(gpa);
+ self.root_scope.destroy(gpa);
+
+ var it = self.global_error_set.iterator();
+ while (it.next()) |entry| {
+ gpa.free(entry.key);
+ }
+ self.global_error_set.deinit(gpa);
+}
+
+fn freeExportList(gpa: *Allocator, export_list: []*Export) void {
+ for (export_list) |exp| {
+ gpa.free(exp.options.name);
+ gpa.destroy(exp);
+ }
+ gpa.free(export_list);
+}
+
+pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const subsequent_analysis = switch (decl.analysis) {
+ .in_progress => unreachable,
+
+ .sema_failure,
+ .sema_failure_retryable,
+ .codegen_failure,
+ .dependency_failure,
+ .codegen_failure_retryable,
+ => return error.AnalysisFail,
+
+ .complete => return,
+
+ .outdated => blk: {
+ log.debug("re-analyzing {}\n", .{decl.name});
+
+ // The exports this Decl performs will be re-discovered, so we remove them here
+ // prior to re-analysis.
+ self.deleteDeclExports(decl);
+ // Dependencies will be re-discovered, so we remove them here prior to re-analysis.
+ for (decl.dependencies.items()) |entry| {
+ const dep = entry.key;
+ dep.removeDependant(decl);
+ if (dep.dependants.items().len == 0 and !dep.deletion_flag) {
+ // We don't perform a deletion here, because this Decl or another one
+ // may end up referencing it before the update is complete.
+ dep.deletion_flag = true;
+ try self.deletion_set.append(self.gpa, dep);
+ }
+ }
+ decl.dependencies.clearRetainingCapacity();
+
+ break :blk true;
+ },
+
+ .unreferenced => false,
+ };
+
+ const type_changed = if (self.root_scope.cast(Scope.ZIRModule)) |zir_module|
+ try zir_sema.analyzeZirDecl(self, decl, zir_module.contents.module.decls[decl.src_index])
+ else
+ self.astGenAndAnalyzeDecl(decl) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ error.AnalysisFail => return error.AnalysisFail,
+ else => {
+ try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
+ self.failed_decls.putAssumeCapacityNoClobber(decl, try Compilation.ErrorMsg.create(
+ self.gpa,
+ decl.src(),
+ "unable to analyze: {}",
+ .{@errorName(err)},
+ ));
+ decl.analysis = .sema_failure_retryable;
+ return error.AnalysisFail;
+ },
+ };
+
+ if (subsequent_analysis) {
+ // We may need to chase the dependants and re-analyze them.
+ // However, if the decl is a function, and the type is the same, we do not need to.
+ if (type_changed or decl.typed_value.most_recent.typed_value.val.tag() != .function) {
+ for (decl.dependants.items()) |entry| {
+ const dep = entry.key;
+ switch (dep.analysis) {
+ .unreferenced => unreachable,
+ .in_progress => unreachable,
+ .outdated => continue, // already queued for update
+
+ .dependency_failure,
+ .sema_failure,
+ .sema_failure_retryable,
+ .codegen_failure,
+ .codegen_failure_retryable,
+ .complete,
+ => if (dep.generation != self.generation) {
+ try self.markOutdatedDecl(dep);
+ },
+ }
+ }
+ }
+ }
+}
+
+fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const container_scope = decl.scope.cast(Scope.Container).?;
+ const tree = try self.getAstTree(container_scope);
+ const ast_node = tree.root_node.decls()[decl.src_index];
+ switch (ast_node.tag) {
+ .FnProto => {
+ const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", ast_node);
+
+ decl.analysis = .in_progress;
+
+ // This arena allocator's memory is discarded at the end of this function. It is used
+ // to determine the type of the function, and hence the type of the decl, which is needed
+ // to complete the Decl analysis.
+ var fn_type_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
+ defer fn_type_scope_arena.deinit();
+ var fn_type_scope: Scope.GenZIR = .{
+ .decl = decl,
+ .arena = &fn_type_scope_arena.allocator,
+ .parent = decl.scope,
+ };
+ defer fn_type_scope.instructions.deinit(self.gpa);
+
+ decl.is_pub = fn_proto.getVisibToken() != null;
+ const body_node = fn_proto.getBodyNode() orelse
+ return self.failTok(&fn_type_scope.base, fn_proto.fn_token, "TODO implement extern functions", .{});
+
+ const param_decls = fn_proto.params();
+ const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_decls.len);
+
+ const fn_src = tree.token_locs[fn_proto.fn_token].start;
+ const type_type = try astgen.addZIRInstConst(self, &fn_type_scope.base, fn_src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.type_type),
+ });
+ const type_type_rl: astgen.ResultLoc = .{ .ty = type_type };
+ for (param_decls) |param_decl, i| {
+ const param_type_node = switch (param_decl.param_type) {
+ .any_type => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement anytype parameter", .{}),
+ .type_expr => |node| node,
+ };
+ param_types[i] = try astgen.expr(self, &fn_type_scope.base, type_type_rl, param_type_node);
+ }
+ if (fn_proto.getVarArgsToken()) |var_args_token| {
+ return self.failTok(&fn_type_scope.base, var_args_token, "TODO implement var args", .{});
+ }
+ if (fn_proto.getLibName()) |lib_name| {
+ return self.failNode(&fn_type_scope.base, lib_name, "TODO implement function library name", .{});
+ }
+ if (fn_proto.getAlignExpr()) |align_expr| {
+ return self.failNode(&fn_type_scope.base, align_expr, "TODO implement function align expression", .{});
+ }
+ if (fn_proto.getSectionExpr()) |sect_expr| {
+ return self.failNode(&fn_type_scope.base, sect_expr, "TODO implement function section expression", .{});
+ }
+ if (fn_proto.getCallconvExpr()) |callconv_expr| {
+ return self.failNode(
+ &fn_type_scope.base,
+ callconv_expr,
+ "TODO implement function calling convention expression",
+ .{},
+ );
+ }
+ const return_type_expr = switch (fn_proto.return_type) {
+ .Explicit => |node| node,
+ .InferErrorSet => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement inferred error sets", .{}),
+ .Invalid => |tok| return self.failTok(&fn_type_scope.base, tok, "unable to parse return type", .{}),
+ };
+
+ const return_type_inst = try astgen.expr(self, &fn_type_scope.base, type_type_rl, return_type_expr);
+ const fn_type_inst = try astgen.addZIRInst(self, &fn_type_scope.base, fn_src, zir.Inst.FnType, .{
+ .return_type = return_type_inst,
+ .param_types = param_types,
+ }, .{});
+
+ // We need the memory for the Type to go into the arena for the Decl
+ var decl_arena = std.heap.ArenaAllocator.init(self.gpa);
+ errdefer decl_arena.deinit();
+ const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
+
+ var block_scope: Scope.Block = .{
+ .parent = null,
+ .func = null,
+ .decl = decl,
+ .instructions = .{},
+ .arena = &decl_arena.allocator,
+ .is_comptime = false,
+ };
+ defer block_scope.instructions.deinit(self.gpa);
+
+ const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, fn_type_inst, .{
+ .instructions = fn_type_scope.instructions.items,
+ });
+ const new_func = try decl_arena.allocator.create(Fn);
+ const fn_payload = try decl_arena.allocator.create(Value.Payload.Function);
+
+ const fn_zir = blk: {
+ // This scope's arena memory is discarded after the ZIR generation
+ // pass completes, and semantic analysis of it completes.
+ var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
+ errdefer gen_scope_arena.deinit();
+ var gen_scope: Scope.GenZIR = .{
+ .decl = decl,
+ .arena = &gen_scope_arena.allocator,
+ .parent = decl.scope,
+ };
+ defer gen_scope.instructions.deinit(self.gpa);
+
+ // We need an instruction for each parameter, and they must be first in the body.
+ try gen_scope.instructions.resize(self.gpa, fn_proto.params_len);
+ var params_scope = &gen_scope.base;
+ for (fn_proto.params()) |param, i| {
+ const name_token = param.name_token.?;
+ const src = tree.token_locs[name_token].start;
+ const param_name = tree.tokenSlice(name_token); // TODO: call identifierTokenString
+ const arg = try gen_scope_arena.allocator.create(zir.Inst.Arg);
+ arg.* = .{
+ .base = .{
+ .tag = .arg,
+ .src = src,
+ },
+ .positionals = .{
+ .name = param_name,
+ },
+ .kw_args = .{},
+ };
+ gen_scope.instructions.items[i] = &arg.base;
+ const sub_scope = try gen_scope_arena.allocator.create(Scope.LocalVal);
+ sub_scope.* = .{
+ .parent = params_scope,
+ .gen_zir = &gen_scope,
+ .name = param_name,
+ .inst = &arg.base,
+ };
+ params_scope = &sub_scope.base;
+ }
+
+ const body_block = body_node.cast(ast.Node.Block).?;
+
+ try astgen.blockExpr(self, params_scope, body_block);
+
+ if (gen_scope.instructions.items.len == 0 or
+ !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn())
+ {
+ const src = tree.token_locs[body_block.rbrace].start;
+ _ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .returnvoid);
+ }
+
+ const fn_zir = try gen_scope_arena.allocator.create(Fn.ZIR);
+ fn_zir.* = .{
+ .body = .{
+ .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items),
+ },
+ .arena = gen_scope_arena.state,
+ };
+ break :blk fn_zir;
+ };
+
+ new_func.* = .{
+ .analysis = .{ .queued = fn_zir },
+ .owner_decl = decl,
+ };
+ fn_payload.* = .{ .func = new_func };
+
+ var prev_type_has_bits = false;
+ var type_changed = true;
+
+ if (decl.typedValueManaged()) |tvm| {
+ prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits();
+ type_changed = !tvm.typed_value.ty.eql(fn_type);
+
+ tvm.deinit(self.gpa);
+ }
+
+ decl_arena_state.* = decl_arena.state;
+ decl.typed_value = .{
+ .most_recent = .{
+ .typed_value = .{
+ .ty = fn_type,
+ .val = Value.initPayload(&fn_payload.base),
+ },
+ .arena = decl_arena_state,
+ },
+ };
+ decl.analysis = .complete;
+ decl.generation = self.generation;
+
+ if (fn_type.hasCodeGenBits()) {
+ // We don't fully codegen the decl until later, but we do need to reserve a global
+ // offset table index for it. This allows us to codegen decls out of dependency order,
+ // increasing how many computations can be done in parallel.
+ try self.comp.bin_file.allocateDeclIndexes(decl);
+ try self.comp.work_queue.writeItem(.{ .codegen_decl = decl });
+ } else if (prev_type_has_bits) {
+ self.comp.bin_file.freeDecl(decl);
+ }
+
+ if (fn_proto.getExternExportInlineToken()) |maybe_export_token| {
+ if (tree.token_ids[maybe_export_token] == .Keyword_export) {
+ const export_src = tree.token_locs[maybe_export_token].start;
+ const name_loc = tree.token_locs[fn_proto.getNameToken().?];
+ const name = tree.tokenSliceLoc(name_loc);
+ // The scope needs to have the decl in it.
+ try self.analyzeExport(&block_scope.base, export_src, name, decl);
+ }
+ }
+ return type_changed;
+ },
+ .VarDecl => {
+ const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", ast_node);
+
+ decl.analysis = .in_progress;
+
+ // We need the memory for the Type to go into the arena for the Decl
+ var decl_arena = std.heap.ArenaAllocator.init(self.gpa);
+ errdefer decl_arena.deinit();
+ const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
+
+ var block_scope: Scope.Block = .{
+ .parent = null,
+ .func = null,
+ .decl = decl,
+ .instructions = .{},
+ .arena = &decl_arena.allocator,
+ .is_comptime = true,
+ };
+ defer block_scope.instructions.deinit(self.gpa);
+
+ decl.is_pub = var_decl.getVisibToken() != null;
+ const is_extern = blk: {
+ const maybe_extern_token = var_decl.getExternExportToken() orelse
+ break :blk false;
+ if (tree.token_ids[maybe_extern_token] != .Keyword_extern) break :blk false;
+ if (var_decl.getInitNode()) |some| {
+ return self.failNode(&block_scope.base, some, "extern variables have no initializers", .{});
+ }
+ break :blk true;
+ };
+ if (var_decl.getLibName()) |lib_name| {
+ assert(is_extern);
+ return self.failNode(&block_scope.base, lib_name, "TODO implement function library name", .{});
+ }
+ const is_mutable = tree.token_ids[var_decl.mut_token] == .Keyword_var;
+ const is_threadlocal = if (var_decl.getThreadLocalToken()) |some| blk: {
+ if (!is_mutable) {
+ return self.failTok(&block_scope.base, some, "threadlocal variable cannot be constant", .{});
+ }
+ break :blk true;
+ } else false;
+ assert(var_decl.getComptimeToken() == null);
+ if (var_decl.getAlignNode()) |align_expr| {
+ return self.failNode(&block_scope.base, align_expr, "TODO implement function align expression", .{});
+ }
+ if (var_decl.getSectionNode()) |sect_expr| {
+ return self.failNode(&block_scope.base, sect_expr, "TODO implement function section expression", .{});
+ }
+
+ const var_info: struct { ty: Type, val: ?Value } = if (var_decl.getInitNode()) |init_node| vi: {
+ var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
+ defer gen_scope_arena.deinit();
+ var gen_scope: Scope.GenZIR = .{
+ .decl = decl,
+ .arena = &gen_scope_arena.allocator,
+ .parent = decl.scope,
+ };
+ defer gen_scope.instructions.deinit(self.gpa);
+
+ const init_result_loc: astgen.ResultLoc = if (var_decl.getTypeNode()) |type_node| rl: {
+ const src = tree.token_locs[type_node.firstToken()].start;
+ const type_type = try astgen.addZIRInstConst(self, &gen_scope.base, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.type_type),
+ });
+ const var_type = try astgen.expr(self, &gen_scope.base, .{ .ty = type_type }, type_node);
+ break :rl .{ .ty = var_type };
+ } else .none;
+
+ const src = tree.token_locs[init_node.firstToken()].start;
+ const init_inst = try astgen.expr(self, &gen_scope.base, init_result_loc, init_node);
+
+ var inner_block: Scope.Block = .{
+ .parent = null,
+ .func = null,
+ .decl = decl,
+ .instructions = .{},
+ .arena = &gen_scope_arena.allocator,
+ .is_comptime = true,
+ };
+ defer inner_block.instructions.deinit(self.gpa);
+ try zir_sema.analyzeBody(self, &inner_block.base, .{ .instructions = gen_scope.instructions.items });
+
+ // The result location guarantees the type coercion.
+ const analyzed_init_inst = init_inst.analyzed_inst.?;
+ // The is_comptime in the Scope.Block guarantees the result is comptime-known.
+ const val = analyzed_init_inst.value().?;
+
+ const ty = try analyzed_init_inst.ty.copy(block_scope.arena);
+ break :vi .{
+ .ty = ty,
+ .val = try val.copy(block_scope.arena),
+ };
+ } else if (!is_extern) {
+ return self.failTok(&block_scope.base, var_decl.firstToken(), "variables must be initialized", .{});
+ } else if (var_decl.getTypeNode()) |type_node| vi: {
+ // Temporary arena for the zir instructions.
+ var type_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
+ defer type_scope_arena.deinit();
+ var type_scope: Scope.GenZIR = .{
+ .decl = decl,
+ .arena = &type_scope_arena.allocator,
+ .parent = decl.scope,
+ };
+ defer type_scope.instructions.deinit(self.gpa);
+
+ const src = tree.token_locs[type_node.firstToken()].start;
+ const type_type = try astgen.addZIRInstConst(self, &type_scope.base, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.type_type),
+ });
+ const var_type = try astgen.expr(self, &type_scope.base, .{ .ty = type_type }, type_node);
+ const ty = try zir_sema.analyzeBodyValueAsType(self, &block_scope, var_type, .{
+ .instructions = type_scope.instructions.items,
+ });
+ break :vi .{
+ .ty = ty,
+ .val = null,
+ };
+ } else {
+ return self.failTok(&block_scope.base, var_decl.firstToken(), "unable to infer variable type", .{});
+ };
+
+ if (is_mutable and !var_info.ty.isValidVarType(is_extern)) {
+ return self.failTok(&block_scope.base, var_decl.firstToken(), "variable of type '{}' must be const", .{var_info.ty});
+ }
+
+ var type_changed = true;
+ if (decl.typedValueManaged()) |tvm| {
+ type_changed = !tvm.typed_value.ty.eql(var_info.ty);
+
+ tvm.deinit(self.gpa);
+ }
+
+ const new_variable = try decl_arena.allocator.create(Var);
+ const var_payload = try decl_arena.allocator.create(Value.Payload.Variable);
+ new_variable.* = .{
+ .owner_decl = decl,
+ .init = var_info.val orelse undefined,
+ .is_extern = is_extern,
+ .is_mutable = is_mutable,
+ .is_threadlocal = is_threadlocal,
+ };
+ var_payload.* = .{ .variable = new_variable };
+
+ decl_arena_state.* = decl_arena.state;
+ decl.typed_value = .{
+ .most_recent = .{
+ .typed_value = .{
+ .ty = var_info.ty,
+ .val = Value.initPayload(&var_payload.base),
+ },
+ .arena = decl_arena_state,
+ },
+ };
+ decl.analysis = .complete;
+ decl.generation = self.generation;
+
+ if (var_decl.getExternExportToken()) |maybe_export_token| {
+ if (tree.token_ids[maybe_export_token] == .Keyword_export) {
+ const export_src = tree.token_locs[maybe_export_token].start;
+ const name_loc = tree.token_locs[var_decl.name_token];
+ const name = tree.tokenSliceLoc(name_loc);
+ // The scope needs to have the decl in it.
+ try self.analyzeExport(&block_scope.base, export_src, name, decl);
+ }
+ }
+ return type_changed;
+ },
+ .Comptime => {
+ const comptime_decl = @fieldParentPtr(ast.Node.Comptime, "base", ast_node);
+
+ decl.analysis = .in_progress;
+
+ // A comptime decl does not store any value so we can just deinit this arena after analysis is done.
+ var analysis_arena = std.heap.ArenaAllocator.init(self.gpa);
+ defer analysis_arena.deinit();
+ var gen_scope: Scope.GenZIR = .{
+ .decl = decl,
+ .arena = &analysis_arena.allocator,
+ .parent = decl.scope,
+ };
+ defer gen_scope.instructions.deinit(self.gpa);
+
+ _ = try astgen.comptimeExpr(self, &gen_scope.base, .none, comptime_decl.expr);
+
+ var block_scope: Scope.Block = .{
+ .parent = null,
+ .func = null,
+ .decl = decl,
+ .instructions = .{},
+ .arena = &analysis_arena.allocator,
+ .is_comptime = true,
+ };
+ defer block_scope.instructions.deinit(self.gpa);
+
+ _ = try zir_sema.analyzeBody(self, &block_scope.base, .{
+ .instructions = gen_scope.instructions.items,
+ });
+
+ decl.analysis = .complete;
+ decl.generation = self.generation;
+ return true;
+ },
+ .Use => @panic("TODO usingnamespace decl"),
+ else => unreachable,
+ }
+}
+
+fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void {
+ try depender.dependencies.ensureCapacity(self.gpa, depender.dependencies.items().len + 1);
+ try dependee.dependants.ensureCapacity(self.gpa, dependee.dependants.items().len + 1);
+
+ depender.dependencies.putAssumeCapacity(dependee, {});
+ dependee.dependants.putAssumeCapacity(depender, {});
+}
+
+fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module {
+ switch (root_scope.status) {
+ .never_loaded, .unloaded_success => {
+ try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
+
+ const source = try root_scope.getSource(self);
+
+ var keep_zir_module = false;
+ const zir_module = try self.gpa.create(zir.Module);
+ defer if (!keep_zir_module) self.gpa.destroy(zir_module);
+
+ zir_module.* = try zir.parse(self.gpa, source);
+ defer if (!keep_zir_module) zir_module.deinit(self.gpa);
+
+ if (zir_module.error_msg) |src_err_msg| {
+ self.failed_files.putAssumeCapacityNoClobber(
+ &root_scope.base,
+ try Compilation.ErrorMsg.create(self.gpa, src_err_msg.byte_offset, "{}", .{src_err_msg.msg}),
+ );
+ root_scope.status = .unloaded_parse_failure;
+ return error.AnalysisFail;
+ }
+
+ root_scope.status = .loaded_success;
+ root_scope.contents = .{ .module = zir_module };
+ keep_zir_module = true;
+
+ return zir_module;
+ },
+
+ .unloaded_parse_failure,
+ .unloaded_sema_failure,
+ => return error.AnalysisFail,
+
+ .loaded_success, .loaded_sema_failure => return root_scope.contents.module,
+ }
+}
+
+fn getAstTree(self: *Module, container_scope: *Scope.Container) !*ast.Tree {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const root_scope = container_scope.file_scope;
+
+ switch (root_scope.status) {
+ .never_loaded, .unloaded_success => {
+ try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
+
+ const source = try root_scope.getSource(self);
+
+ var keep_tree = false;
+ const tree = try std.zig.parse(self.gpa, source);
+ defer if (!keep_tree) tree.deinit();
+
+ if (tree.errors.len != 0) {
+ const parse_err = tree.errors[0];
+
+ var msg = std.ArrayList(u8).init(self.gpa);
+ defer msg.deinit();
+
+ try parse_err.render(tree.token_ids, msg.outStream());
+ const err_msg = try self.gpa.create(Compilation.ErrorMsg);
+ err_msg.* = .{
+ .msg = msg.toOwnedSlice(),
+ .byte_offset = tree.token_locs[parse_err.loc()].start,
+ };
+
+ self.failed_files.putAssumeCapacityNoClobber(&root_scope.base, err_msg);
+ root_scope.status = .unloaded_parse_failure;
+ return error.AnalysisFail;
+ }
+
+ root_scope.status = .loaded_success;
+ root_scope.contents = .{ .tree = tree };
+ keep_tree = true;
+
+ return tree;
+ },
+
+ .unloaded_parse_failure => return error.AnalysisFail,
+
+ .loaded_success => return root_scope.contents.tree,
+ }
+}
+
+pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ // We may be analyzing it for the first time, or this may be
+ // an incremental update. This code handles both cases.
+ const tree = try self.getAstTree(container_scope);
+ const decls = tree.root_node.decls();
+
+ try self.comp.work_queue.ensureUnusedCapacity(decls.len);
+ try container_scope.decls.ensureCapacity(self.gpa, decls.len);
+
+ // Keep track of the decls that we expect to see in this file so that
+ // we know which ones have been deleted.
+ var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(self.gpa);
+ defer deleted_decls.deinit();
+ try deleted_decls.ensureCapacity(container_scope.decls.items().len);
+ for (container_scope.decls.items()) |entry| {
+ deleted_decls.putAssumeCapacityNoClobber(entry.key, {});
+ }
+
+ for (decls) |src_decl, decl_i| {
+ if (src_decl.cast(ast.Node.FnProto)) |fn_proto| {
+ // We will create a Decl for it regardless of analysis status.
+ const name_tok = fn_proto.getNameToken() orelse {
+ @panic("TODO missing function name");
+ };
+
+ const name_loc = tree.token_locs[name_tok];
+ const name = tree.tokenSliceLoc(name_loc);
+ const name_hash = container_scope.fullyQualifiedNameHash(name);
+ const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
+ if (self.decl_table.get(name_hash)) |decl| {
+ // Update the AST Node index of the decl, even if its contents are unchanged, it may
+ // have been re-ordered.
+ decl.src_index = decl_i;
+ if (deleted_decls.remove(decl) == null) {
+ decl.analysis = .sema_failure;
+ const err_msg = try Compilation.ErrorMsg.create(self.gpa, tree.token_locs[name_tok].start, "redefinition of '{}'", .{decl.name});
+ errdefer err_msg.destroy(self.gpa);
+ try self.failed_decls.putNoClobber(self.gpa, decl, err_msg);
+ } else {
+ if (!srcHashEql(decl.contents_hash, contents_hash)) {
+ try self.markOutdatedDecl(decl);
+ decl.contents_hash = contents_hash;
+ } else switch (self.comp.bin_file.tag) {
+ .coff => {
+ // TODO Implement for COFF
+ },
+ .elf => if (decl.fn_link.elf.len != 0) {
+ // TODO Look into detecting when this would be unnecessary by storing enough state
+ // in `Decl` to notice that the line number did not change.
+ self.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
+ },
+ .macho => {
+ // TODO Implement for MachO
+ },
+ .c, .wasm => {},
+ }
+ }
+ } else {
+ const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
+ container_scope.decls.putAssumeCapacity(new_decl, {});
+ if (fn_proto.getExternExportInlineToken()) |maybe_export_token| {
+ if (tree.token_ids[maybe_export_token] == .Keyword_export) {
+ self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
+ }
+ }
+ }
+ } else if (src_decl.castTag(.VarDecl)) |var_decl| {
+ const name_loc = tree.token_locs[var_decl.name_token];
+ const name = tree.tokenSliceLoc(name_loc);
+ const name_hash = container_scope.fullyQualifiedNameHash(name);
+ const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
+ if (self.decl_table.get(name_hash)) |decl| {
+ // Update the AST Node index of the decl, even if its contents are unchanged, it may
+ // have been re-ordered.
+ decl.src_index = decl_i;
+ if (deleted_decls.remove(decl) == null) {
+ decl.analysis = .sema_failure;
+ const err_msg = try Compilation.ErrorMsg.create(self.gpa, name_loc.start, "redefinition of '{}'", .{decl.name});
+ errdefer err_msg.destroy(self.gpa);
+ try self.failed_decls.putNoClobber(self.gpa, decl, err_msg);
+ } else if (!srcHashEql(decl.contents_hash, contents_hash)) {
+ try self.markOutdatedDecl(decl);
+ decl.contents_hash = contents_hash;
+ }
+ } else {
+ const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
+ container_scope.decls.putAssumeCapacity(new_decl, {});
+ if (var_decl.getExternExportToken()) |maybe_export_token| {
+ if (tree.token_ids[maybe_export_token] == .Keyword_export) {
+ self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
+ }
+ }
+ }
+ } else if (src_decl.castTag(.Comptime)) |comptime_node| {
+ const name_index = self.getNextAnonNameIndex();
+ const name = try std.fmt.allocPrint(self.gpa, "__comptime_{}", .{name_index});
+ defer self.gpa.free(name);
+
+ const name_hash = container_scope.fullyQualifiedNameHash(name);
+ const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
+
+ const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
+ container_scope.decls.putAssumeCapacity(new_decl, {});
+ self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
+ } else if (src_decl.castTag(.ContainerField)) |container_field| {
+ log.err("TODO: analyze container field", .{});
+ } else if (src_decl.castTag(.TestDecl)) |test_decl| {
+ log.err("TODO: analyze test decl", .{});
+ } else if (src_decl.castTag(.Use)) |use_decl| {
+ log.err("TODO: analyze usingnamespace decl", .{});
+ } else {
+ unreachable;
+ }
+ }
+ // Handle explicitly deleted decls from the source code. Not to be confused
+ // with when we delete decls because they are no longer referenced.
+ for (deleted_decls.items()) |entry| {
+ log.debug("noticed '{}' deleted from source\n", .{entry.key.name});
+ try self.deleteDecl(entry.key);
+ }
+}
+
+pub fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {
+ // We may be analyzing it for the first time, or this may be
+ // an incremental update. This code handles both cases.
+ const src_module = try self.getSrcModule(root_scope);
+
+ try self.comp.work_queue.ensureUnusedCapacity(src_module.decls.len);
+ try root_scope.decls.ensureCapacity(self.gpa, src_module.decls.len);
+
+ var exports_to_resolve = std.ArrayList(*zir.Decl).init(self.gpa);
+ defer exports_to_resolve.deinit();
+
+ // Keep track of the decls that we expect to see in this file so that
+ // we know which ones have been deleted.
+ var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(self.gpa);
+ defer deleted_decls.deinit();
+ try deleted_decls.ensureCapacity(self.decl_table.items().len);
+ for (self.decl_table.items()) |entry| {
+ deleted_decls.putAssumeCapacityNoClobber(entry.value, {});
+ }
+
+ for (src_module.decls) |src_decl, decl_i| {
+ const name_hash = root_scope.fullyQualifiedNameHash(src_decl.name);
+ if (self.decl_table.get(name_hash)) |decl| {
+ deleted_decls.removeAssertDiscard(decl);
+ if (!srcHashEql(src_decl.contents_hash, decl.contents_hash)) {
+ try self.markOutdatedDecl(decl);
+ decl.contents_hash = src_decl.contents_hash;
+ }
+ } else {
+ const new_decl = try self.createNewDecl(
+ &root_scope.base,
+ src_decl.name,
+ decl_i,
+ name_hash,
+ src_decl.contents_hash,
+ );
+ root_scope.decls.appendAssumeCapacity(new_decl);
+ if (src_decl.inst.cast(zir.Inst.Export)) |export_inst| {
+ try exports_to_resolve.append(src_decl);
+ }
+ }
+ }
+ for (exports_to_resolve.items) |export_decl| {
+ _ = try zir_sema.resolveZirDecl(self, &root_scope.base, export_decl);
+ }
+ // Handle explicitly deleted decls from the source code. Not to be confused
+ // with when we delete decls because they are no longer referenced.
+ for (deleted_decls.items()) |entry| {
+ log.debug("noticed '{}' deleted from source\n", .{entry.key.name});
+ try self.deleteDecl(entry.key);
+ }
+}
+
+pub fn deleteDecl(self: *Module, decl: *Decl) !void {
+ try self.deletion_set.ensureCapacity(self.gpa, self.deletion_set.items.len + decl.dependencies.items().len);
+
+ // Remove from the namespace it resides in. In the case of an anonymous Decl it will
+ // not be present in the set, and this does nothing.
+ decl.scope.removeDecl(decl);
+
+ log.debug("deleting decl '{}'\n", .{decl.name});
+ const name_hash = decl.fullyQualifiedNameHash();
+ self.decl_table.removeAssertDiscard(name_hash);
+ // Remove itself from its dependencies, because we are about to destroy the decl pointer.
+ for (decl.dependencies.items()) |entry| {
+ const dep = entry.key;
+ dep.removeDependant(decl);
+ if (dep.dependants.items().len == 0 and !dep.deletion_flag) {
+ // We don't recursively perform a deletion here, because during the update,
+ // another reference to it may turn up.
+ dep.deletion_flag = true;
+ self.deletion_set.appendAssumeCapacity(dep);
+ }
+ }
+ // Anything that depends on this deleted decl certainly needs to be re-analyzed.
+ for (decl.dependants.items()) |entry| {
+ const dep = entry.key;
+ dep.removeDependency(decl);
+ if (dep.analysis != .outdated) {
+ // TODO Move this failure possibility to the top of the function.
+ try self.markOutdatedDecl(dep);
+ }
+ }
+ if (self.failed_decls.remove(decl)) |entry| {
+ entry.value.destroy(self.gpa);
+ }
+ self.deleteDeclExports(decl);
+ self.comp.bin_file.freeDecl(decl);
+ decl.destroy(self.gpa);
+}
+
+/// Delete all the Export objects that are caused by this Decl. Re-analysis of
+/// this Decl will cause them to be re-created (or not).
+fn deleteDeclExports(self: *Module, decl: *Decl) void {
+ const kv = self.export_owners.remove(decl) orelse return;
+
+ for (kv.value) |exp| {
+ if (self.decl_exports.getEntry(exp.exported_decl)) |decl_exports_kv| {
+ // Remove exports with owner_decl matching the regenerating decl.
+ const list = decl_exports_kv.value;
+ var i: usize = 0;
+ var new_len = list.len;
+ while (i < new_len) {
+ if (list[i].owner_decl == decl) {
+ mem.copyBackwards(*Export, list[i..], list[i + 1 .. new_len]);
+ new_len -= 1;
+ } else {
+ i += 1;
+ }
+ }
+ decl_exports_kv.value = self.gpa.shrink(list, new_len);
+ if (new_len == 0) {
+ self.decl_exports.removeAssertDiscard(exp.exported_decl);
+ }
+ }
+ if (self.comp.bin_file.cast(link.File.Elf)) |elf| {
+ elf.deleteExport(exp.link);
+ }
+ if (self.failed_exports.remove(exp)) |entry| {
+ entry.value.destroy(self.gpa);
+ }
+ _ = self.symbol_exports.remove(exp.options.name);
+ self.gpa.free(exp.options.name);
+ self.gpa.destroy(exp);
+ }
+ self.gpa.free(kv.value);
+}
+
+pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ // Use the Decl's arena for function memory.
+ var arena = decl.typed_value.most_recent.arena.?.promote(self.gpa);
+ defer decl.typed_value.most_recent.arena.?.* = arena.state;
+ var inner_block: Scope.Block = .{
+ .parent = null,
+ .func = func,
+ .decl = decl,
+ .instructions = .{},
+ .arena = &arena.allocator,
+ .is_comptime = false,
+ };
+ defer inner_block.instructions.deinit(self.gpa);
+
+ const fn_zir = func.analysis.queued;
+ defer fn_zir.arena.promote(self.gpa).deinit();
+ func.analysis = .{ .in_progress = {} };
+ log.debug("set {} to in_progress\n", .{decl.name});
+
+ try zir_sema.analyzeBody(self, &inner_block.base, fn_zir.body);
+
+ const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items);
+ func.analysis = .{ .success = .{ .instructions = instructions } };
+ log.debug("set {} to success\n", .{decl.name});
+}
+
+fn markOutdatedDecl(self: *Module, decl: *Decl) !void {
+ log.debug("mark {} outdated\n", .{decl.name});
+ try self.comp.work_queue.writeItem(.{ .analyze_decl = decl });
+ if (self.failed_decls.remove(decl)) |entry| {
+ entry.value.destroy(self.gpa);
+ }
+ decl.analysis = .outdated;
+}
+
+fn allocateNewDecl(
+ self: *Module,
+ scope: *Scope,
+ src_index: usize,
+ contents_hash: std.zig.SrcHash,
+) !*Decl {
+ const new_decl = try self.gpa.create(Decl);
+ new_decl.* = .{
+ .name = "",
+ .scope = scope.namespace(),
+ .src_index = src_index,
+ .typed_value = .{ .never_succeeded = {} },
+ .analysis = .unreferenced,
+ .deletion_flag = false,
+ .contents_hash = contents_hash,
+ .link = switch (self.comp.bin_file.tag) {
+ .coff => .{ .coff = link.File.Coff.TextBlock.empty },
+ .elf => .{ .elf = link.File.Elf.TextBlock.empty },
+ .macho => .{ .macho = link.File.MachO.TextBlock.empty },
+ .c => .{ .c = {} },
+ .wasm => .{ .wasm = {} },
+ },
+ .fn_link = switch (self.comp.bin_file.tag) {
+ .coff => .{ .coff = {} },
+ .elf => .{ .elf = link.File.Elf.SrcFn.empty },
+ .macho => .{ .macho = link.File.MachO.SrcFn.empty },
+ .c => .{ .c = {} },
+ .wasm => .{ .wasm = null },
+ },
+ .generation = 0,
+ .is_pub = false,
+ };
+ return new_decl;
+}
+
+fn createNewDecl(
+ self: *Module,
+ scope: *Scope,
+ decl_name: []const u8,
+ src_index: usize,
+ name_hash: Scope.NameHash,
+ contents_hash: std.zig.SrcHash,
+) !*Decl {
+ try self.decl_table.ensureCapacity(self.gpa, self.decl_table.items().len + 1);
+ const new_decl = try self.allocateNewDecl(scope, src_index, contents_hash);
+ errdefer self.gpa.destroy(new_decl);
+ new_decl.name = try mem.dupeZ(self.gpa, u8, decl_name);
+ self.decl_table.putAssumeCapacityNoClobber(name_hash, new_decl);
+ return new_decl;
+}
+
+/// Get error value for error tag `name`.
+pub fn getErrorValue(self: *Module, name: []const u8) !std.StringHashMapUnmanaged(u16).Entry {
+ const gop = try self.global_error_set.getOrPut(self.gpa, name);
+ if (gop.found_existing)
+ return gop.entry.*;
+ errdefer self.global_error_set.removeAssertDiscard(name);
+
+ gop.entry.key = try self.gpa.dupe(u8, name);
+ gop.entry.value = @intCast(u16, self.global_error_set.count() - 1);
+ return gop.entry.*;
+}
+
+pub fn requireFunctionBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
+ return scope.cast(Scope.Block) orelse
+ return self.fail(scope, src, "instruction illegal outside function body", .{});
+}
+
+pub fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
+ const block = try self.requireFunctionBlock(scope, src);
+ if (block.is_comptime) {
+ return self.fail(scope, src, "unable to resolve comptime value", .{});
+ }
+ return block;
+}
+
+pub fn resolveConstValue(self: *Module, scope: *Scope, base: *Inst) !Value {
+ return (try self.resolveDefinedValue(scope, base)) orelse
+ return self.fail(scope, base.src, "unable to resolve comptime value", .{});
+}
+
+pub fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value {
+ if (base.value()) |val| {
+ if (val.isUndef()) {
+ return self.fail(scope, base.src, "use of undefined value here causes undefined behavior", .{});
+ }
+ return val;
+ }
+ return null;
+}
+
+pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, borrowed_symbol_name: []const u8, exported_decl: *Decl) !void {
+ try self.ensureDeclAnalyzed(exported_decl);
+ const typed_value = exported_decl.typed_value.most_recent.typed_value;
+ switch (typed_value.ty.zigTypeTag()) {
+ .Fn => {},
+ else => return self.fail(scope, src, "unable to export type '{}'", .{typed_value.ty}),
+ }
+
+ try self.decl_exports.ensureCapacity(self.gpa, self.decl_exports.items().len + 1);
+ try self.export_owners.ensureCapacity(self.gpa, self.export_owners.items().len + 1);
+
+ const new_export = try self.gpa.create(Export);
+ errdefer self.gpa.destroy(new_export);
+
+ const symbol_name = try self.gpa.dupe(u8, borrowed_symbol_name);
+ errdefer self.gpa.free(symbol_name);
+
+ const owner_decl = scope.decl().?;
+
+ new_export.* = .{
+ .options = .{ .name = symbol_name },
+ .src = src,
+ .link = .{},
+ .owner_decl = owner_decl,
+ .exported_decl = exported_decl,
+ .status = .in_progress,
+ };
+
+ // Add to export_owners table.
+ const eo_gop = self.export_owners.getOrPutAssumeCapacity(owner_decl);
+ if (!eo_gop.found_existing) {
+ eo_gop.entry.value = &[0]*Export{};
+ }
+ eo_gop.entry.value = try self.gpa.realloc(eo_gop.entry.value, eo_gop.entry.value.len + 1);
+ eo_gop.entry.value[eo_gop.entry.value.len - 1] = new_export;
+ errdefer eo_gop.entry.value = self.gpa.shrink(eo_gop.entry.value, eo_gop.entry.value.len - 1);
+
+ // Add to exported_decl table.
+ const de_gop = self.decl_exports.getOrPutAssumeCapacity(exported_decl);
+ if (!de_gop.found_existing) {
+ de_gop.entry.value = &[0]*Export{};
+ }
+ de_gop.entry.value = try self.gpa.realloc(de_gop.entry.value, de_gop.entry.value.len + 1);
+ de_gop.entry.value[de_gop.entry.value.len - 1] = new_export;
+ errdefer de_gop.entry.value = self.gpa.shrink(de_gop.entry.value, de_gop.entry.value.len - 1);
+
+ if (self.symbol_exports.get(symbol_name)) |_| {
+ try self.failed_exports.ensureCapacity(self.gpa, self.failed_exports.items().len + 1);
+ self.failed_exports.putAssumeCapacityNoClobber(new_export, try Compilation.ErrorMsg.create(
+ self.gpa,
+ src,
+ "exported symbol collision: {}",
+ .{symbol_name},
+ ));
+ // TODO: add a note
+ new_export.status = .failed;
+ return;
+ }
+
+ try self.symbol_exports.putNoClobber(self.gpa, symbol_name, new_export);
+ self.comp.bin_file.updateDeclExports(self, exported_decl, de_gop.entry.value) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ else => {
+ try self.failed_exports.ensureCapacity(self.gpa, self.failed_exports.items().len + 1);
+ self.failed_exports.putAssumeCapacityNoClobber(new_export, try Compilation.ErrorMsg.create(
+ self.gpa,
+ src,
+ "unable to export: {}",
+ .{@errorName(err)},
+ ));
+ new_export.status = .failed_retryable;
+ },
+ };
+}
+
+pub fn addNoOp(
+ self: *Module,
+ block: *Scope.Block,
+ src: usize,
+ ty: Type,
+ comptime tag: Inst.Tag,
+) !*Inst {
+ const inst = try block.arena.create(tag.Type());
+ inst.* = .{
+ .base = .{
+ .tag = tag,
+ .ty = ty,
+ .src = src,
+ },
+ };
+ try block.instructions.append(self.gpa, &inst.base);
+ return &inst.base;
+}
+
+pub fn addUnOp(
+ self: *Module,
+ block: *Scope.Block,
+ src: usize,
+ ty: Type,
+ tag: Inst.Tag,
+ operand: *Inst,
+) !*Inst {
+ const inst = try block.arena.create(Inst.UnOp);
+ inst.* = .{
+ .base = .{
+ .tag = tag,
+ .ty = ty,
+ .src = src,
+ },
+ .operand = operand,
+ };
+ try block.instructions.append(self.gpa, &inst.base);
+ return &inst.base;
+}
+
+pub fn addBinOp(
+ self: *Module,
+ block: *Scope.Block,
+ src: usize,
+ ty: Type,
+ tag: Inst.Tag,
+ lhs: *Inst,
+ rhs: *Inst,
+) !*Inst {
+ const inst = try block.arena.create(Inst.BinOp);
+ inst.* = .{
+ .base = .{
+ .tag = tag,
+ .ty = ty,
+ .src = src,
+ },
+ .lhs = lhs,
+ .rhs = rhs,
+ };
+ try block.instructions.append(self.gpa, &inst.base);
+ return &inst.base;
+}
+
+pub fn addArg(self: *Module, block: *Scope.Block, src: usize, ty: Type, name: [*:0]const u8) !*Inst {
+ const inst = try block.arena.create(Inst.Arg);
+ inst.* = .{
+ .base = .{
+ .tag = .arg,
+ .ty = ty,
+ .src = src,
+ },
+ .name = name,
+ };
+ try block.instructions.append(self.gpa, &inst.base);
+ return &inst.base;
+}
+
+pub fn addBr(
+ self: *Module,
+ scope_block: *Scope.Block,
+ src: usize,
+ target_block: *Inst.Block,
+ operand: *Inst,
+) !*Inst {
+ const inst = try scope_block.arena.create(Inst.Br);
+ inst.* = .{
+ .base = .{
+ .tag = .br,
+ .ty = Type.initTag(.noreturn),
+ .src = src,
+ },
+ .operand = operand,
+ .block = target_block,
+ };
+ try scope_block.instructions.append(self.gpa, &inst.base);
+ return &inst.base;
+}
+
+pub fn addCondBr(
+ self: *Module,
+ block: *Scope.Block,
+ src: usize,
+ condition: *Inst,
+ then_body: ir.Body,
+ else_body: ir.Body,
+) !*Inst {
+ const inst = try block.arena.create(Inst.CondBr);
+ inst.* = .{
+ .base = .{
+ .tag = .condbr,
+ .ty = Type.initTag(.noreturn),
+ .src = src,
+ },
+ .condition = condition,
+ .then_body = then_body,
+ .else_body = else_body,
+ };
+ try block.instructions.append(self.gpa, &inst.base);
+ return &inst.base;
+}
+
+pub fn addCall(
+ self: *Module,
+ block: *Scope.Block,
+ src: usize,
+ ty: Type,
+ func: *Inst,
+ args: []const *Inst,
+) !*Inst {
+ const inst = try block.arena.create(Inst.Call);
+ inst.* = .{
+ .base = .{
+ .tag = .call,
+ .ty = ty,
+ .src = src,
+ },
+ .func = func,
+ .args = args,
+ };
+ try block.instructions.append(self.gpa, &inst.base);
+ return &inst.base;
+}
+
+pub fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*Inst {
+ const const_inst = try scope.arena().create(Inst.Constant);
+ const_inst.* = .{
+ .base = .{
+ .tag = Inst.Constant.base_tag,
+ .ty = typed_value.ty,
+ .src = src,
+ },
+ .val = typed_value.val,
+ };
+ return &const_inst.base;
+}
+
+pub fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
+ return self.constInst(scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = try ty.toValue(scope.arena()),
+ });
+}
+
+pub fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst {
+ return self.constInst(scope, src, .{
+ .ty = Type.initTag(.void),
+ .val = Value.initTag(.void_value),
+ });
+}
+
+pub fn constNoReturn(self: *Module, scope: *Scope, src: usize) !*Inst {
+ return self.constInst(scope, src, .{
+ .ty = Type.initTag(.noreturn),
+ .val = Value.initTag(.unreachable_value),
+ });
+}
+
+pub fn constUndef(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
+ return self.constInst(scope, src, .{
+ .ty = ty,
+ .val = Value.initTag(.undef),
+ });
+}
+
+pub fn constBool(self: *Module, scope: *Scope, src: usize, v: bool) !*Inst {
+ return self.constInst(scope, src, .{
+ .ty = Type.initTag(.bool),
+ .val = ([2]Value{ Value.initTag(.bool_false), Value.initTag(.bool_true) })[@boolToInt(v)],
+ });
+}
+
+pub fn constIntUnsigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: u64) !*Inst {
+ const int_payload = try scope.arena().create(Value.Payload.Int_u64);
+ int_payload.* = .{ .int = int };
+
+ return self.constInst(scope, src, .{
+ .ty = ty,
+ .val = Value.initPayload(&int_payload.base),
+ });
+}
+
+pub fn constIntSigned(self: *Module, scope: *Scope, src: usize, ty: Type, int: i64) !*Inst {
+ const int_payload = try scope.arena().create(Value.Payload.Int_i64);
+ int_payload.* = .{ .int = int };
+
+ return self.constInst(scope, src, .{
+ .ty = ty,
+ .val = Value.initPayload(&int_payload.base),
+ });
+}
+
+pub fn constIntBig(self: *Module, scope: *Scope, src: usize, ty: Type, big_int: BigIntConst) !*Inst {
+ const val_payload = if (big_int.positive) blk: {
+ if (big_int.to(u64)) |x| {
+ return self.constIntUnsigned(scope, src, ty, x);
+ } else |err| switch (err) {
+ error.NegativeIntoUnsigned => unreachable,
+ error.TargetTooSmall => {}, // handled below
+ }
+ const big_int_payload = try scope.arena().create(Value.Payload.IntBigPositive);
+ big_int_payload.* = .{ .limbs = big_int.limbs };
+ break :blk &big_int_payload.base;
+ } else blk: {
+ if (big_int.to(i64)) |x| {
+ return self.constIntSigned(scope, src, ty, x);
+ } else |err| switch (err) {
+ error.NegativeIntoUnsigned => unreachable,
+ error.TargetTooSmall => {}, // handled below
+ }
+ const big_int_payload = try scope.arena().create(Value.Payload.IntBigNegative);
+ big_int_payload.* = .{ .limbs = big_int.limbs };
+ break :blk &big_int_payload.base;
+ };
+
+ return self.constInst(scope, src, .{
+ .ty = ty,
+ .val = Value.initPayload(val_payload),
+ });
+}
+
+pub fn createAnonymousDecl(
+ self: *Module,
+ scope: *Scope,
+ decl_arena: *std.heap.ArenaAllocator,
+ typed_value: TypedValue,
+) !*Decl {
+ const name_index = self.getNextAnonNameIndex();
+ const scope_decl = scope.decl().?;
+ const name = try std.fmt.allocPrint(self.gpa, "{}__anon_{}", .{ scope_decl.name, name_index });
+ defer self.gpa.free(name);
+ const name_hash = scope.namespace().fullyQualifiedNameHash(name);
+ const src_hash: std.zig.SrcHash = undefined;
+ const new_decl = try self.createNewDecl(scope, name, scope_decl.src_index, name_hash, src_hash);
+ const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
+
+ decl_arena_state.* = decl_arena.state;
+ new_decl.typed_value = .{
+ .most_recent = .{
+ .typed_value = typed_value,
+ .arena = decl_arena_state,
+ },
+ };
+ new_decl.analysis = .complete;
+ new_decl.generation = self.generation;
+
+ // TODO: This generates the Decl into the machine code file if it is of a type that is non-zero size.
+ // We should be able to further improve the compiler to not omit Decls which are only referenced at
+ // compile-time and not runtime.
+ if (typed_value.ty.hasCodeGenBits()) {
+ try self.comp.bin_file.allocateDeclIndexes(new_decl);
+ try self.comp.work_queue.writeItem(.{ .codegen_decl = new_decl });
+ }
+
+ return new_decl;
+}
+
+fn getNextAnonNameIndex(self: *Module) usize {
+ return @atomicRmw(usize, &self.next_anon_name_index, .Add, 1, .Monotonic);
+}
+
+pub fn lookupDeclName(self: *Module, scope: *Scope, ident_name: []const u8) ?*Decl {
+ const namespace = scope.namespace();
+ const name_hash = namespace.fullyQualifiedNameHash(ident_name);
+ return self.decl_table.get(name_hash);
+}
+
+pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst {
+ const scope_decl = scope.decl().?;
+ try self.declareDeclDependency(scope_decl, decl);
+ self.ensureDeclAnalyzed(decl) catch |err| {
+ if (scope.cast(Scope.Block)) |block| {
+ if (block.func) |func| {
+ func.analysis = .dependency_failure;
+ } else {
+ block.decl.analysis = .dependency_failure;
+ }
+ } else {
+ scope_decl.analysis = .dependency_failure;
+ }
+ return err;
+ };
+
+ const decl_tv = try decl.typedValue();
+ if (decl_tv.val.tag() == .variable) {
+ return self.analyzeVarRef(scope, src, decl_tv);
+ }
+ const ty = try self.simplePtrType(scope, src, decl_tv.ty, false, .One);
+ const val_payload = try scope.arena().create(Value.Payload.DeclRef);
+ val_payload.* = .{ .decl = decl };
+
+ return self.constInst(scope, src, .{
+ .ty = ty,
+ .val = Value.initPayload(&val_payload.base),
+ });
+}
+
+fn analyzeVarRef(self: *Module, scope: *Scope, src: usize, tv: TypedValue) InnerError!*Inst {
+ const variable = tv.val.cast(Value.Payload.Variable).?.variable;
+
+ const ty = try self.simplePtrType(scope, src, tv.ty, variable.is_mutable, .One);
+ if (!variable.is_mutable and !variable.is_extern) {
+ const val_payload = try scope.arena().create(Value.Payload.RefVal);
+ val_payload.* = .{ .val = variable.init };
+ return self.constInst(scope, src, .{
+ .ty = ty,
+ .val = Value.initPayload(&val_payload.base),
+ });
+ }
+
+ const b = try self.requireRuntimeBlock(scope, src);
+ const inst = try b.arena.create(Inst.VarPtr);
+ inst.* = .{
+ .base = .{
+ .tag = .varptr,
+ .ty = ty,
+ .src = src,
+ },
+ .variable = variable,
+ };
+ try b.instructions.append(self.gpa, &inst.base);
+ return &inst.base;
+}
+
+pub fn analyzeDeref(self: *Module, scope: *Scope, src: usize, ptr: *Inst, ptr_src: usize) InnerError!*Inst {
+ const elem_ty = switch (ptr.ty.zigTypeTag()) {
+ .Pointer => ptr.ty.elemType(),
+ else => return self.fail(scope, ptr_src, "expected pointer, found '{}'", .{ptr.ty}),
+ };
+ if (ptr.value()) |val| {
+ return self.constInst(scope, src, .{
+ .ty = elem_ty,
+ .val = try val.pointerDeref(scope.arena()),
+ });
+ }
+
+ const b = try self.requireRuntimeBlock(scope, src);
+ return self.addUnOp(b, src, elem_ty, .load, ptr);
+}
+
+pub fn analyzeDeclRefByName(self: *Module, scope: *Scope, src: usize, decl_name: []const u8) InnerError!*Inst {
+ const decl = self.lookupDeclName(scope, decl_name) orelse
+ return self.fail(scope, src, "decl '{}' not found", .{decl_name});
+ return self.analyzeDeclRef(scope, src, decl);
+}
+
+pub fn wantSafety(self: *Module, scope: *Scope) bool {
+ // TODO take into account scope's safety overrides
+ return switch (self.optimizeMode()) {
+ .Debug => true,
+ .ReleaseSafe => true,
+ .ReleaseFast => false,
+ .ReleaseSmall => false,
+ };
+}
+
+pub fn analyzeIsNull(
+ self: *Module,
+ scope: *Scope,
+ src: usize,
+ operand: *Inst,
+ invert_logic: bool,
+) InnerError!*Inst {
+ if (operand.value()) |opt_val| {
+ const is_null = opt_val.isNull();
+ const bool_value = if (invert_logic) !is_null else is_null;
+ return self.constBool(scope, src, bool_value);
+ }
+ const b = try self.requireRuntimeBlock(scope, src);
+ const inst_tag: Inst.Tag = if (invert_logic) .isnonnull else .isnull;
+ return self.addUnOp(b, src, Type.initTag(.bool), inst_tag, operand);
+}
+
+pub fn analyzeIsErr(self: *Module, scope: *Scope, src: usize, operand: *Inst) InnerError!*Inst {
+ return self.fail(scope, src, "TODO implement analysis of iserr", .{});
+}
+
+pub fn analyzeSlice(self: *Module, scope: *Scope, src: usize, array_ptr: *Inst, start: *Inst, end_opt: ?*Inst, sentinel_opt: ?*Inst) InnerError!*Inst {
+ const ptr_child = switch (array_ptr.ty.zigTypeTag()) {
+ .Pointer => array_ptr.ty.elemType(),
+ else => return self.fail(scope, src, "expected pointer, found '{}'", .{array_ptr.ty}),
+ };
+
+ var array_type = ptr_child;
+ const elem_type = switch (ptr_child.zigTypeTag()) {
+ .Array => ptr_child.elemType(),
+ .Pointer => blk: {
+ if (ptr_child.isSinglePointer()) {
+ if (ptr_child.elemType().zigTypeTag() == .Array) {
+ array_type = ptr_child.elemType();
+ break :blk ptr_child.elemType().elemType();
+ }
+
+ return self.fail(scope, src, "slice of single-item pointer", .{});
+ }
+ break :blk ptr_child.elemType();
+ },
+ else => return self.fail(scope, src, "slice of non-array type '{}'", .{ptr_child}),
+ };
+
+ const slice_sentinel = if (sentinel_opt) |sentinel| blk: {
+ const casted = try self.coerce(scope, elem_type, sentinel);
+ break :blk try self.resolveConstValue(scope, casted);
+ } else null;
+
+ var return_ptr_size: std.builtin.TypeInfo.Pointer.Size = .Slice;
+ var return_elem_type = elem_type;
+ if (end_opt) |end| {
+ if (end.value()) |end_val| {
+ if (start.value()) |start_val| {
+ const start_u64 = start_val.toUnsignedInt();
+ const end_u64 = end_val.toUnsignedInt();
+ if (start_u64 > end_u64) {
+ return self.fail(scope, src, "out of bounds slice", .{});
+ }
+
+ const len = end_u64 - start_u64;
+ const array_sentinel = if (array_type.zigTypeTag() == .Array and end_u64 == array_type.arrayLen())
+ array_type.sentinel()
+ else
+ slice_sentinel;
+ return_elem_type = try self.arrayType(scope, len, array_sentinel, elem_type);
+ return_ptr_size = .One;
+ }
+ }
+ }
+ const return_type = try self.ptrType(
+ scope,
+ src,
+ return_elem_type,
+ if (end_opt == null) slice_sentinel else null,
+ 0, // TODO alignment
+ 0,
+ 0,
+ !ptr_child.isConstPtr(),
+ ptr_child.isAllowzeroPtr(),
+ ptr_child.isVolatilePtr(),
+ return_ptr_size,
+ );
+
+ return self.fail(scope, src, "TODO implement analysis of slice", .{});
+}
+
+/// Asserts that lhs and rhs types are both numeric.
+pub fn cmpNumeric(
+ self: *Module,
+ scope: *Scope,
+ src: usize,
+ lhs: *Inst,
+ rhs: *Inst,
+ op: std.math.CompareOperator,
+) !*Inst {
+ assert(lhs.ty.isNumeric());
+ assert(rhs.ty.isNumeric());
+
+ const lhs_ty_tag = lhs.ty.zigTypeTag();
+ const rhs_ty_tag = rhs.ty.zigTypeTag();
+
+ if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) {
+ if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) {
+ return self.fail(scope, src, "vector length mismatch: {} and {}", .{
+ lhs.ty.arrayLen(),
+ rhs.ty.arrayLen(),
+ });
+ }
+ return self.fail(scope, src, "TODO implement support for vectors in cmpNumeric", .{});
+ } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) {
+ return self.fail(scope, src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{
+ lhs.ty,
+ rhs.ty,
+ });
+ }
+
+ if (lhs.value()) |lhs_val| {
+ if (rhs.value()) |rhs_val| {
+ return self.constBool(scope, src, Value.compare(lhs_val, op, rhs_val));
+ }
+ }
+
+ // TODO handle comparisons against lazy zero values
+ // Some values can be compared against zero without being runtime known or without forcing
+ // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to
+ // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout
+ // of this function if we don't need to.
+
+ // It must be a runtime comparison.
+ const b = try self.requireRuntimeBlock(scope, src);
+ // For floats, emit a float comparison instruction.
+ const lhs_is_float = switch (lhs_ty_tag) {
+ .Float, .ComptimeFloat => true,
+ else => false,
+ };
+ const rhs_is_float = switch (rhs_ty_tag) {
+ .Float, .ComptimeFloat => true,
+ else => false,
+ };
+ if (lhs_is_float and rhs_is_float) {
+ // Implicit cast the smaller one to the larger one.
+ const dest_type = x: {
+ if (lhs_ty_tag == .ComptimeFloat) {
+ break :x rhs.ty;
+ } else if (rhs_ty_tag == .ComptimeFloat) {
+ break :x lhs.ty;
+ }
+ if (lhs.ty.floatBits(self.getTarget()) >= rhs.ty.floatBits(self.getTarget())) {
+ break :x lhs.ty;
+ } else {
+ break :x rhs.ty;
+ }
+ };
+ const casted_lhs = try self.coerce(scope, dest_type, lhs);
+ const casted_rhs = try self.coerce(scope, dest_type, rhs);
+ return self.addBinOp(b, src, dest_type, Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
+ }
+ // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.
+ // For mixed signed and unsigned integers, implicit cast both operands to a signed
+ // integer with + 1 bit.
+ // For mixed floats and integers, extract the integer part from the float, cast that to
+ // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float,
+ // add/subtract 1.
+ const lhs_is_signed = if (lhs.value()) |lhs_val|
+ lhs_val.compareWithZero(.lt)
+ else
+ (lhs.ty.isFloat() or lhs.ty.isSignedInt());
+ const rhs_is_signed = if (rhs.value()) |rhs_val|
+ rhs_val.compareWithZero(.lt)
+ else
+ (rhs.ty.isFloat() or rhs.ty.isSignedInt());
+ const dest_int_is_signed = lhs_is_signed or rhs_is_signed;
+
+ var dest_float_type: ?Type = null;
+
+ var lhs_bits: usize = undefined;
+ if (lhs.value()) |lhs_val| {
+ if (lhs_val.isUndef())
+ return self.constUndef(scope, src, Type.initTag(.bool));
+ const is_unsigned = if (lhs_is_float) x: {
+ var bigint_space: Value.BigIntSpace = undefined;
+ var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(self.gpa);
+ defer bigint.deinit();
+ const zcmp = lhs_val.orderAgainstZero();
+ if (lhs_val.floatHasFraction()) {
+ switch (op) {
+ .eq => return self.constBool(scope, src, false),
+ .neq => return self.constBool(scope, src, true),
+ else => {},
+ }
+ if (zcmp == .lt) {
+ try bigint.addScalar(bigint.toConst(), -1);
+ } else {
+ try bigint.addScalar(bigint.toConst(), 1);
+ }
+ }
+ lhs_bits = bigint.toConst().bitCountTwosComp();
+ break :x (zcmp != .lt);
+ } else x: {
+ lhs_bits = lhs_val.intBitCountTwosComp();
+ break :x (lhs_val.orderAgainstZero() != .lt);
+ };
+ lhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
+ } else if (lhs_is_float) {
+ dest_float_type = lhs.ty;
+ } else {
+ const int_info = lhs.ty.intInfo(self.getTarget());
+ lhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed);
+ }
+
+ var rhs_bits: usize = undefined;
+ if (rhs.value()) |rhs_val| {
+ if (rhs_val.isUndef())
+ return self.constUndef(scope, src, Type.initTag(.bool));
+ const is_unsigned = if (rhs_is_float) x: {
+ var bigint_space: Value.BigIntSpace = undefined;
+ var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(self.gpa);
+ defer bigint.deinit();
+ const zcmp = rhs_val.orderAgainstZero();
+ if (rhs_val.floatHasFraction()) {
+ switch (op) {
+ .eq => return self.constBool(scope, src, false),
+ .neq => return self.constBool(scope, src, true),
+ else => {},
+ }
+ if (zcmp == .lt) {
+ try bigint.addScalar(bigint.toConst(), -1);
+ } else {
+ try bigint.addScalar(bigint.toConst(), 1);
+ }
+ }
+ rhs_bits = bigint.toConst().bitCountTwosComp();
+ break :x (zcmp != .lt);
+ } else x: {
+ rhs_bits = rhs_val.intBitCountTwosComp();
+ break :x (rhs_val.orderAgainstZero() != .lt);
+ };
+ rhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
+ } else if (rhs_is_float) {
+ dest_float_type = rhs.ty;
+ } else {
+ const int_info = rhs.ty.intInfo(self.getTarget());
+ rhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed);
+ }
+
+ const dest_type = if (dest_float_type) |ft| ft else blk: {
+ const max_bits = std.math.max(lhs_bits, rhs_bits);
+ const casted_bits = std.math.cast(u16, max_bits) catch |err| switch (err) {
+ error.Overflow => return self.fail(scope, src, "{} exceeds maximum integer bit count", .{max_bits}),
+ };
+ break :blk try self.makeIntType(scope, dest_int_is_signed, casted_bits);
+ };
+ const casted_lhs = try self.coerce(scope, dest_type, lhs);
+ const casted_rhs = try self.coerce(scope, dest_type, rhs);
+
+ return self.addBinOp(b, src, Type.initTag(.bool), Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
+}
+
+fn wrapOptional(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
+ if (inst.value()) |val| {
+ return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
+ }
+
+ const b = try self.requireRuntimeBlock(scope, inst.src);
+ return self.addUnOp(b, inst.src, dest_type, .wrap_optional, inst);
+}
+
+fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type {
+ if (signed) {
+ const int_payload = try scope.arena().create(Type.Payload.IntSigned);
+ int_payload.* = .{ .bits = bits };
+ return Type.initPayload(&int_payload.base);
+ } else {
+ const int_payload = try scope.arena().create(Type.Payload.IntUnsigned);
+ int_payload.* = .{ .bits = bits };
+ return Type.initPayload(&int_payload.base);
+ }
+}
+
+pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Type {
+ if (instructions.len == 0)
+ return Type.initTag(.noreturn);
+
+ if (instructions.len == 1)
+ return instructions[0].ty;
+
+ var prev_inst = instructions[0];
+ for (instructions[1..]) |next_inst| {
+ if (next_inst.ty.eql(prev_inst.ty))
+ continue;
+ if (next_inst.ty.zigTypeTag() == .NoReturn)
+ continue;
+ if (prev_inst.ty.zigTypeTag() == .NoReturn) {
+ prev_inst = next_inst;
+ continue;
+ }
+ if (next_inst.ty.zigTypeTag() == .Undefined)
+ continue;
+ if (prev_inst.ty.zigTypeTag() == .Undefined) {
+ prev_inst = next_inst;
+ continue;
+ }
+ if (prev_inst.ty.isInt() and
+ next_inst.ty.isInt() and
+ prev_inst.ty.isSignedInt() == next_inst.ty.isSignedInt())
+ {
+ if (prev_inst.ty.intInfo(self.getTarget()).bits < next_inst.ty.intInfo(self.getTarget()).bits) {
+ prev_inst = next_inst;
+ }
+ continue;
+ }
+ if (prev_inst.ty.isFloat() and next_inst.ty.isFloat()) {
+ if (prev_inst.ty.floatBits(self.getTarget()) < next_inst.ty.floatBits(self.getTarget())) {
+ prev_inst = next_inst;
+ }
+ continue;
+ }
+
+ // TODO error notes pointing out each type
+ return self.fail(scope, next_inst.src, "incompatible types: '{}' and '{}'", .{ prev_inst.ty, next_inst.ty });
+ }
+
+ return prev_inst.ty;
+}
+
+pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
+ // If the types are the same, we can return the operand.
+ if (dest_type.eql(inst.ty))
+ return inst;
+
+ const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty);
+ if (in_memory_result == .ok) {
+ return self.bitcast(scope, dest_type, inst);
+ }
+
+ // undefined to anything
+ if (inst.value()) |val| {
+ if (val.isUndef() or inst.ty.zigTypeTag() == .Undefined) {
+ return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
+ }
+ }
+ assert(inst.ty.zigTypeTag() != .Undefined);
+
+ // null to ?T
+ if (dest_type.zigTypeTag() == .Optional and inst.ty.zigTypeTag() == .Null) {
+ return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = Value.initTag(.null_value) });
+ }
+
+ // T to ?T
+ if (dest_type.zigTypeTag() == .Optional) {
+ var buf: Type.Payload.PointerSimple = undefined;
+ const child_type = dest_type.optionalChild(&buf);
+ if (child_type.eql(inst.ty)) {
+ return self.wrapOptional(scope, dest_type, inst);
+ } else if (try self.coerceNum(scope, child_type, inst)) |some| {
+ return self.wrapOptional(scope, dest_type, some);
+ }
+ }
+
+ // *[N]T to []T
+ if (inst.ty.isSinglePointer() and dest_type.isSlice() and
+ (!inst.ty.isConstPtr() or dest_type.isConstPtr()))
+ {
+ const array_type = inst.ty.elemType();
+ const dst_elem_type = dest_type.elemType();
+ if (array_type.zigTypeTag() == .Array and
+ coerceInMemoryAllowed(dst_elem_type, array_type.elemType()) == .ok)
+ {
+ return self.coerceArrayPtrToSlice(scope, dest_type, inst);
+ }
+ }
+
+ // comptime known number to other number
+ if (try self.coerceNum(scope, dest_type, inst)) |some|
+ return some;
+
+ // integer widening
+ if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) {
+ assert(inst.value() == null); // handled above
+
+ const src_info = inst.ty.intInfo(self.getTarget());
+ const dst_info = dest_type.intInfo(self.getTarget());
+ if ((src_info.signed == dst_info.signed and dst_info.bits >= src_info.bits) or
+ // small enough unsigned ints can get casted to large enough signed ints
+ (src_info.signed and !dst_info.signed and dst_info.bits > src_info.bits))
+ {
+ const b = try self.requireRuntimeBlock(scope, inst.src);
+ return self.addUnOp(b, inst.src, dest_type, .intcast, inst);
+ }
+ }
+
+ // float widening
+ if (inst.ty.zigTypeTag() == .Float and dest_type.zigTypeTag() == .Float) {
+ assert(inst.value() == null); // handled above
+
+ const src_bits = inst.ty.floatBits(self.getTarget());
+ const dst_bits = dest_type.floatBits(self.getTarget());
+ if (dst_bits >= src_bits) {
+ const b = try self.requireRuntimeBlock(scope, inst.src);
+ return self.addUnOp(b, inst.src, dest_type, .floatcast, inst);
+ }
+ }
+
+ return self.fail(scope, inst.src, "expected {}, found {}", .{ dest_type, inst.ty });
+}
+
+pub fn coerceNum(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !?*Inst {
+ const val = inst.value() orelse return null;
+ const src_zig_tag = inst.ty.zigTypeTag();
+ const dst_zig_tag = dest_type.zigTypeTag();
+
+ if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) {
+ if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
+ if (val.floatHasFraction()) {
+ return self.fail(scope, inst.src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst.ty });
+ }
+ return self.fail(scope, inst.src, "TODO float to int", .{});
+ } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
+ if (!val.intFitsInType(dest_type, self.getTarget())) {
+ return self.fail(scope, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val });
+ }
+ return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
+ }
+ } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) {
+ if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
+ const res = val.floatCast(scope.arena(), dest_type, self.getTarget()) catch |err| switch (err) {
+ error.Overflow => return self.fail(
+ scope,
+ inst.src,
+ "cast of value {} to type '{}' loses information",
+ .{ val, dest_type },
+ ),
+ error.OutOfMemory => return error.OutOfMemory,
+ };
+ return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = res });
+ } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
+ return self.fail(scope, inst.src, "TODO int to float", .{});
+ }
+ }
+ return null;
+}
+
+pub fn storePtr(self: *Module, scope: *Scope, src: usize, ptr: *Inst, uncasted_value: *Inst) !*Inst {
+ if (ptr.ty.isConstPtr())
+ return self.fail(scope, src, "cannot assign to constant", .{});
+
+ const elem_ty = ptr.ty.elemType();
+ const value = try self.coerce(scope, elem_ty, uncasted_value);
+ if (elem_ty.onePossibleValue() != null)
+ return self.constVoid(scope, src);
+
+ // TODO handle comptime pointer writes
+ // TODO handle if the element type requires comptime
+
+ const b = try self.requireRuntimeBlock(scope, src);
+ return self.addBinOp(b, src, Type.initTag(.void), .store, ptr, value);
+}
+
+pub fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
+ if (inst.value()) |val| {
+ // Keep the comptime Value representation; take the new type.
+ return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
+ }
+ // TODO validate the type size and other compile errors
+ const b = try self.requireRuntimeBlock(scope, inst.src);
+ return self.addUnOp(b, inst.src, dest_type, .bitcast, inst);
+}
+
+fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
+ if (inst.value()) |val| {
+ // The comptime Value representation is compatible with both types.
+ return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
+ }
+ return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});
+}
+
+pub fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: anytype) InnerError {
+ @setCold(true);
+ const err_msg = try Compilation.ErrorMsg.create(self.gpa, src, format, args);
+ return self.failWithOwnedErrorMsg(scope, src, err_msg);
+}
+
+pub fn failTok(
+ self: *Module,
+ scope: *Scope,
+ token_index: ast.TokenIndex,
+ comptime format: []const u8,
+ args: anytype,
+) InnerError {
+ @setCold(true);
+ const src = scope.tree().token_locs[token_index].start;
+ return self.fail(scope, src, format, args);
+}
+
+pub fn failNode(
+ self: *Module,
+ scope: *Scope,
+ ast_node: *ast.Node,
+ comptime format: []const u8,
+ args: anytype,
+) InnerError {
+ @setCold(true);
+ const src = scope.tree().token_locs[ast_node.firstToken()].start;
+ return self.fail(scope, src, format, args);
+}
+
+fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Compilation.ErrorMsg) InnerError {
+ {
+ errdefer err_msg.destroy(self.gpa);
+ try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
+ try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
+ }
+ switch (scope.tag) {
+ .decl => {
+ const decl = scope.cast(Scope.DeclAnalysis).?.decl;
+ decl.analysis = .sema_failure;
+ decl.generation = self.generation;
+ self.failed_decls.putAssumeCapacityNoClobber(decl, err_msg);
+ },
+ .block => {
+ const block = scope.cast(Scope.Block).?;
+ if (block.func) |func| {
+ func.analysis = .sema_failure;
+ } else {
+ block.decl.analysis = .sema_failure;
+ block.decl.generation = self.generation;
+ }
+ self.failed_decls.putAssumeCapacityNoClobber(block.decl, err_msg);
+ },
+ .gen_zir => {
+ const gen_zir = scope.cast(Scope.GenZIR).?;
+ gen_zir.decl.analysis = .sema_failure;
+ gen_zir.decl.generation = self.generation;
+ self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg);
+ },
+ .local_val => {
+ const gen_zir = scope.cast(Scope.LocalVal).?.gen_zir;
+ gen_zir.decl.analysis = .sema_failure;
+ gen_zir.decl.generation = self.generation;
+ self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg);
+ },
+ .local_ptr => {
+ const gen_zir = scope.cast(Scope.LocalPtr).?.gen_zir;
+ gen_zir.decl.analysis = .sema_failure;
+ gen_zir.decl.generation = self.generation;
+ self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg);
+ },
+ .zir_module => {
+ const zir_module = scope.cast(Scope.ZIRModule).?;
+ zir_module.status = .loaded_sema_failure;
+ self.failed_files.putAssumeCapacityNoClobber(scope, err_msg);
+ },
+ .file => unreachable,
+ .container => unreachable,
+ }
+ return error.AnalysisFail;
+}
+
+const InMemoryCoercionResult = enum {
+ ok,
+ no_match,
+};
+
+fn coerceInMemoryAllowed(dest_type: Type, src_type: Type) InMemoryCoercionResult {
+ if (dest_type.eql(src_type))
+ return .ok;
+
+ // TODO: implement more of this function
+
+ return .no_match;
+}
+
+fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool {
+ return @bitCast(u128, a) == @bitCast(u128, b);
+}
+
+pub fn intAdd(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
+ // TODO is this a performance issue? maybe we should try the operation without
+ // resorting to BigInt first.
+ var lhs_space: Value.BigIntSpace = undefined;
+ var rhs_space: Value.BigIntSpace = undefined;
+ const lhs_bigint = lhs.toBigInt(&lhs_space);
+ const rhs_bigint = rhs.toBigInt(&rhs_space);
+ const limbs = try allocator.alloc(
+ std.math.big.Limb,
+ std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
+ );
+ var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
+ result_bigint.add(lhs_bigint, rhs_bigint);
+ const result_limbs = result_bigint.limbs[0..result_bigint.len];
+
+ const val_payload = if (result_bigint.positive) blk: {
+ const val_payload = try allocator.create(Value.Payload.IntBigPositive);
+ val_payload.* = .{ .limbs = result_limbs };
+ break :blk &val_payload.base;
+ } else blk: {
+ const val_payload = try allocator.create(Value.Payload.IntBigNegative);
+ val_payload.* = .{ .limbs = result_limbs };
+ break :blk &val_payload.base;
+ };
+
+ return Value.initPayload(val_payload);
+}
+
+pub fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
+ // TODO is this a performance issue? maybe we should try the operation without
+ // resorting to BigInt first.
+ var lhs_space: Value.BigIntSpace = undefined;
+ var rhs_space: Value.BigIntSpace = undefined;
+ const lhs_bigint = lhs.toBigInt(&lhs_space);
+ const rhs_bigint = rhs.toBigInt(&rhs_space);
+ const limbs = try allocator.alloc(
+ std.math.big.Limb,
+ std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
+ );
+ var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
+ result_bigint.sub(lhs_bigint, rhs_bigint);
+ const result_limbs = result_bigint.limbs[0..result_bigint.len];
+
+ const val_payload = if (result_bigint.positive) blk: {
+ const val_payload = try allocator.create(Value.Payload.IntBigPositive);
+ val_payload.* = .{ .limbs = result_limbs };
+ break :blk &val_payload.base;
+ } else blk: {
+ const val_payload = try allocator.create(Value.Payload.IntBigNegative);
+ val_payload.* = .{ .limbs = result_limbs };
+ break :blk &val_payload.base;
+ };
+
+ return Value.initPayload(val_payload);
+}
+
+pub fn floatAdd(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs: Value, rhs: Value) !Value {
+ var bit_count = switch (float_type.tag()) {
+ .comptime_float => 128,
+ else => float_type.floatBits(self.getTarget()),
+ };
+
+ const allocator = scope.arena();
+ const val_payload = switch (bit_count) {
+ 16 => {
+ return self.fail(scope, src, "TODO Implement addition for soft floats", .{});
+ },
+ 32 => blk: {
+ const lhs_val = lhs.toFloat(f32);
+ const rhs_val = rhs.toFloat(f32);
+ const val_payload = try allocator.create(Value.Payload.Float_32);
+ val_payload.* = .{ .val = lhs_val + rhs_val };
+ break :blk &val_payload.base;
+ },
+ 64 => blk: {
+ const lhs_val = lhs.toFloat(f64);
+ const rhs_val = rhs.toFloat(f64);
+ const val_payload = try allocator.create(Value.Payload.Float_64);
+ val_payload.* = .{ .val = lhs_val + rhs_val };
+ break :blk &val_payload.base;
+ },
+ 128 => {
+ return self.fail(scope, src, "TODO Implement addition for big floats", .{});
+ },
+ else => unreachable,
+ };
+
+ return Value.initPayload(val_payload);
+}
+
+pub fn floatSub(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs: Value, rhs: Value) !Value {
+ var bit_count = switch (float_type.tag()) {
+ .comptime_float => 128,
+ else => float_type.floatBits(self.getTarget()),
+ };
+
+ const allocator = scope.arena();
+ const val_payload = switch (bit_count) {
+ 16 => {
+ return self.fail(scope, src, "TODO Implement substraction for soft floats", .{});
+ },
+ 32 => blk: {
+ const lhs_val = lhs.toFloat(f32);
+ const rhs_val = rhs.toFloat(f32);
+ const val_payload = try allocator.create(Value.Payload.Float_32);
+ val_payload.* = .{ .val = lhs_val - rhs_val };
+ break :blk &val_payload.base;
+ },
+ 64 => blk: {
+ const lhs_val = lhs.toFloat(f64);
+ const rhs_val = rhs.toFloat(f64);
+ const val_payload = try allocator.create(Value.Payload.Float_64);
+ val_payload.* = .{ .val = lhs_val - rhs_val };
+ break :blk &val_payload.base;
+ },
+ 128 => {
+ return self.fail(scope, src, "TODO Implement substraction for big floats", .{});
+ },
+ else => unreachable,
+ };
+
+ return Value.initPayload(val_payload);
+}
+
+pub fn simplePtrType(self: *Module, scope: *Scope, src: usize, elem_ty: Type, mutable: bool, size: std.builtin.TypeInfo.Pointer.Size) Allocator.Error!Type {
+ if (!mutable and size == .Slice and elem_ty.eql(Type.initTag(.u8))) {
+ return Type.initTag(.const_slice_u8);
+ }
+ // TODO stage1 type inference bug
+ const T = Type.Tag;
+
+ const type_payload = try scope.arena().create(Type.Payload.PointerSimple);
+ type_payload.* = .{
+ .base = .{
+ .tag = switch (size) {
+ .One => if (mutable) T.single_mut_pointer else T.single_const_pointer,
+ .Many => if (mutable) T.many_mut_pointer else T.many_const_pointer,
+ .C => if (mutable) T.c_mut_pointer else T.c_const_pointer,
+ .Slice => if (mutable) T.mut_slice else T.const_slice,
+ },
+ },
+ .pointee_type = elem_ty,
+ };
+ return Type.initPayload(&type_payload.base);
+}
+
+pub fn ptrType(
+ self: *Module,
+ scope: *Scope,
+ src: usize,
+ elem_ty: Type,
+ sentinel: ?Value,
+ @"align": u32,
+ bit_offset: u16,
+ host_size: u16,
+ mutable: bool,
+ @"allowzero": bool,
+ @"volatile": bool,
+ size: std.builtin.TypeInfo.Pointer.Size,
+) Allocator.Error!Type {
+ assert(host_size == 0 or bit_offset < host_size * 8);
+
+ // TODO check if type can be represented by simplePtrType
+ const type_payload = try scope.arena().create(Type.Payload.Pointer);
+ type_payload.* = .{
+ .pointee_type = elem_ty,
+ .sentinel = sentinel,
+ .@"align" = @"align",
+ .bit_offset = bit_offset,
+ .host_size = host_size,
+ .@"allowzero" = @"allowzero",
+ .mutable = mutable,
+ .@"volatile" = @"volatile",
+ .size = size,
+ };
+ return Type.initPayload(&type_payload.base);
+}
+
+pub fn optionalType(self: *Module, scope: *Scope, child_type: Type) Allocator.Error!Type {
+ return Type.initPayload(switch (child_type.tag()) {
+ .single_const_pointer => blk: {
+ const payload = try scope.arena().create(Type.Payload.PointerSimple);
+ payload.* = .{
+ .base = .{ .tag = .optional_single_const_pointer },
+ .pointee_type = child_type.elemType(),
+ };
+ break :blk &payload.base;
+ },
+ .single_mut_pointer => blk: {
+ const payload = try scope.arena().create(Type.Payload.PointerSimple);
+ payload.* = .{
+ .base = .{ .tag = .optional_single_mut_pointer },
+ .pointee_type = child_type.elemType(),
+ };
+ break :blk &payload.base;
+ },
+ else => blk: {
+ const payload = try scope.arena().create(Type.Payload.Optional);
+ payload.* = .{
+ .child_type = child_type,
+ };
+ break :blk &payload.base;
+ },
+ });
+}
+
+pub fn arrayType(self: *Module, scope: *Scope, len: u64, sentinel: ?Value, elem_type: Type) Allocator.Error!Type {
+ if (elem_type.eql(Type.initTag(.u8))) {
+ if (sentinel) |some| {
+ if (some.eql(Value.initTag(.zero))) {
+ const payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0);
+ payload.* = .{
+ .len = len,
+ };
+ return Type.initPayload(&payload.base);
+ }
+ } else {
+ const payload = try scope.arena().create(Type.Payload.Array_u8);
+ payload.* = .{
+ .len = len,
+ };
+ return Type.initPayload(&payload.base);
+ }
+ }
+
+ if (sentinel) |some| {
+ const payload = try scope.arena().create(Type.Payload.ArraySentinel);
+ payload.* = .{
+ .len = len,
+ .sentinel = some,
+ .elem_type = elem_type,
+ };
+ return Type.initPayload(&payload.base);
+ }
+
+ const payload = try scope.arena().create(Type.Payload.Array);
+ payload.* = .{
+ .len = len,
+ .elem_type = elem_type,
+ };
+ return Type.initPayload(&payload.base);
+}
+
+pub fn errorUnionType(self: *Module, scope: *Scope, error_set: Type, payload: Type) Allocator.Error!Type {
+ assert(error_set.zigTypeTag() == .ErrorSet);
+ if (error_set.eql(Type.initTag(.anyerror)) and payload.eql(Type.initTag(.void))) {
+ return Type.initTag(.anyerror_void_error_union);
+ }
+
+ const result = try scope.arena().create(Type.Payload.ErrorUnion);
+ result.* = .{
+ .error_set = error_set,
+ .payload = payload,
+ };
+ return Type.initPayload(&result.base);
+}
+
+pub fn anyframeType(self: *Module, scope: *Scope, return_type: Type) Allocator.Error!Type {
+ const result = try scope.arena().create(Type.Payload.AnyFrame);
+ result.* = .{
+ .return_type = return_type,
+ };
+ return Type.initPayload(&result.base);
+}
+
+pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {
+ const zir_module = scope.namespace();
+ const source = zir_module.getSource(self) catch @panic("dumpInst failed to get source");
+ const loc = std.zig.findLineColumn(source, inst.src);
+ if (inst.tag == .constant) {
+ std.debug.print("constant ty={} val={} src={}:{}:{}\n", .{
+ inst.ty,
+ inst.castTag(.constant).?.val,
+ zir_module.subFilePath(),
+ loc.line + 1,
+ loc.column + 1,
+ });
+ } else if (inst.deaths == 0) {
+ std.debug.print("{} ty={} src={}:{}:{}\n", .{
+ @tagName(inst.tag),
+ inst.ty,
+ zir_module.subFilePath(),
+ loc.line + 1,
+ loc.column + 1,
+ });
+ } else {
+ std.debug.print("{} ty={} deaths={b} src={}:{}:{}\n", .{
+ @tagName(inst.tag),
+ inst.ty,
+ inst.deaths,
+ zir_module.subFilePath(),
+ loc.line + 1,
+ loc.column + 1,
+ });
+ }
+}
+
+pub const PanicId = enum {
+ unreach,
+ unwrap_null,
+};
+
+pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic_id: PanicId) !void {
+ const block_inst = try parent_block.arena.create(Inst.Block);
+ block_inst.* = .{
+ .base = .{
+ .tag = Inst.Block.base_tag,
+ .ty = Type.initTag(.void),
+ .src = ok.src,
+ },
+ .body = .{
+ .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the condbr.
+ },
+ };
+
+ const ok_body: ir.Body = .{
+ .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the brvoid.
+ };
+ const brvoid = try parent_block.arena.create(Inst.BrVoid);
+ brvoid.* = .{
+ .base = .{
+ .tag = .brvoid,
+ .ty = Type.initTag(.noreturn),
+ .src = ok.src,
+ },
+ .block = block_inst,
+ };
+ ok_body.instructions[0] = &brvoid.base;
+
+ var fail_block: Scope.Block = .{
+ .parent = parent_block,
+ .func = parent_block.func,
+ .decl = parent_block.decl,
+ .instructions = .{},
+ .arena = parent_block.arena,
+ .is_comptime = parent_block.is_comptime,
+ };
+ defer fail_block.instructions.deinit(mod.gpa);
+
+ _ = try mod.safetyPanic(&fail_block, ok.src, panic_id);
+
+ const fail_body: ir.Body = .{ .instructions = try parent_block.arena.dupe(*Inst, fail_block.instructions.items) };
+
+ const condbr = try parent_block.arena.create(Inst.CondBr);
+ condbr.* = .{
+ .base = .{
+ .tag = .condbr,
+ .ty = Type.initTag(.noreturn),
+ .src = ok.src,
+ },
+ .condition = ok,
+ .then_body = ok_body,
+ .else_body = fail_body,
+ };
+ block_inst.body.instructions[0] = &condbr.base;
+
+ try parent_block.instructions.append(mod.gpa, &block_inst.base);
+}
+
+pub fn safetyPanic(mod: *Module, block: *Scope.Block, src: usize, panic_id: PanicId) !*Inst {
+ // TODO Once we have a panic function to call, call it here instead of breakpoint.
+ _ = try mod.addNoOp(block, src, Type.initTag(.void), .breakpoint);
+ return mod.addNoOp(block, src, Type.initTag(.noreturn), .unreach);
+}
+
+pub fn getTarget(self: Module) Target {
+ return self.comp.bin_file.options.target;
+}
+
+pub fn optimizeMode(self: Module) std.builtin.Mode {
+ return self.comp.bin_file.options.optimize_mode;
+}
diff --git a/src/Package.zig b/src/Package.zig
new file mode 100644
index 0000000000000000000000000000000000000000..14be8b64d6fb1905be106a17ce83cd1749fc076d
--- /dev/null
+++ b/src/Package.zig
@@ -0,0 +1,61 @@
+pub const Table = std.StringHashMapUnmanaged(*Package);
+
+root_src_directory: Compilation.Directory,
+/// Relative to `root_src_directory`. May contain path separators.
+root_src_path: []const u8,
+table: Table = .{},
+
+const std = @import("std");
+const mem = std.mem;
+const Allocator = std.mem.Allocator;
+const assert = std.debug.assert;
+const Package = @This();
+const Compilation = @import("Compilation.zig");
+
+/// No references to `root_src_dir` and `root_src_path` are kept.
+pub fn create(
+ gpa: *Allocator,
+ base_directory: Compilation.Directory,
+ /// Relative to `base_directory`.
+ root_src_dir: []const u8,
+ /// Relative to `root_src_dir`.
+ root_src_path: []const u8,
+) !*Package {
+ const ptr = try gpa.create(Package);
+ errdefer gpa.destroy(ptr);
+
+ const root_src_dir_path = try base_directory.join(gpa, &[_][]const u8{root_src_dir});
+ errdefer gpa.free(root_src_dir_path);
+
+ const root_src_path_dupe = try mem.dupe(gpa, u8, root_src_path);
+ errdefer gpa.free(root_src_path_dupe);
+
+ ptr.* = .{
+ .root_src_directory = .{
+ .path = root_src_dir_path,
+ .handle = try base_directory.handle.openDir(root_src_dir, .{}),
+ },
+ .root_src_path = root_src_path_dupe,
+ };
+ return ptr;
+}
+
+pub fn destroy(pkg: *Package, gpa: *Allocator) void {
+ pkg.root_src_directory.handle.close();
+ gpa.free(pkg.root_src_path);
+ if (pkg.root_src_directory.path) |p| gpa.free(p);
+ {
+ var it = pkg.table.iterator();
+ while (it.next()) |kv| {
+ gpa.free(kv.key);
+ }
+ }
+ pkg.table.deinit(gpa);
+ gpa.destroy(pkg);
+}
+
+pub fn add(pkg: *Package, gpa: *Allocator, name: []const u8, package: *Package) !void {
+ try pkg.table.ensureCapacity(gpa, pkg.table.items().len + 1);
+ const name_dupe = try mem.dupe(gpa, u8, name);
+ pkg.table.putAssumeCapacityNoClobber(name_dupe, package);
+}
diff --git a/src/TypedValue.zig b/src/TypedValue.zig
new file mode 100644
index 0000000000000000000000000000000000000000..48b2c04970d15593a420f73d40967f003cbec9d9
--- /dev/null
+++ b/src/TypedValue.zig
@@ -0,0 +1,31 @@
+const std = @import("std");
+const Type = @import("type.zig").Type;
+const Value = @import("value.zig").Value;
+const Allocator = std.mem.Allocator;
+const TypedValue = @This();
+
+ty: Type,
+val: Value,
+
+/// Memory management for TypedValue. The main purpose of this type
+/// is to be small and have a deinit() function to free associated resources.
+pub const Managed = struct {
+ /// If the tag value is less than Tag.no_payload_count, then no pointer
+ /// dereference is needed.
+ typed_value: TypedValue,
+ /// If this is `null` then there is no memory management needed.
+ arena: ?*std.heap.ArenaAllocator.State = null,
+
+ pub fn deinit(self: *Managed, allocator: *Allocator) void {
+ if (self.arena) |a| a.promote(allocator).deinit();
+ self.* = undefined;
+ }
+};
+
+/// Assumes arena allocation. Does a recursive copy.
+pub fn copy(self: TypedValue, allocator: *Allocator) error{OutOfMemory}!TypedValue {
+ return TypedValue{
+ .ty = try self.ty.copy(allocator),
+ .val = try self.val.copy(allocator),
+ };
+}
diff --git a/src/all_types.hpp b/src/all_types.hpp
deleted file mode 100644
index 75a89d272f9c321d22c22e30c9f50eddc97dceb0..0000000000000000000000000000000000000000
--- a/src/all_types.hpp
+++ /dev/null
@@ -1,4653 +0,0 @@
-/*
- * Copyright (c) 2015 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#ifndef ZIG_ALL_TYPES_HPP
-#define ZIG_ALL_TYPES_HPP
-
-#include "list.hpp"
-#include "buffer.hpp"
-#include "zig_llvm.h"
-#include "hash_map.hpp"
-#include "errmsg.hpp"
-#include "bigint.hpp"
-#include "bigfloat.hpp"
-#include "target.hpp"
-#include "tokenizer.hpp"
-
-#ifndef NDEBUG
-#define DBG_MACRO_NO_WARNING
-#include
-#endif
-
-struct AstNode;
-struct ZigFn;
-struct Scope;
-struct ScopeBlock;
-struct ScopeFnDef;
-struct ScopeExpr;
-struct ZigType;
-struct ZigVar;
-struct ErrorTableEntry;
-struct BuiltinFnEntry;
-struct TypeStructField;
-struct CodeGen;
-struct ZigValue;
-struct IrInst;
-struct IrInstSrc;
-struct IrInstGen;
-struct IrInstGenCast;
-struct IrInstGenAlloca;
-struct IrInstGenCall;
-struct IrInstGenAwait;
-struct IrBasicBlockSrc;
-struct IrBasicBlockGen;
-struct ScopeDecls;
-struct ZigWindowsSDK;
-struct Tld;
-struct TldExport;
-struct IrAnalyze;
-struct ResultLoc;
-struct ResultLocPeer;
-struct ResultLocPeerParent;
-struct ResultLocBitCast;
-struct ResultLocCast;
-struct ResultLocReturn;
-struct IrExecutableGen;
-
-enum FileExt {
- FileExtUnknown,
- FileExtAsm,
- FileExtC,
- FileExtCpp,
- FileExtHeader,
- FileExtLLVMIr,
- FileExtLLVMBitCode,
-};
-
-enum PtrLen {
- PtrLenUnknown,
- PtrLenSingle,
- PtrLenC,
-};
-
-enum CallingConvention {
- CallingConventionUnspecified,
- CallingConventionC,
- CallingConventionCold,
- CallingConventionNaked,
- CallingConventionAsync,
- CallingConventionInterrupt,
- CallingConventionSignal,
- CallingConventionStdcall,
- CallingConventionFastcall,
- CallingConventionVectorcall,
- CallingConventionThiscall,
- CallingConventionAPCS,
- CallingConventionAAPCS,
- CallingConventionAAPCSVFP,
-};
-
-// This one corresponds to the builtin.zig enum.
-enum BuiltinPtrSize {
- BuiltinPtrSizeOne,
- BuiltinPtrSizeMany,
- BuiltinPtrSizeSlice,
- BuiltinPtrSizeC,
-};
-
-enum UndefAllowed {
- UndefOk,
- UndefBad,
- LazyOkNoUndef,
- LazyOk,
-};
-
-enum X64CABIClass {
- X64CABIClass_Unknown,
- X64CABIClass_MEMORY,
- X64CABIClass_MEMORY_nobyval,
- X64CABIClass_INTEGER,
- X64CABIClass_SSE,
-};
-
-struct IrExecutableSrc {
- ZigList basic_block_list;
- Buf *name;
- ZigFn *name_fn;
- size_t mem_slot_count;
- size_t next_debug_id;
- size_t *backward_branch_count;
- size_t *backward_branch_quota;
- ZigFn *fn_entry;
- Buf *c_import_buf;
- AstNode *source_node;
- IrExecutableGen *parent_exec;
- IrAnalyze *analysis;
- Scope *begin_scope;
- ErrorMsg *first_err_trace_msg;
- ZigList tld_list;
-
- bool is_inline;
- bool is_generic_instantiation;
- bool need_err_code_spill;
-
- // This is a function for use in the debugger to print
- // the source location.
- void src();
-};
-
-struct IrExecutableGen {
- ZigList basic_block_list;
- Buf *name;
- ZigFn *name_fn;
- size_t mem_slot_count;
- size_t next_debug_id;
- size_t *backward_branch_count;
- size_t *backward_branch_quota;
- ZigFn *fn_entry;
- Buf *c_import_buf;
- AstNode *source_node;
- IrExecutableGen *parent_exec;
- IrExecutableSrc *source_exec;
- Scope *begin_scope;
- ErrorMsg *first_err_trace_msg;
- ZigList tld_list;
-
- bool is_inline;
- bool is_generic_instantiation;
- bool need_err_code_spill;
-
- // This is a function for use in the debugger to print
- // the source location.
- void src();
-};
-
-enum OutType {
- OutTypeUnknown,
- OutTypeExe,
- OutTypeLib,
- OutTypeObj,
-};
-
-enum ConstParentId {
- ConstParentIdNone,
- ConstParentIdStruct,
- ConstParentIdErrUnionCode,
- ConstParentIdErrUnionPayload,
- ConstParentIdOptionalPayload,
- ConstParentIdArray,
- ConstParentIdUnion,
- ConstParentIdScalar,
-};
-
-struct ConstParent {
- ConstParentId id;
-
- union {
- struct {
- ZigValue *array_val;
- size_t elem_index;
- } p_array;
- struct {
- ZigValue *struct_val;
- size_t field_index;
- } p_struct;
- struct {
- ZigValue *err_union_val;
- } p_err_union_code;
- struct {
- ZigValue *err_union_val;
- } p_err_union_payload;
- struct {
- ZigValue *optional_val;
- } p_optional_payload;
- struct {
- ZigValue *union_val;
- } p_union;
- struct {
- ZigValue *scalar_val;
- } p_scalar;
- } data;
-};
-
-struct ConstStructValue {
- ZigValue **fields;
-};
-
-struct ConstUnionValue {
- BigInt tag;
- ZigValue *payload;
-};
-
-enum ConstArraySpecial {
- ConstArraySpecialNone,
- ConstArraySpecialUndef,
- ConstArraySpecialBuf,
-};
-
-struct ConstArrayValue {
- ConstArraySpecial special;
- union {
- struct {
- ZigValue *elements;
- } s_none;
- Buf *s_buf;
- } data;
-};
-
-enum ConstPtrSpecial {
- // Enforce explicitly setting this ID by making the zero value invalid.
- ConstPtrSpecialInvalid,
- // The pointer is a reference to a single object.
- ConstPtrSpecialRef,
- // The pointer points to an element in an underlying array.
- // Not to be confused with ConstPtrSpecialSubArray.
- ConstPtrSpecialBaseArray,
- // The pointer points to a field in an underlying struct.
- ConstPtrSpecialBaseStruct,
- // The pointer points to the error set field of an error union
- ConstPtrSpecialBaseErrorUnionCode,
- // The pointer points to the payload field of an error union
- ConstPtrSpecialBaseErrorUnionPayload,
- // The pointer points to the payload field of an optional
- ConstPtrSpecialBaseOptionalPayload,
- // This means that we did a compile-time pointer reinterpret and we cannot
- // understand the value of pointee at compile time. However, we will still
- // emit a binary with a compile time known address.
- // In this case index is the numeric address value.
- ConstPtrSpecialHardCodedAddr,
- // This means that the pointer represents memory of assigning to _.
- // That is, storing discards the data, and loading is invalid.
- ConstPtrSpecialDiscard,
- // This is actually a function.
- ConstPtrSpecialFunction,
- // This means the pointer is null. This is only allowed when the type is ?*T.
- // We use this instead of ConstPtrSpecialHardCodedAddr because often we check
- // for that value to avoid doing comptime work.
- // We need the data layout for ConstCastOnly == true
- // types to be the same, so all optionals of pointer types use x_ptr
- // instead of x_optional.
- ConstPtrSpecialNull,
- // The pointer points to a sub-array (not an individual element).
- // Not to be confused with ConstPtrSpecialBaseArray. However, it uses the same
- // union payload struct (base_array).
- ConstPtrSpecialSubArray,
-};
-
-enum ConstPtrMut {
- // The pointer points to memory that is known at compile time and immutable.
- ConstPtrMutComptimeConst,
- // This means that the pointer points to memory used by a comptime variable,
- // so attempting to write a non-compile-time known value is an error
- // But the underlying value is allowed to change at compile time.
- ConstPtrMutComptimeVar,
- // The pointer points to memory that is known only at runtime.
- // For example it may point to the initializer value of a variable.
- ConstPtrMutRuntimeVar,
- // The pointer points to memory for which it must be inferred whether the
- // value is comptime known or not.
- ConstPtrMutInfer,
-};
-
-struct ConstPtrValue {
- ConstPtrSpecial special;
- ConstPtrMut mut;
-
- union {
- struct {
- ZigValue *pointee;
- } ref;
- struct {
- ZigValue *array_val;
- size_t elem_index;
- } base_array;
- struct {
- ZigValue *struct_val;
- size_t field_index;
- } base_struct;
- struct {
- ZigValue *err_union_val;
- } base_err_union_code;
- struct {
- ZigValue *err_union_val;
- } base_err_union_payload;
- struct {
- ZigValue *optional_val;
- } base_optional_payload;
- struct {
- uint64_t addr;
- } hard_coded_addr;
- struct {
- ZigFn *fn_entry;
- } fn;
- } data;
-};
-
-struct ConstErrValue {
- ZigValue *error_set;
- ZigValue *payload;
-};
-
-struct ConstBoundFnValue {
- ZigFn *fn;
- IrInstGen *first_arg;
- IrInst *first_arg_src;
-};
-
-struct ConstArgTuple {
- size_t start_index;
- size_t end_index;
-};
-
-enum ConstValSpecial {
- ConstValSpecialRuntime,
- ConstValSpecialStatic,
- ConstValSpecialUndef,
- ConstValSpecialLazy,
-};
-
-enum RuntimeHintErrorUnion {
- RuntimeHintErrorUnionUnknown,
- RuntimeHintErrorUnionError,
- RuntimeHintErrorUnionNonError,
-};
-
-enum RuntimeHintOptional {
- RuntimeHintOptionalUnknown,
- RuntimeHintOptionalNull, // TODO is this value even possible? if this is the case it might mean the const value is compile time known.
- RuntimeHintOptionalNonNull,
-};
-
-enum RuntimeHintPtr {
- RuntimeHintPtrUnknown,
- RuntimeHintPtrStack,
- RuntimeHintPtrNonStack,
-};
-
-enum RuntimeHintSliceId {
- RuntimeHintSliceIdUnknown,
- RuntimeHintSliceIdLen,
-};
-
-struct RuntimeHintSlice {
- enum RuntimeHintSliceId id;
- uint64_t len;
-};
-
-enum LazyValueId {
- LazyValueIdInvalid,
- LazyValueIdAlignOf,
- LazyValueIdSizeOf,
- LazyValueIdPtrType,
- LazyValueIdOptType,
- LazyValueIdSliceType,
- LazyValueIdFnType,
- LazyValueIdErrUnionType,
- LazyValueIdArrayType,
- LazyValueIdTypeInfoDecls,
-};
-
-struct LazyValue {
- LazyValueId id;
-};
-
-struct LazyValueTypeInfoDecls {
- LazyValue base;
-
- IrAnalyze *ira;
-
- ScopeDecls *decls_scope;
- IrInst *source_instr;
-};
-
-struct LazyValueAlignOf {
- LazyValue base;
-
- IrAnalyze *ira;
- IrInstGen *target_type;
-};
-
-struct LazyValueSizeOf {
- LazyValue base;
-
- IrAnalyze *ira;
- IrInstGen *target_type;
-
- bool bit_size;
-};
-
-struct LazyValueSliceType {
- LazyValue base;
-
- IrAnalyze *ira;
- IrInstGen *sentinel; // can be null
- IrInstGen *elem_type;
- IrInstGen *align_inst; // can be null
-
- bool is_const;
- bool is_volatile;
- bool is_allowzero;
-};
-
-struct LazyValueArrayType {
- LazyValue base;
-
- IrAnalyze *ira;
- IrInstGen *sentinel; // can be null
- IrInstGen *elem_type;
- uint64_t length;
-};
-
-struct LazyValuePtrType {
- LazyValue base;
-
- IrAnalyze *ira;
- IrInstGen *sentinel; // can be null
- IrInstGen *elem_type;
- IrInstGen *align_inst; // can be null
-
- PtrLen ptr_len;
- uint32_t bit_offset_in_host;
-
- uint32_t host_int_bytes;
- bool is_const;
- bool is_volatile;
- bool is_allowzero;
-};
-
-struct LazyValueOptType {
- LazyValue base;
-
- IrAnalyze *ira;
- IrInstGen *payload_type;
-};
-
-struct LazyValueFnType {
- LazyValue base;
-
- IrAnalyze *ira;
- AstNode *proto_node;
- IrInstGen **param_types;
- IrInstGen *align_inst; // can be null
- IrInstGen *return_type;
-
- CallingConvention cc;
- bool is_generic;
-};
-
-struct LazyValueErrUnionType {
- LazyValue base;
-
- IrAnalyze *ira;
- IrInstGen *err_set_type;
- IrInstGen *payload_type;
- Buf *type_name;
-};
-
-struct ZigValue {
- ZigType *type;
- ConstValSpecial special;
- uint32_t llvm_align;
- ConstParent parent;
- LLVMValueRef llvm_value;
- LLVMValueRef llvm_global;
-
- union {
- // populated if special == ConstValSpecialLazy
- LazyValue *x_lazy;
-
- // populated if special == ConstValSpecialStatic
- BigInt x_bigint;
- BigFloat x_bigfloat;
- float16_t x_f16;
- float x_f32;
- double x_f64;
- float128_t x_f128;
- bool x_bool;
- ConstBoundFnValue x_bound_fn;
- ZigType *x_type;
- ZigValue *x_optional;
- ConstErrValue x_err_union;
- ErrorTableEntry *x_err_set;
- BigInt x_enum_tag;
- ConstStructValue x_struct;
- ConstUnionValue x_union;
- ConstArrayValue x_array;
- ConstPtrValue x_ptr;
- ConstArgTuple x_arg_tuple;
- Buf *x_enum_literal;
-
- // populated if special == ConstValSpecialRuntime
- RuntimeHintErrorUnion rh_error_union;
- RuntimeHintOptional rh_maybe;
- RuntimeHintPtr rh_ptr;
- RuntimeHintSlice rh_slice;
- } data;
-
- // uncomment this to find bugs. can't leave it uncommented because of a gcc-9 warning
- //ZigValue& operator= (const ZigValue &other) = delete; // use copy_const_val
-
- ZigValue(const ZigValue &other) = delete; // plz zero initialize with ZigValue val = {};
-
- // for use in debuggers
- void dump();
-};
-
-enum ReturnKnowledge {
- ReturnKnowledgeUnknown,
- ReturnKnowledgeKnownError,
- ReturnKnowledgeKnownNonError,
- ReturnKnowledgeKnownNull,
- ReturnKnowledgeKnownNonNull,
- ReturnKnowledgeSkipDefers,
-};
-
-enum VisibMod {
- VisibModPrivate,
- VisibModPub,
-};
-
-enum GlobalLinkageId {
- GlobalLinkageIdInternal,
- GlobalLinkageIdStrong,
- GlobalLinkageIdWeak,
- GlobalLinkageIdLinkOnce,
-};
-
-enum TldId {
- TldIdVar,
- TldIdFn,
- TldIdContainer,
- TldIdCompTime,
- TldIdUsingNamespace,
-};
-
-enum TldResolution {
- TldResolutionUnresolved,
- TldResolutionResolving,
- TldResolutionInvalid,
- TldResolutionOkLazy,
- TldResolutionOk,
-};
-
-struct Tld {
- TldId id;
- Buf *name;
- VisibMod visib_mod;
- AstNode *source_node;
-
- ZigType *import;
- Scope *parent_scope;
- TldResolution resolution;
-};
-
-struct TldVar {
- Tld base;
-
- ZigVar *var;
- Buf *extern_lib_name;
- bool analyzing_type; // flag to detect dependency loops
-};
-
-struct TldFn {
- Tld base;
-
- ZigFn *fn_entry;
- Buf *extern_lib_name;
-};
-
-struct TldContainer {
- Tld base;
-
- ScopeDecls *decls_scope;
- ZigType *type_entry;
-};
-
-struct TldCompTime {
- Tld base;
-};
-
-struct TldUsingNamespace {
- Tld base;
-
- ZigValue *using_namespace_value;
-};
-
-struct TypeEnumField {
- Buf *name;
- BigInt value;
- uint32_t decl_index;
- AstNode *decl_node;
-};
-
-struct TypeUnionField {
- Buf *name;
- ZigType *type_entry; // available after ResolveStatusSizeKnown
- ZigValue *type_val; // available after ResolveStatusZeroBitsKnown
- TypeEnumField *enum_field;
- AstNode *decl_node;
- uint32_t gen_index;
- uint32_t align;
-};
-
-enum NodeType {
- NodeTypeFnProto,
- NodeTypeFnDef,
- NodeTypeParamDecl,
- NodeTypeBlock,
- NodeTypeGroupedExpr,
- NodeTypeReturnExpr,
- NodeTypeDefer,
- NodeTypeVariableDeclaration,
- NodeTypeTestDecl,
- NodeTypeBinOpExpr,
- NodeTypeCatchExpr,
- NodeTypeFloatLiteral,
- NodeTypeIntLiteral,
- NodeTypeStringLiteral,
- NodeTypeCharLiteral,
- NodeTypeSymbol,
- NodeTypePrefixOpExpr,
- NodeTypePointerType,
- NodeTypeFnCallExpr,
- NodeTypeArrayAccessExpr,
- NodeTypeSliceExpr,
- NodeTypeFieldAccessExpr,
- NodeTypePtrDeref,
- NodeTypeUnwrapOptional,
- NodeTypeUsingNamespace,
- NodeTypeBoolLiteral,
- NodeTypeNullLiteral,
- NodeTypeUndefinedLiteral,
- NodeTypeUnreachable,
- NodeTypeIfBoolExpr,
- NodeTypeWhileExpr,
- NodeTypeForExpr,
- NodeTypeSwitchExpr,
- NodeTypeSwitchProng,
- NodeTypeSwitchRange,
- NodeTypeCompTime,
- NodeTypeNoSuspend,
- NodeTypeBreak,
- NodeTypeContinue,
- NodeTypeAsmExpr,
- NodeTypeContainerDecl,
- NodeTypeStructField,
- NodeTypeContainerInitExpr,
- NodeTypeStructValueField,
- NodeTypeArrayType,
- NodeTypeInferredArrayType,
- NodeTypeErrorType,
- NodeTypeIfErrorExpr,
- NodeTypeIfOptional,
- NodeTypeErrorSetDecl,
- NodeTypeErrorSetField,
- NodeTypeResume,
- NodeTypeAwaitExpr,
- NodeTypeSuspend,
- NodeTypeAnyFrameType,
- NodeTypeEnumLiteral,
- NodeTypeAnyTypeField,
-};
-
-enum FnInline {
- FnInlineAuto,
- FnInlineAlways,
- FnInlineNever,
-};
-
-struct AstNodeFnProto {
- Buf *name;
- ZigList params;
- AstNode *return_type;
- Token *return_anytype_token;
- AstNode *fn_def_node;
- // populated if this is an extern declaration
- Buf *lib_name;
- // populated if the "align A" is present
- AstNode *align_expr;
- // populated if the "section(S)" is present
- AstNode *section_expr;
- // populated if the "callconv(S)" is present
- AstNode *callconv_expr;
- Buf doc_comments;
-
- FnInline fn_inline;
-
- VisibMod visib_mod;
- bool auto_err_set;
- bool is_var_args;
- bool is_extern;
- bool is_export;
-};
-
-struct AstNodeFnDef {
- AstNode *fn_proto;
- AstNode *body;
-};
-
-struct AstNodeParamDecl {
- Buf *name;
- AstNode *type;
- Token *anytype_token;
- Buf doc_comments;
- bool is_noalias;
- bool is_comptime;
- bool is_var_args;
-};
-
-struct AstNodeBlock {
- Buf *name;
- ZigList statements;
-};
-
-enum ReturnKind {
- ReturnKindUnconditional,
- ReturnKindError,
-};
-
-struct AstNodeReturnExpr {
- ReturnKind kind;
- // might be null in case of return void;
- AstNode *expr;
-};
-
-struct AstNodeDefer {
- ReturnKind kind;
- AstNode *err_payload;
- AstNode *expr;
-
- // temporary data used in IR generation
- Scope *child_scope;
- Scope *expr_scope;
-};
-
-struct AstNodeVariableDeclaration {
- Buf *symbol;
- // one or both of type and expr will be non null
- AstNode *type;
- AstNode *expr;
- // populated if this is an extern declaration
- Buf *lib_name;
- // populated if the "align(A)" is present
- AstNode *align_expr;
- // populated if the "section(S)" is present
- AstNode *section_expr;
- Token *threadlocal_tok;
- Buf doc_comments;
-
- VisibMod visib_mod;
- bool is_const;
- bool is_comptime;
- bool is_export;
- bool is_extern;
-};
-
-struct AstNodeTestDecl {
- Buf *name;
-
- AstNode *body;
-};
-
-enum BinOpType {
- BinOpTypeInvalid,
- BinOpTypeAssign,
- BinOpTypeAssignTimes,
- BinOpTypeAssignTimesWrap,
- BinOpTypeAssignDiv,
- BinOpTypeAssignMod,
- BinOpTypeAssignPlus,
- BinOpTypeAssignPlusWrap,
- BinOpTypeAssignMinus,
- BinOpTypeAssignMinusWrap,
- BinOpTypeAssignBitShiftLeft,
- BinOpTypeAssignBitShiftRight,
- BinOpTypeAssignBitAnd,
- BinOpTypeAssignBitXor,
- BinOpTypeAssignBitOr,
- BinOpTypeAssignMergeErrorSets,
- BinOpTypeBoolOr,
- BinOpTypeBoolAnd,
- BinOpTypeCmpEq,
- BinOpTypeCmpNotEq,
- BinOpTypeCmpLessThan,
- BinOpTypeCmpGreaterThan,
- BinOpTypeCmpLessOrEq,
- BinOpTypeCmpGreaterOrEq,
- BinOpTypeBinOr,
- BinOpTypeBinXor,
- BinOpTypeBinAnd,
- BinOpTypeBitShiftLeft,
- BinOpTypeBitShiftRight,
- BinOpTypeAdd,
- BinOpTypeAddWrap,
- BinOpTypeSub,
- BinOpTypeSubWrap,
- BinOpTypeMult,
- BinOpTypeMultWrap,
- BinOpTypeDiv,
- BinOpTypeMod,
- BinOpTypeUnwrapOptional,
- BinOpTypeArrayCat,
- BinOpTypeArrayMult,
- BinOpTypeErrorUnion,
- BinOpTypeMergeErrorSets,
-};
-
-struct AstNodeBinOpExpr {
- AstNode *op1;
- BinOpType bin_op;
- AstNode *op2;
-};
-
-struct AstNodeCatchExpr {
- AstNode *op1;
- AstNode *symbol; // can be null
- AstNode *op2;
-};
-
-struct AstNodeUnwrapOptional {
- AstNode *expr;
-};
-
-// Must be synchronized with std.builtin.CallOptions.Modifier
-enum CallModifier {
- CallModifierNone,
- CallModifierAsync,
- CallModifierNeverTail,
- CallModifierNeverInline,
- CallModifierNoSuspend,
- CallModifierAlwaysTail,
- CallModifierAlwaysInline,
- CallModifierCompileTime,
-
- // These are additional tags in the compiler, but not exposed in the std lib.
- CallModifierBuiltin,
-};
-
-struct AstNodeFnCallExpr {
- AstNode *fn_ref_expr;
- ZigList params;
- CallModifier modifier;
- bool seen; // used by @compileLog
-};
-
-struct AstNodeArrayAccessExpr {
- AstNode *array_ref_expr;
- AstNode *subscript;
-};
-
-struct AstNodeSliceExpr {
- AstNode *array_ref_expr;
- AstNode *start;
- AstNode *end;
- AstNode *sentinel; // can be null
-};
-
-struct AstNodeFieldAccessExpr {
- AstNode *struct_expr;
- Buf *field_name;
-};
-
-struct AstNodePtrDerefExpr {
- AstNode *target;
-};
-
-enum PrefixOp {
- PrefixOpInvalid,
- PrefixOpBoolNot,
- PrefixOpBinNot,
- PrefixOpNegation,
- PrefixOpNegationWrap,
- PrefixOpOptional,
- PrefixOpAddrOf,
-};
-
-struct AstNodePrefixOpExpr {
- PrefixOp prefix_op;
- AstNode *primary_expr;
-};
-
-struct AstNodePointerType {
- Token *star_token;
- AstNode *sentinel;
- AstNode *align_expr;
- BigInt *bit_offset_start;
- BigInt *host_int_bytes;
- AstNode *op_expr;
- Token *allow_zero_token;
- bool is_const;
- bool is_volatile;
-};
-
-struct AstNodeInferredArrayType {
- AstNode *sentinel; // can be null
- AstNode *child_type;
-};
-
-struct AstNodeArrayType {
- AstNode *size;
- AstNode *sentinel;
- AstNode *child_type;
- AstNode *align_expr;
- Token *allow_zero_token;
- bool is_const;
- bool is_volatile;
-};
-
-struct AstNodeUsingNamespace {
- VisibMod visib_mod;
- AstNode *expr;
-};
-
-struct AstNodeIfBoolExpr {
- AstNode *condition;
- AstNode *then_block;
- AstNode *else_node; // null, block node, or other if expr node
-};
-
-struct AstNodeTryExpr {
- Buf *var_symbol;
- bool var_is_ptr;
- AstNode *target_node;
- AstNode *then_node;
- AstNode *else_node;
- Buf *err_symbol;
-};
-
-struct AstNodeTestExpr {
- Buf *var_symbol;
- bool var_is_ptr;
- AstNode *target_node;
- AstNode *then_node;
- AstNode *else_node; // null, block node, or other if expr node
-};
-
-struct AstNodeWhileExpr {
- Buf *name;
- AstNode *condition;
- Buf *var_symbol;
- bool var_is_ptr;
- AstNode *continue_expr;
- AstNode *body;
- AstNode *else_node;
- Buf *err_symbol;
- bool is_inline;
-};
-
-struct AstNodeForExpr {
- Buf *name;
- AstNode *array_expr;
- AstNode *elem_node; // always a symbol
- AstNode *index_node; // always a symbol, might be null
- AstNode *body;
- AstNode *else_node; // can be null
- bool elem_is_ptr;
- bool is_inline;
-};
-
-struct AstNodeSwitchExpr {
- AstNode *expr;
- ZigList prongs;
-};
-
-struct AstNodeSwitchProng {
- ZigList items;
- AstNode *var_symbol;
- AstNode *expr;
- bool var_is_ptr;
- bool any_items_are_range;
-};
-
-struct AstNodeSwitchRange {
- AstNode *start;
- AstNode *end;
-};
-
-struct AstNodeCompTime {
- AstNode *expr;
-};
-
-struct AstNodeNoSuspend {
- AstNode *expr;
-};
-
-struct AsmOutput {
- Buf *asm_symbolic_name;
- Buf *constraint;
- Buf *variable_name;
- AstNode *return_type; // null unless "=r" and return
-};
-
-struct AsmInput {
- Buf *asm_symbolic_name;
- Buf *constraint;
- AstNode *expr;
-};
-
-struct SrcPos {
- size_t line;
- size_t column;
-};
-
-enum AsmTokenId {
- AsmTokenIdTemplate,
- AsmTokenIdPercent,
- AsmTokenIdVar,
- AsmTokenIdUniqueId,
-};
-
-struct AsmToken {
- enum AsmTokenId id;
- size_t start;
- size_t end;
-};
-
-struct AstNodeAsmExpr {
- Token *volatile_token;
- AstNode *asm_template;
- ZigList output_list;
- ZigList input_list;
- ZigList clobber_list;
-};
-
-enum ContainerKind {
- ContainerKindStruct,
- ContainerKindEnum,
- ContainerKindUnion,
-};
-
-enum ContainerLayout {
- ContainerLayoutAuto,
- ContainerLayoutExtern,
- ContainerLayoutPacked,
-};
-
-struct AstNodeContainerDecl {
- AstNode *init_arg_expr; // enum(T), struct(endianness), or union(T), or union(enum(T))
- ZigList fields;
- ZigList decls;
- Buf doc_comments;
-
- ContainerKind kind;
- ContainerLayout layout;
-
- bool auto_enum, is_root; // union(enum)
-};
-
-struct AstNodeErrorSetField {
- Buf doc_comments;
- AstNode *field_name;
-};
-
-struct AstNodeErrorSetDecl {
- // Each AstNode could be AstNodeErrorSetField or just AstNodeSymbolExpr to save memory
- ZigList decls;
-};
-
-struct AstNodeStructField {
- Buf *name;
- AstNode *type;
- AstNode *value;
- // populated if the "align(A)" is present
- AstNode *align_expr;
- Buf doc_comments;
- Token *comptime_token;
-};
-
-struct AstNodeStringLiteral {
- Buf *buf;
-};
-
-struct AstNodeCharLiteral {
- uint32_t value;
-};
-
-struct AstNodeFloatLiteral {
- BigFloat *bigfloat;
-
- // overflow is true if when parsing the number, we discovered it would not
- // fit without losing data in a double
- bool overflow;
-};
-
-struct AstNodeIntLiteral {
- BigInt *bigint;
-};
-
-struct AstNodeStructValueField {
- Buf *name;
- AstNode *expr;
-};
-
-enum ContainerInitKind {
- ContainerInitKindStruct,
- ContainerInitKindArray,
-};
-
-struct AstNodeContainerInitExpr {
- AstNode *type;
- ZigList entries;
- ContainerInitKind kind;
-};
-
-struct AstNodeNullLiteral {
-};
-
-struct AstNodeUndefinedLiteral {
-};
-
-struct AstNodeThisLiteral {
-};
-
-struct AstNodeSymbolExpr {
- Buf *symbol;
-};
-
-struct AstNodeBoolLiteral {
- bool value;
-};
-
-struct AstNodeBreakExpr {
- Buf *name;
- AstNode *expr; // may be null
-};
-
-struct AstNodeResumeExpr {
- AstNode *expr;
-};
-
-struct AstNodeContinueExpr {
- Buf *name;
-};
-
-struct AstNodeUnreachableExpr {
-};
-
-
-struct AstNodeErrorType {
-};
-
-struct AstNodeAwaitExpr {
- AstNode *expr;
-};
-
-struct AstNodeSuspend {
- AstNode *block;
-};
-
-struct AstNodeAnyFrameType {
- AstNode *payload_type; // can be NULL
-};
-
-struct AstNodeEnumLiteral {
- Token *period;
- Token *identifier;
-};
-
-struct AstNode {
- enum NodeType type;
- bool already_traced_this_node;
- size_t line;
- size_t column;
- ZigType *owner;
- union {
- AstNodeFnDef fn_def;
- AstNodeFnProto fn_proto;
- AstNodeParamDecl param_decl;
- AstNodeBlock block;
- AstNode * grouped_expr;
- AstNodeReturnExpr return_expr;
- AstNodeDefer defer;
- AstNodeVariableDeclaration variable_declaration;
- AstNodeTestDecl test_decl;
- AstNodeBinOpExpr bin_op_expr;
- AstNodeCatchExpr unwrap_err_expr;
- AstNodeUnwrapOptional unwrap_optional;
- AstNodePrefixOpExpr prefix_op_expr;
- AstNodePointerType pointer_type;
- AstNodeFnCallExpr fn_call_expr;
- AstNodeArrayAccessExpr array_access_expr;
- AstNodeSliceExpr slice_expr;
- AstNodeUsingNamespace using_namespace;
- AstNodeIfBoolExpr if_bool_expr;
- AstNodeTryExpr if_err_expr;
- AstNodeTestExpr test_expr;
- AstNodeWhileExpr while_expr;
- AstNodeForExpr for_expr;
- AstNodeSwitchExpr switch_expr;
- AstNodeSwitchProng switch_prong;
- AstNodeSwitchRange switch_range;
- AstNodeCompTime comptime_expr;
- AstNodeNoSuspend nosuspend_expr;
- AstNodeAsmExpr asm_expr;
- AstNodeFieldAccessExpr field_access_expr;
- AstNodePtrDerefExpr ptr_deref_expr;
- AstNodeContainerDecl container_decl;
- AstNodeStructField struct_field;
- AstNodeStringLiteral string_literal;
- AstNodeCharLiteral char_literal;
- AstNodeFloatLiteral float_literal;
- AstNodeIntLiteral int_literal;
- AstNodeContainerInitExpr container_init_expr;
- AstNodeStructValueField struct_val_field;
- AstNodeNullLiteral null_literal;
- AstNodeUndefinedLiteral undefined_literal;
- AstNodeThisLiteral this_literal;
- AstNodeSymbolExpr symbol_expr;
- AstNodeBoolLiteral bool_literal;
- AstNodeBreakExpr break_expr;
- AstNodeContinueExpr continue_expr;
- AstNodeUnreachableExpr unreachable_expr;
- AstNodeArrayType array_type;
- AstNodeInferredArrayType inferred_array_type;
- AstNodeErrorType error_type;
- AstNodeErrorSetDecl err_set_decl;
- AstNodeErrorSetField err_set_field;
- AstNodeResumeExpr resume_expr;
- AstNodeAwaitExpr await_expr;
- AstNodeSuspend suspend;
- AstNodeAnyFrameType anyframe_type;
- AstNodeEnumLiteral enum_literal;
- } data;
-
- // This is a function for use in the debugger to print
- // the source location.
- void src();
-};
-
-// this struct is allocated with allocate_nonzero
-struct FnTypeParamInfo {
- bool is_noalias;
- ZigType *type;
-};
-
-struct GenericFnTypeId {
- CodeGen *codegen;
- ZigFn *fn_entry;
- ZigValue *params;
- size_t param_count;
-};
-
-uint32_t generic_fn_type_id_hash(GenericFnTypeId *id);
-bool generic_fn_type_id_eql(GenericFnTypeId *a, GenericFnTypeId *b);
-
-struct FnTypeId {
- ZigType *return_type;
- FnTypeParamInfo *param_info;
- size_t param_count;
- size_t next_param_index;
- bool is_var_args;
- CallingConvention cc;
- uint32_t alignment;
-};
-
-uint32_t fn_type_id_hash(FnTypeId*);
-bool fn_type_id_eql(FnTypeId *a, FnTypeId *b);
-
-static const uint32_t VECTOR_INDEX_NONE = UINT32_MAX;
-static const uint32_t VECTOR_INDEX_RUNTIME = UINT32_MAX - 1;
-
-struct InferredStructField {
- ZigType *inferred_struct_type;
- Buf *field_name;
- bool already_resolved;
-};
-
-struct ZigTypePointer {
- ZigType *child_type;
- ZigType *slice_parent;
-
- // Anonymous struct literal syntax uses this when the result location has
- // no type in it. This field is null if this pointer does not refer to
- // a field of a currently-being-inferred struct type.
- // When this is non-null, the pointer is pointing to the base of the inferred
- // struct.
- InferredStructField *inferred_struct_field;
-
- // This can be null. If it is non-null, it means the pointer is terminated by this
- // sentinel value. This is most commonly used for C-style strings, with a 0 byte
- // to specify the length of the memory pointed to.
- ZigValue *sentinel;
-
- PtrLen ptr_len;
- uint32_t explicit_alignment; // 0 means use ABI alignment
-
- uint32_t bit_offset_in_host;
- // size of host integer. 0 means no host integer; this field is aligned
- // when vector_index != VECTOR_INDEX_NONE this is the len of the containing vector
- uint32_t host_int_bytes;
-
- uint32_t vector_index; // see the VECTOR_INDEX_* constants
- bool is_const;
- bool is_volatile;
- bool allow_zero;
- bool resolve_loop_flag_zero_bits;
-};
-
-struct ZigTypeInt {
- uint32_t bit_count;
- bool is_signed;
-};
-
-struct ZigTypeFloat {
- size_t bit_count;
-};
-
-// Needs to have the same memory layout as ZigTypeVector
-struct ZigTypeArray {
- ZigType *child_type;
- uint64_t len;
- ZigValue *sentinel;
-};
-
-struct TypeStructField {
- Buf *name;
- ZigType *type_entry; // available after ResolveStatusSizeKnown
- ZigValue *type_val; // available after ResolveStatusZeroBitsKnown
- size_t src_index;
- size_t gen_index;
- size_t offset; // byte offset from beginning of struct
- AstNode *decl_node;
- ZigValue *init_val; // null and then memoized
- uint32_t bit_offset_in_host; // offset from the memory at gen_index
- uint32_t host_int_bytes; // size of host integer
- uint32_t align;
- bool is_comptime;
-};
-
-enum ResolveStatus {
- ResolveStatusUnstarted,
- ResolveStatusInvalid,
- ResolveStatusBeingInferred,
- ResolveStatusZeroBitsKnown,
- ResolveStatusAlignmentKnown,
- ResolveStatusSizeKnown,
- ResolveStatusLLVMFwdDecl,
- ResolveStatusLLVMFull,
-};
-
-struct ZigPackage {
- Buf root_src_dir;
- Buf root_src_path; // relative to root_src_dir
- Buf pkg_path; // a.b.c.d which follows the package dependency chain from the root package
-
- // reminder: hash tables must be initialized before use
- HashMap package_table;
-
- bool added_to_cache;
-};
-
-// Stuff that only applies to a struct which is the implicit root struct of a file
-struct RootStruct {
- ZigPackage *package;
- Buf *path; // relative to root_package->root_src_dir
- ZigList *line_offsets;
- Buf *source_code;
- ZigLLVMDIFile *di_file;
-};
-
-enum StructSpecial {
- StructSpecialNone,
- StructSpecialSlice,
- StructSpecialInferredTuple,
- StructSpecialInferredStruct,
-};
-
-struct ZigTypeStruct {
- AstNode *decl_node;
- TypeStructField **fields;
- ScopeDecls *decls_scope;
- HashMap fields_by_name;
- RootStruct *root_struct;
- uint32_t *host_int_bytes; // available for packed structs, indexed by gen_index
- size_t llvm_full_type_queue_index;
-
- uint32_t src_field_count;
- uint32_t gen_field_count;
-
- ContainerLayout layout;
- ResolveStatus resolve_status;
-
- StructSpecial special;
- // whether any of the fields require comptime
- // known after ResolveStatusZeroBitsKnown
- bool requires_comptime;
- bool resolve_loop_flag_zero_bits;
- bool resolve_loop_flag_other;
- bool created_by_at_type;
-};
-
-struct ZigTypeOptional {
- ZigType *child_type;
- ResolveStatus resolve_status;
-};
-
-struct ZigTypeErrorUnion {
- ZigType *err_set_type;
- ZigType *payload_type;
- size_t pad_bytes;
- LLVMTypeRef pad_llvm_type;
-};
-
-struct ZigTypeErrorSet {
- ErrorTableEntry **errors;
- ZigFn *infer_fn;
- uint32_t err_count;
- bool incomplete;
-};
-
-struct ZigTypeEnum {
- AstNode *decl_node;
- TypeEnumField *fields;
- ZigType *tag_int_type;
-
- ScopeDecls *decls_scope;
-
- LLVMValueRef name_function;
-
- HashMap fields_by_name;
- uint32_t src_field_count;
-
- ContainerLayout layout;
- ResolveStatus resolve_status;
-
- bool non_exhaustive;
- bool resolve_loop_flag;
-};
-
-uint32_t type_ptr_hash(const ZigType *ptr);
-bool type_ptr_eql(const ZigType *a, const ZigType *b);
-
-uint32_t pkg_ptr_hash(const ZigPackage *ptr);
-bool pkg_ptr_eql(const ZigPackage *a, const ZigPackage *b);
-
-uint32_t tld_ptr_hash(const Tld *ptr);
-bool tld_ptr_eql(const Tld *a, const Tld *b);
-
-uint32_t node_ptr_hash(const AstNode *ptr);
-bool node_ptr_eql(const AstNode *a, const AstNode *b);
-
-uint32_t fn_ptr_hash(const ZigFn *ptr);
-bool fn_ptr_eql(const ZigFn *a, const ZigFn *b);
-
-uint32_t err_ptr_hash(const ErrorTableEntry *ptr);
-bool err_ptr_eql(const ErrorTableEntry *a, const ErrorTableEntry *b);
-
-struct ZigTypeUnion {
- AstNode *decl_node;
- TypeUnionField *fields;
- ScopeDecls *decls_scope;
- HashMap fields_by_name;
- ZigType *tag_type; // always an enum or null
- LLVMTypeRef union_llvm_type;
- TypeUnionField *most_aligned_union_member;
- size_t gen_union_index;
- size_t gen_tag_index;
- size_t union_abi_size;
-
- uint32_t src_field_count;
- uint32_t gen_field_count;
-
- ContainerLayout layout;
- ResolveStatus resolve_status;
-
- bool have_explicit_tag_type;
- // whether any of the fields require comptime
- // the value is not valid until zero_bits_known == true
- bool requires_comptime;
- bool resolve_loop_flag_zero_bits;
- bool resolve_loop_flag_other;
-};
-
-struct FnGenParamInfo {
- size_t src_index;
- size_t gen_index;
- bool is_byval;
- ZigType *type;
-};
-
-struct ZigTypeFn {
- FnTypeId fn_type_id;
- bool is_generic;
- ZigType *gen_return_type;
- size_t gen_param_count;
- FnGenParamInfo *gen_param_info;
-
- LLVMTypeRef raw_type_ref;
- ZigLLVMDIType *raw_di_type;
-
- ZigType *bound_fn_parent;
-};
-
-struct ZigTypeBoundFn {
- ZigType *fn_type;
-};
-
-// Needs to have the same memory layout as ZigTypeArray
-struct ZigTypeVector {
- // The type must be a pointer, integer, bool, or float
- ZigType *elem_type;
- uint64_t len;
- size_t padding;
-};
-
-// A lot of code is relying on ZigTypeArray and ZigTypeVector having the same layout/size
-static_assert(sizeof(ZigTypeVector) == sizeof(ZigTypeArray), "Size of ZigTypeVector and ZigTypeArray do not match!");
-
-enum ZigTypeId {
- ZigTypeIdInvalid,
- ZigTypeIdMetaType,
- ZigTypeIdVoid,
- ZigTypeIdBool,
- ZigTypeIdUnreachable,
- ZigTypeIdInt,
- ZigTypeIdFloat,
- ZigTypeIdPointer,
- ZigTypeIdArray,
- ZigTypeIdStruct,
- ZigTypeIdComptimeFloat,
- ZigTypeIdComptimeInt,
- ZigTypeIdUndefined,
- ZigTypeIdNull,
- ZigTypeIdOptional,
- ZigTypeIdErrorUnion,
- ZigTypeIdErrorSet,
- ZigTypeIdEnum,
- ZigTypeIdUnion,
- ZigTypeIdFn,
- ZigTypeIdBoundFn,
- ZigTypeIdOpaque,
- ZigTypeIdFnFrame,
- ZigTypeIdAnyFrame,
- ZigTypeIdVector,
- ZigTypeIdEnumLiteral,
-};
-
-enum OnePossibleValue {
- OnePossibleValueInvalid,
- OnePossibleValueNo,
- OnePossibleValueYes,
-};
-
-struct ZigTypeOpaque {
- Buf *bare_name;
-};
-
-struct ZigTypeFnFrame {
- ZigFn *fn;
- ZigType *locals_struct;
-
- // This is set to the type that resolving the frame currently depends on, null if none.
- // It's for generating a helpful error message.
- ZigType *resolve_loop_type;
- AstNode *resolve_loop_src_node;
- bool reported_loop_err;
-};
-
-struct ZigTypeAnyFrame {
- ZigType *result_type; // null if `anyframe` instead of `anyframe->T`
-};
-
-struct ZigType {
- ZigTypeId id;
- Buf name;
-
- // These are not supposed to be accessed directly. They're
- // null during semantic analysis, memoized with get_llvm_type
- // and get_llvm_di_type
- LLVMTypeRef llvm_type;
- ZigLLVMDIType *llvm_di_type;
-
- union {
- ZigTypePointer pointer;
- ZigTypeInt integral;
- ZigTypeFloat floating;
- ZigTypeArray array;
- ZigTypeStruct structure;
- ZigTypeOptional maybe;
- ZigTypeErrorUnion error_union;
- ZigTypeErrorSet error_set;
- ZigTypeEnum enumeration;
- ZigTypeUnion unionation;
- ZigTypeFn fn;
- ZigTypeBoundFn bound_fn;
- ZigTypeVector vector;
- ZigTypeOpaque opaque;
- ZigTypeFnFrame frame;
- ZigTypeAnyFrame any_frame;
- } data;
-
- // use these fields to make sure we don't duplicate type table entries for the same type
- ZigType *pointer_parent[2]; // [0 - mut, 1 - const]
- ZigType *optional_parent;
- ZigType *any_frame_parent;
- // If we generate a constant name value for this type, we memoize it here.
- // The type of this is array
- ZigValue *cached_const_name_val;
-
- OnePossibleValue one_possible_value;
- // Known after ResolveStatusAlignmentKnown.
- uint32_t abi_align;
- // The offset in bytes between consecutive array elements of this type. Known
- // after ResolveStatusSizeKnown.
- size_t abi_size;
- // Number of bits of information in this type. Known after ResolveStatusSizeKnown.
- size_t size_in_bits;
-};
-
-enum FnAnalState {
- FnAnalStateReady,
- FnAnalStateProbing,
- FnAnalStateComplete,
- FnAnalStateInvalid,
-};
-
-struct GlobalExport {
- Buf name;
- GlobalLinkageId linkage;
-};
-
-struct ZigFn {
- LLVMValueRef llvm_value;
- const char *llvm_name;
- AstNode *proto_node;
- AstNode *body_node;
- ScopeFnDef *fndef_scope; // parent should be the top level decls or container decls
- Scope *child_scope; // parent is scope for last parameter
- ScopeBlock *def_scope; // parent is child_scope
- Buf symbol_name;
- // This is the function type assuming the function does not suspend.
- // Note that for an async function, this can be shared with non-async functions. So the value here
- // should only be read for things in common between non-async and async function types.
- ZigType *type_entry;
- // For normal functions one could use the type_entry->raw_type_ref and type_entry->raw_di_type.
- // However for functions that suspend, those values could possibly be their non-suspending equivalents.
- // So these values should be preferred.
- LLVMTypeRef raw_type_ref;
- ZigLLVMDIType *raw_di_type;
-
- ZigType *frame_type;
- // in the case of normal functions this is the implicit return type
- // 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;
- IrExecutableSrc *ir_executable;
- IrExecutableGen analyzed_executable;
- size_t prealloc_bbc;
- size_t prealloc_backward_branch_quota;
- AstNode **param_source_nodes;
- Buf **param_names;
- IrInstGen *err_code_spill;
- AstNode *assumed_non_async;
-
- AstNode *fn_no_inline_set_node;
- AstNode *fn_static_eval_set_node;
-
- ZigList alloca_gen_list;
- ZigList variable_list;
-
- Buf *section_name;
- AstNode *set_alignstack_node;
-
- AstNode *set_cold_node;
- const AstNode *inferred_async_node;
- ZigFn *inferred_async_fn;
- AstNode *non_async_node;
-
- ZigList export_list;
- ZigList call_list;
- ZigList await_list;
-
- LLVMValueRef valgrind_client_request_array;
-
- FnInline fn_inline;
- FnAnalState anal_state;
-
- uint32_t align_bytes;
- uint32_t alignstack_value;
-
- bool calls_or_awaits_errorable_fn;
- bool is_cold;
- bool is_test;
-};
-
-uint32_t fn_table_entry_hash(ZigFn*);
-bool fn_table_entry_eql(ZigFn *a, ZigFn *b);
-
-enum BuiltinFnId {
- BuiltinFnIdInvalid,
- BuiltinFnIdMemcpy,
- BuiltinFnIdMemset,
- BuiltinFnIdSizeof,
- BuiltinFnIdAlignOf,
- BuiltinFnIdField,
- BuiltinFnIdTypeInfo,
- BuiltinFnIdType,
- BuiltinFnIdHasField,
- BuiltinFnIdTypeof,
- BuiltinFnIdAddWithOverflow,
- BuiltinFnIdSubWithOverflow,
- BuiltinFnIdMulWithOverflow,
- BuiltinFnIdShlWithOverflow,
- BuiltinFnIdMulAdd,
- BuiltinFnIdCInclude,
- BuiltinFnIdCDefine,
- BuiltinFnIdCUndef,
- BuiltinFnIdCompileErr,
- BuiltinFnIdCompileLog,
- BuiltinFnIdCtz,
- BuiltinFnIdClz,
- BuiltinFnIdPopCount,
- BuiltinFnIdBswap,
- BuiltinFnIdBitReverse,
- BuiltinFnIdImport,
- BuiltinFnIdCImport,
- BuiltinFnIdErrName,
- BuiltinFnIdBreakpoint,
- BuiltinFnIdReturnAddress,
- BuiltinFnIdEmbedFile,
- BuiltinFnIdCmpxchgWeak,
- BuiltinFnIdCmpxchgStrong,
- BuiltinFnIdFence,
- BuiltinFnIdDivExact,
- BuiltinFnIdDivTrunc,
- BuiltinFnIdDivFloor,
- BuiltinFnIdRem,
- BuiltinFnIdMod,
- BuiltinFnIdSqrt,
- BuiltinFnIdSin,
- BuiltinFnIdCos,
- BuiltinFnIdExp,
- BuiltinFnIdExp2,
- BuiltinFnIdLog,
- BuiltinFnIdLog2,
- BuiltinFnIdLog10,
- BuiltinFnIdFabs,
- BuiltinFnIdFloor,
- BuiltinFnIdCeil,
- BuiltinFnIdTrunc,
- BuiltinFnIdNearbyInt,
- BuiltinFnIdRound,
- BuiltinFnIdTruncate,
- BuiltinFnIdIntCast,
- BuiltinFnIdFloatCast,
- BuiltinFnIdErrSetCast,
- BuiltinFnIdIntToFloat,
- BuiltinFnIdFloatToInt,
- BuiltinFnIdBoolToInt,
- BuiltinFnIdErrToInt,
- BuiltinFnIdIntToErr,
- BuiltinFnIdEnumToInt,
- BuiltinFnIdIntToEnum,
- BuiltinFnIdVectorType,
- BuiltinFnIdShuffle,
- BuiltinFnIdSplat,
- BuiltinFnIdSetCold,
- BuiltinFnIdSetRuntimeSafety,
- BuiltinFnIdSetFloatMode,
- BuiltinFnIdTypeName,
- BuiltinFnIdPanic,
- BuiltinFnIdPtrCast,
- BuiltinFnIdBitCast,
- BuiltinFnIdIntToPtr,
- BuiltinFnIdPtrToInt,
- BuiltinFnIdTagName,
- BuiltinFnIdTagType,
- BuiltinFnIdFieldParentPtr,
- BuiltinFnIdByteOffsetOf,
- BuiltinFnIdBitOffsetOf,
- BuiltinFnIdAsyncCall,
- BuiltinFnIdShlExact,
- BuiltinFnIdShrExact,
- BuiltinFnIdSetEvalBranchQuota,
- BuiltinFnIdAlignCast,
- BuiltinFnIdThis,
- BuiltinFnIdSetAlignStack,
- BuiltinFnIdExport,
- BuiltinFnIdErrorReturnTrace,
- BuiltinFnIdAtomicRmw,
- BuiltinFnIdAtomicLoad,
- BuiltinFnIdAtomicStore,
- BuiltinFnIdHasDecl,
- BuiltinFnIdUnionInit,
- BuiltinFnIdFrameAddress,
- BuiltinFnIdFrameType,
- BuiltinFnIdFrameHandle,
- BuiltinFnIdFrameSize,
- BuiltinFnIdAs,
- BuiltinFnIdCall,
- BuiltinFnIdBitSizeof,
- BuiltinFnIdWasmMemorySize,
- BuiltinFnIdWasmMemoryGrow,
- BuiltinFnIdSrc,
-};
-
-struct BuiltinFnEntry {
- BuiltinFnId id;
- Buf name;
- size_t param_count;
-};
-
-enum PanicMsgId {
- PanicMsgIdUnreachable,
- PanicMsgIdBoundsCheckFailure,
- PanicMsgIdCastNegativeToUnsigned,
- PanicMsgIdCastTruncatedData,
- PanicMsgIdIntegerOverflow,
- PanicMsgIdShlOverflowedBits,
- PanicMsgIdShrOverflowedBits,
- PanicMsgIdDivisionByZero,
- PanicMsgIdRemainderDivisionByZero,
- PanicMsgIdExactDivisionRemainder,
- PanicMsgIdUnwrapOptionalFail,
- PanicMsgIdInvalidErrorCode,
- PanicMsgIdIncorrectAlignment,
- PanicMsgIdBadUnionField,
- PanicMsgIdBadEnumValue,
- PanicMsgIdFloatToInt,
- PanicMsgIdPtrCastNull,
- PanicMsgIdBadResume,
- PanicMsgIdBadAwait,
- PanicMsgIdBadReturn,
- PanicMsgIdResumedAnAwaitingFn,
- PanicMsgIdFrameTooSmall,
- PanicMsgIdResumedFnPendingAwait,
- PanicMsgIdBadNoSuspendCall,
- PanicMsgIdResumeNotSuspendedFn,
- PanicMsgIdBadSentinel,
- PanicMsgIdShxTooBigRhs,
-
- PanicMsgIdCount,
-};
-
-uint32_t fn_eval_hash(Scope*);
-bool fn_eval_eql(Scope *a, Scope *b);
-
-struct TypeId {
- ZigTypeId id;
-
- union {
- struct {
- CodeGen *codegen;
- ZigType *child_type;
- InferredStructField *inferred_struct_field;
- ZigValue *sentinel;
- PtrLen ptr_len;
- uint32_t alignment;
-
- uint32_t bit_offset_in_host;
- uint32_t host_int_bytes;
-
- uint32_t vector_index;
- bool is_const;
- bool is_volatile;
- bool allow_zero;
- } pointer;
- struct {
- CodeGen *codegen;
- ZigType *child_type;
- uint64_t size;
- ZigValue *sentinel;
- } array;
- struct {
- bool is_signed;
- uint32_t bit_count;
- } integer;
- struct {
- ZigType *err_set_type;
- ZigType *payload_type;
- } error_union;
- struct {
- ZigType *elem_type;
- uint32_t len;
- } vector;
- } data;
-};
-
-uint32_t type_id_hash(TypeId);
-bool type_id_eql(TypeId a, TypeId b);
-
-enum ZigLLVMFnId {
- ZigLLVMFnIdCtz,
- ZigLLVMFnIdClz,
- ZigLLVMFnIdPopCount,
- ZigLLVMFnIdOverflowArithmetic,
- ZigLLVMFnIdFMA,
- ZigLLVMFnIdFloatOp,
- ZigLLVMFnIdBswap,
- ZigLLVMFnIdBitReverse,
-};
-
-// There are a bunch of places in code that rely on these values being in
-// exactly this order.
-enum AddSubMul {
- AddSubMulAdd = 0,
- AddSubMulSub = 1,
- AddSubMulMul = 2,
-};
-
-struct ZigLLVMFnKey {
- ZigLLVMFnId id;
-
- union {
- struct {
- uint32_t bit_count;
- } ctz;
- struct {
- uint32_t bit_count;
- } clz;
- struct {
- uint32_t bit_count;
- } pop_count;
- struct {
- BuiltinFnId op;
- uint32_t bit_count;
- uint32_t vector_len; // 0 means not a vector
- } floating;
- struct {
- AddSubMul add_sub_mul;
- uint32_t bit_count;
- uint32_t vector_len; // 0 means not a vector
- bool is_signed;
- } overflow_arithmetic;
- struct {
- uint32_t bit_count;
- uint32_t vector_len; // 0 means not a vector
- } bswap;
- struct {
- uint32_t bit_count;
- } bit_reverse;
- } data;
-};
-
-uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey);
-bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b);
-
-struct TimeEvent {
- double time;
- const char *name;
-};
-
-struct CFile {
- ZigList args;
- const char *source_path;
- const char *preprocessor_only_basename;
-};
-
-struct CodeGen {
- // Other code depends on this being first.
- ZigStage1 stage1;
-
- // arena allocator destroyed just prior to codegen emit
- heap::ArenaAllocator *pass1_arena;
-
- //////////////////////////// Runtime State
- LLVMModuleRef module;
- ZigList errors;
- ErrorMsg *trace_err;
- LLVMBuilderRef builder;
- ZigLLVMDIBuilder *dbuilder;
- ZigLLVMDICompileUnit *compile_unit;
- ZigLLVMDIFile *compile_unit_file;
- LLVMTargetDataRef target_data_ref;
- LLVMTargetMachineRef target_machine;
- ZigLLVMDIFile *dummy_di_file;
- LLVMValueRef cur_ret_ptr;
- LLVMValueRef cur_frame_ptr;
- LLVMValueRef cur_fn_val;
- LLVMValueRef cur_async_switch_instr;
- LLVMValueRef cur_async_resume_index_ptr;
- LLVMValueRef cur_async_awaiter_ptr;
- LLVMBasicBlockRef cur_preamble_llvm_block;
- size_t cur_resume_block_count;
- LLVMValueRef cur_err_ret_trace_val_arg;
- LLVMValueRef cur_err_ret_trace_val_stack;
- LLVMValueRef cur_bad_not_suspended_index;
- LLVMValueRef memcpy_fn_val;
- LLVMValueRef memset_fn_val;
- LLVMValueRef trap_fn_val;
- LLVMValueRef return_address_fn_val;
- LLVMValueRef frame_address_fn_val;
- LLVMValueRef add_error_return_trace_addr_fn_val;
- LLVMValueRef stacksave_fn_val;
- LLVMValueRef stackrestore_fn_val;
- LLVMValueRef write_register_fn_val;
- LLVMValueRef merge_err_ret_traces_fn_val;
- LLVMValueRef sp_md_node;
- LLVMValueRef err_name_table;
- LLVMValueRef safety_crash_err_fn;
- LLVMValueRef return_err_fn;
- LLVMValueRef wasm_memory_size;
- LLVMValueRef wasm_memory_grow;
- LLVMTypeRef anyframe_fn_type;
-
- // reminder: hash tables must be initialized before use
- HashMap import_table;
- HashMap builtin_fn_table;
- HashMap primitive_type_table;
- HashMap type_table;
- HashMap fn_type_table;
- HashMap error_table;
- HashMap generic_table;
- HashMap memoized_fn_eval_table;
- HashMap llvm_fn_table;
- HashMap exported_symbol_names;
- HashMap external_symbol_names;
- HashMap string_literals_table;
- HashMap type_info_cache;
- HashMap one_possible_values;
-
- ZigList resolve_queue;
- size_t resolve_queue_index;
- ZigList timing_events;
- ZigList inline_fns;
- ZigList test_fns;
- ZigList errors_by_index;
- size_t largest_err_name_len;
- ZigList type_resolve_stack;
-
- ZigPackage *std_package;
- ZigPackage *test_runner_package;
- ZigPackage *compile_var_package;
- ZigPackage *root_pkg; // @import("root")
- ZigPackage *main_pkg; // usually same as root_pkg, except for `zig test`
- ZigType *compile_var_import;
- ZigType *root_import;
- ZigType *start_import;
-
- struct {
- ZigType *entry_bool;
- ZigType *entry_c_int[CIntTypeCount];
- ZigType *entry_c_longdouble;
- ZigType *entry_c_void;
- ZigType *entry_u8;
- ZigType *entry_u16;
- ZigType *entry_u32;
- ZigType *entry_u29;
- ZigType *entry_u64;
- ZigType *entry_i8;
- ZigType *entry_i32;
- ZigType *entry_i64;
- ZigType *entry_isize;
- ZigType *entry_usize;
- ZigType *entry_f16;
- ZigType *entry_f32;
- ZigType *entry_f64;
- ZigType *entry_f128;
- ZigType *entry_void;
- ZigType *entry_unreachable;
- ZigType *entry_type;
- ZigType *entry_invalid;
- ZigType *entry_block;
- ZigType *entry_num_lit_int;
- ZigType *entry_num_lit_float;
- ZigType *entry_undef;
- ZigType *entry_null;
- ZigType *entry_anytype;
- ZigType *entry_global_error_set;
- ZigType *entry_enum_literal;
- ZigType *entry_any_frame;
- } builtin_types;
-
- struct Intern {
- ZigValue x_undefined;
- ZigValue x_void;
- ZigValue x_null;
- ZigValue x_unreachable;
- ZigValue zero_byte;
-
- ZigValue *for_undefined();
- ZigValue *for_void();
- ZigValue *for_null();
- ZigValue *for_unreachable();
- ZigValue *for_zero_byte();
- } intern;
-
- ZigType *align_amt_type;
- ZigType *stack_trace_type;
- ZigType *err_tag_type;
- ZigType *test_fn_type;
-
- Buf llvm_triple_str;
- Buf global_asm;
- Buf o_file_output_path;
- Buf asm_file_output_path;
- Buf llvm_ir_file_output_path;
- Buf *cache_dir;
- // As an input parameter, mutually exclusive with enable_cache. But it gets
- // populated in codegen_build_and_link.
- Buf *output_dir;
- Buf *c_artifact_dir;
- const char **libc_include_dir_list;
- size_t libc_include_dir_len;
-
- Buf *builtin_zig_path;
- Buf *zig_std_special_dir; // Cannot be overridden; derived from zig_lib_dir.
-
- IrInstSrc *invalid_inst_src;
- IrInstGen *invalid_inst_gen;
- IrInstGen *unreach_instruction;
-
- ZigValue panic_msg_vals[PanicMsgIdCount];
-
- // The function definitions this module includes.
- ZigList fn_defs;
- size_t fn_defs_index;
- ZigList global_vars;
-
- ZigFn *cur_fn;
- ZigFn *panic_fn;
-
- ZigFn *largest_frame_fn;
-
- Stage2ProgressNode *main_progress_node;
- Stage2ProgressNode *sub_progress_node;
-
- ErrColor err_color;
- uint32_t next_unresolved_index;
- unsigned pointer_size_bytes;
- bool is_big_endian;
- bool have_c_main;
- bool have_winmain;
- bool have_wwinmain;
- bool have_winmain_crt_startup;
- bool have_wwinmain_crt_startup;
- bool have_dllmain_crt_startup;
- bool have_err_ret_tracing;
- bool verbose_tokenize;
- bool verbose_ast;
- bool verbose_ir;
- bool verbose_llvm_ir;
- bool verbose_cimport;
- bool verbose_llvm_cpu_features;
- bool error_during_imports;
- bool generate_error_name_table;
- bool enable_time_report;
- bool enable_stack_report;
- bool reported_bad_link_libc_error;
- bool need_frame_size_prefix_data;
- bool link_libc;
- bool link_libcpp;
-
- BuildMode build_mode;
- const ZigTarget *zig_target;
- TargetSubsystem subsystem; // careful using this directly; see detect_subsystem
- CodeModel code_model;
- bool strip_debug_symbols;
- bool is_test_build;
- bool is_single_threaded;
- bool have_pic;
- bool link_mode_dynamic;
- bool dll_export_fns;
- bool have_stack_probing;
- bool function_sections;
- bool enable_dump_analysis;
- bool enable_doc_generation;
- bool emit_bin;
- bool emit_asm;
- bool emit_llvm_ir;
- bool test_is_evented;
- bool valgrind_enabled;
-
- Buf *root_out_name;
- Buf *test_filter;
- Buf *test_name_prefix;
- Buf *zig_lib_dir;
- Buf *zig_std_dir;
-};
-
-struct ZigVar {
- const char *name;
- ZigValue *const_value;
- ZigType *var_type;
- LLVMValueRef value_ref;
- IrInstSrc *is_comptime;
- IrInstGen *ptr_instruction;
- // which node is the declaration of the variable
- AstNode *decl_node;
- ZigLLVMDILocalVariable *di_loc_var;
- size_t src_arg_index;
- Scope *parent_scope;
- Scope *child_scope;
- LLVMValueRef param_value_ref;
-
- Buf *section_name;
-
- // In an inline loop, multiple variables may be created,
- // In this case, a reference to a variable should follow
- // this pointer to the redefined variable.
- ZigVar *next_var;
-
- ZigList export_list;
-
- uint32_t align_bytes;
- uint32_t ref_count;
-
- bool shadowable;
- bool src_is_const;
- bool gen_is_const;
- bool is_thread_local;
- bool is_comptime_memoized;
- bool is_comptime_memoized_value;
- bool did_the_decl_codegen;
-};
-
-struct ErrorTableEntry {
- Buf name;
- uint32_t value;
- AstNode *decl_node;
- ErrorTableEntry *other; // null, or another error decl that was merged into this
- ZigType *set_with_only_this_in_it;
- // If we generate a constant error name value for this error, we memoize it here.
- // The type of this is array
- ZigValue *cached_error_name_val;
-};
-
-enum ScopeId {
- ScopeIdDecls,
- ScopeIdBlock,
- ScopeIdDefer,
- ScopeIdDeferExpr,
- ScopeIdVarDecl,
- ScopeIdCImport,
- ScopeIdLoop,
- ScopeIdSuspend,
- ScopeIdFnDef,
- ScopeIdCompTime,
- ScopeIdRuntime,
- ScopeIdTypeOf,
- ScopeIdExpr,
- ScopeIdNoSuspend,
-};
-
-struct Scope {
- CodeGen *codegen;
- AstNode *source_node;
-
- // if the scope has a parent, this is it
- Scope *parent;
-
- ZigLLVMDIScope *di_scope;
- ScopeId id;
-};
-
-// This scope comes from global declarations or from
-// declarations in a container declaration
-// NodeTypeContainerDecl
-struct ScopeDecls {
- Scope base;
-
- HashMap decl_table;
- ZigList use_decls;
- AstNode *safety_set_node;
- AstNode *fast_math_set_node;
- ZigType *import;
- // If this is a scope from a container, this is the type entry, otherwise null
- ZigType *container_type;
- Buf *bare_name;
-
- bool safety_off;
- bool fast_math_on;
- bool any_imports_failed;
-};
-
-enum LVal {
- LValNone,
- LValPtr,
- LValAssign,
-};
-
-// This scope comes from a block expression in user code.
-// NodeTypeBlock
-struct ScopeBlock {
- Scope base;
-
- Buf *name;
- IrBasicBlockSrc *end_block;
- IrInstSrc *is_comptime;
- ResultLocPeerParent *peer_parent;
- ZigList *incoming_values;
- ZigList *incoming_blocks;
-
- AstNode *safety_set_node;
- AstNode *fast_math_set_node;
-
- LVal lval;
- bool safety_off;
- bool fast_math_on;
- bool name_used;
-};
-
-// This scope is created from every defer expression.
-// It's the code following the defer statement.
-// NodeTypeDefer
-struct ScopeDefer {
- Scope base;
-};
-
-// This scope is created from every defer expression.
-// It's the parent of the defer expression itself.
-// NodeTypeDefer
-struct ScopeDeferExpr {
- Scope base;
-
- bool reported_err;
-};
-
-// This scope is created for every variable declaration inside an IrExecutable
-// NodeTypeVariableDeclaration, NodeTypeParamDecl
-struct ScopeVarDecl {
- Scope base;
-
- // The variable that creates this scope
- ZigVar *var;
-};
-
-// This scope is created for a @cImport
-// NodeTypeFnCallExpr
-struct ScopeCImport {
- Scope base;
-
- Buf buf;
-};
-
-// This scope is created for a loop such as for or while in order to
-// make break and continue statements work.
-// NodeTypeForExpr or NodeTypeWhileExpr
-struct ScopeLoop {
- Scope base;
-
- LVal lval;
- Buf *name;
- IrBasicBlockSrc *break_block;
- IrBasicBlockSrc *continue_block;
- IrInstSrc *is_comptime;
- ZigList *incoming_values;
- ZigList *incoming_blocks;
- ResultLocPeerParent *peer_parent;
- ScopeExpr *spill_scope;
-
- bool name_used;
-};
-
-// This scope blocks certain things from working such as comptime continue
-// inside a runtime if expression.
-// NodeTypeIfBoolExpr, NodeTypeWhileExpr, NodeTypeForExpr
-struct ScopeRuntime {
- Scope base;
-
- IrInstSrc *is_comptime;
-};
-
-// This scope is created for a suspend block in order to have labeled
-// suspend for breaking out of a suspend and for detecting if a suspend
-// block is inside a suspend block.
-struct ScopeSuspend {
- Scope base;
-
- bool reported_err;
-};
-
-// This scope is created for a comptime expression.
-// NodeTypeCompTime, NodeTypeSwitchExpr
-struct ScopeCompTime {
- Scope base;
-};
-
-// This scope is created for a nosuspend expression.
-// NodeTypeNoSuspend
-struct ScopeNoSuspend {
- Scope base;
-};
-
-// This scope is created for a function definition.
-// NodeTypeFnDef
-struct ScopeFnDef {
- Scope base;
-
- ZigFn *fn_entry;
-};
-
-// This scope is created for a @TypeOf.
-// All runtime side-effects are elided within it.
-// NodeTypeFnCallExpr
-struct ScopeTypeOf {
- Scope base;
-};
-
-enum MemoizedBool {
- MemoizedBoolUnknown,
- MemoizedBoolFalse,
- MemoizedBoolTrue,
-};
-
-// This scope is created for each expression.
-// It's used to identify when an instruction needs to be spilled,
-// so that it can be accessed after a suspend point.
-struct ScopeExpr {
- Scope base;
-
- ScopeExpr **children_ptr;
- size_t children_len;
-
- MemoizedBool need_spill;
- // This is a hack. I apologize for this, I need this to work so that I
- // can make progress on other fronts. I'll pay off this tech debt eventually.
- bool spill_harder;
-};
-
-// synchronized with code in define_builtin_compile_vars
-enum AtomicOrder {
- AtomicOrderUnordered,
- AtomicOrderMonotonic,
- AtomicOrderAcquire,
- AtomicOrderRelease,
- AtomicOrderAcqRel,
- AtomicOrderSeqCst,
-};
-
-// synchronized with the code in define_builtin_compile_vars
-enum AtomicRmwOp {
- AtomicRmwOp_xchg,
- AtomicRmwOp_add,
- AtomicRmwOp_sub,
- AtomicRmwOp_and,
- AtomicRmwOp_nand,
- AtomicRmwOp_or,
- AtomicRmwOp_xor,
- AtomicRmwOp_max,
- AtomicRmwOp_min,
-};
-
-// A basic block contains no branching. Branches send control flow
-// to another basic block.
-// Phi instructions must be first in a basic block.
-// The last instruction in a basic block must be of type unreachable.
-struct IrBasicBlockSrc {
- ZigList instruction_list;
- IrBasicBlockGen *child;
- Scope *scope;
- const char *name_hint;
- IrInst *suspend_instruction_ref;
-
- uint32_t ref_count;
- uint32_t index; // index into the basic block list
-
- uint32_t debug_id;
- bool suspended;
- bool in_resume_stack;
-};
-
-struct IrBasicBlockGen {
- ZigList instruction_list;
- Scope *scope;
- const char *name_hint;
- LLVMBasicBlockRef llvm_block;
- LLVMBasicBlockRef llvm_exit_block;
- // The instruction that referenced this basic block and caused us to
- // analyze the basic block. If the same instruction wants us to emit
- // the same basic block, then we re-generate it instead of saving it.
- IrInst *ref_instruction;
- // When this is non-null, a branch to this basic block is only allowed
- // if the branch is comptime. The instruction points to the reason
- // the basic block must be comptime.
- IrInst *must_be_comptime_source_instr;
-
- uint32_t debug_id;
- bool already_appended;
-};
-
-// Src instructions are generated by ir_gen_* functions in ir.cpp from AST.
-// ir_analyze_* functions consume Src instructions and produce Gen instructions.
-// Src instructions do not have type information; Gen instructions do.
-enum IrInstSrcId {
- IrInstSrcIdInvalid,
- IrInstSrcIdDeclVar,
- IrInstSrcIdBr,
- IrInstSrcIdCondBr,
- IrInstSrcIdSwitchBr,
- IrInstSrcIdSwitchVar,
- IrInstSrcIdSwitchElseVar,
- IrInstSrcIdSwitchTarget,
- IrInstSrcIdPhi,
- IrInstSrcIdUnOp,
- IrInstSrcIdBinOp,
- IrInstSrcIdMergeErrSets,
- IrInstSrcIdLoadPtr,
- IrInstSrcIdStorePtr,
- IrInstSrcIdFieldPtr,
- IrInstSrcIdElemPtr,
- IrInstSrcIdVarPtr,
- IrInstSrcIdCall,
- IrInstSrcIdCallArgs,
- IrInstSrcIdCallExtra,
- IrInstSrcIdAsyncCallExtra,
- IrInstSrcIdConst,
- IrInstSrcIdReturn,
- IrInstSrcIdContainerInitList,
- IrInstSrcIdContainerInitFields,
- IrInstSrcIdUnreachable,
- IrInstSrcIdTypeOf,
- IrInstSrcIdSetCold,
- IrInstSrcIdSetRuntimeSafety,
- IrInstSrcIdSetFloatMode,
- IrInstSrcIdArrayType,
- IrInstSrcIdAnyFrameType,
- IrInstSrcIdSliceType,
- IrInstSrcIdAsm,
- IrInstSrcIdSizeOf,
- IrInstSrcIdTestNonNull,
- IrInstSrcIdOptionalUnwrapPtr,
- IrInstSrcIdClz,
- IrInstSrcIdCtz,
- IrInstSrcIdPopCount,
- IrInstSrcIdBswap,
- IrInstSrcIdBitReverse,
- IrInstSrcIdImport,
- IrInstSrcIdCImport,
- IrInstSrcIdCInclude,
- IrInstSrcIdCDefine,
- IrInstSrcIdCUndef,
- IrInstSrcIdRef,
- IrInstSrcIdCompileErr,
- IrInstSrcIdCompileLog,
- IrInstSrcIdErrName,
- IrInstSrcIdEmbedFile,
- IrInstSrcIdCmpxchg,
- IrInstSrcIdFence,
- IrInstSrcIdTruncate,
- IrInstSrcIdIntCast,
- IrInstSrcIdFloatCast,
- IrInstSrcIdIntToFloat,
- IrInstSrcIdFloatToInt,
- IrInstSrcIdBoolToInt,
- IrInstSrcIdVectorType,
- IrInstSrcIdShuffleVector,
- IrInstSrcIdSplat,
- IrInstSrcIdBoolNot,
- IrInstSrcIdMemset,
- IrInstSrcIdMemcpy,
- IrInstSrcIdSlice,
- IrInstSrcIdBreakpoint,
- IrInstSrcIdReturnAddress,
- IrInstSrcIdFrameAddress,
- IrInstSrcIdFrameHandle,
- IrInstSrcIdFrameType,
- IrInstSrcIdFrameSize,
- IrInstSrcIdAlignOf,
- IrInstSrcIdOverflowOp,
- IrInstSrcIdTestErr,
- IrInstSrcIdMulAdd,
- IrInstSrcIdFloatOp,
- IrInstSrcIdUnwrapErrCode,
- IrInstSrcIdUnwrapErrPayload,
- IrInstSrcIdFnProto,
- IrInstSrcIdTestComptime,
- IrInstSrcIdPtrCast,
- IrInstSrcIdBitCast,
- IrInstSrcIdIntToPtr,
- IrInstSrcIdPtrToInt,
- IrInstSrcIdIntToEnum,
- IrInstSrcIdEnumToInt,
- IrInstSrcIdIntToErr,
- IrInstSrcIdErrToInt,
- IrInstSrcIdCheckSwitchProngs,
- IrInstSrcIdCheckStatementIsVoid,
- IrInstSrcIdTypeName,
- IrInstSrcIdDeclRef,
- IrInstSrcIdPanic,
- IrInstSrcIdTagName,
- IrInstSrcIdTagType,
- IrInstSrcIdFieldParentPtr,
- IrInstSrcIdByteOffsetOf,
- IrInstSrcIdBitOffsetOf,
- IrInstSrcIdTypeInfo,
- IrInstSrcIdType,
- IrInstSrcIdHasField,
- IrInstSrcIdSetEvalBranchQuota,
- IrInstSrcIdPtrType,
- IrInstSrcIdAlignCast,
- IrInstSrcIdImplicitCast,
- IrInstSrcIdResolveResult,
- IrInstSrcIdResetResult,
- IrInstSrcIdSetAlignStack,
- IrInstSrcIdArgType,
- IrInstSrcIdExport,
- IrInstSrcIdErrorReturnTrace,
- IrInstSrcIdErrorUnion,
- IrInstSrcIdAtomicRmw,
- IrInstSrcIdAtomicLoad,
- IrInstSrcIdAtomicStore,
- IrInstSrcIdSaveErrRetAddr,
- IrInstSrcIdAddImplicitReturnType,
- IrInstSrcIdErrSetCast,
- IrInstSrcIdCheckRuntimeScope,
- IrInstSrcIdHasDecl,
- IrInstSrcIdUndeclaredIdent,
- IrInstSrcIdAlloca,
- IrInstSrcIdEndExpr,
- IrInstSrcIdUnionInitNamedField,
- IrInstSrcIdSuspendBegin,
- IrInstSrcIdSuspendFinish,
- IrInstSrcIdAwait,
- IrInstSrcIdResume,
- IrInstSrcIdSpillBegin,
- IrInstSrcIdSpillEnd,
- IrInstSrcIdWasmMemorySize,
- IrInstSrcIdWasmMemoryGrow,
- IrInstSrcIdSrc,
-};
-
-// ir_render_* functions in codegen.cpp consume Gen instructions and produce LLVM IR.
-// Src instructions do not have type information; Gen instructions do.
-enum IrInstGenId {
- IrInstGenIdInvalid,
- IrInstGenIdDeclVar,
- IrInstGenIdBr,
- IrInstGenIdCondBr,
- IrInstGenIdSwitchBr,
- IrInstGenIdPhi,
- IrInstGenIdBinaryNot,
- IrInstGenIdNegation,
- IrInstGenIdNegationWrapping,
- IrInstGenIdBinOp,
- IrInstGenIdLoadPtr,
- IrInstGenIdStorePtr,
- IrInstGenIdVectorStoreElem,
- IrInstGenIdStructFieldPtr,
- IrInstGenIdUnionFieldPtr,
- IrInstGenIdElemPtr,
- IrInstGenIdVarPtr,
- IrInstGenIdReturnPtr,
- IrInstGenIdCall,
- IrInstGenIdReturn,
- IrInstGenIdCast,
- IrInstGenIdUnreachable,
- IrInstGenIdAsm,
- IrInstGenIdTestNonNull,
- IrInstGenIdOptionalUnwrapPtr,
- IrInstGenIdOptionalWrap,
- IrInstGenIdUnionTag,
- IrInstGenIdClz,
- IrInstGenIdCtz,
- IrInstGenIdPopCount,
- IrInstGenIdBswap,
- IrInstGenIdBitReverse,
- IrInstGenIdRef,
- IrInstGenIdErrName,
- IrInstGenIdCmpxchg,
- IrInstGenIdFence,
- IrInstGenIdTruncate,
- IrInstGenIdShuffleVector,
- IrInstGenIdSplat,
- IrInstGenIdBoolNot,
- IrInstGenIdMemset,
- IrInstGenIdMemcpy,
- IrInstGenIdSlice,
- IrInstGenIdBreakpoint,
- IrInstGenIdReturnAddress,
- IrInstGenIdFrameAddress,
- IrInstGenIdFrameHandle,
- IrInstGenIdFrameSize,
- IrInstGenIdOverflowOp,
- IrInstGenIdTestErr,
- IrInstGenIdMulAdd,
- IrInstGenIdFloatOp,
- IrInstGenIdUnwrapErrCode,
- IrInstGenIdUnwrapErrPayload,
- IrInstGenIdErrWrapCode,
- IrInstGenIdErrWrapPayload,
- IrInstGenIdPtrCast,
- IrInstGenIdBitCast,
- IrInstGenIdWidenOrShorten,
- IrInstGenIdIntToPtr,
- IrInstGenIdPtrToInt,
- IrInstGenIdIntToEnum,
- IrInstGenIdIntToErr,
- IrInstGenIdErrToInt,
- IrInstGenIdPanic,
- IrInstGenIdTagName,
- IrInstGenIdFieldParentPtr,
- IrInstGenIdAlignCast,
- IrInstGenIdErrorReturnTrace,
- IrInstGenIdAtomicRmw,
- IrInstGenIdAtomicLoad,
- IrInstGenIdAtomicStore,
- IrInstGenIdSaveErrRetAddr,
- IrInstGenIdVectorToArray,
- IrInstGenIdArrayToVector,
- IrInstGenIdAssertZero,
- IrInstGenIdAssertNonNull,
- IrInstGenIdPtrOfArrayToSlice,
- IrInstGenIdSuspendBegin,
- IrInstGenIdSuspendFinish,
- IrInstGenIdAwait,
- IrInstGenIdResume,
- IrInstGenIdSpillBegin,
- IrInstGenIdSpillEnd,
- IrInstGenIdVectorExtractElem,
- IrInstGenIdAlloca,
- IrInstGenIdConst,
- IrInstGenIdWasmMemorySize,
- IrInstGenIdWasmMemoryGrow,
-};
-
-// Common fields between IrInstSrc and IrInstGen. This allows future passes
-// after pass2 to be added to zig.
-struct IrInst {
- // if ref_count is zero and the instruction has no side effects,
- // the instruction can be omitted in codegen
- uint32_t ref_count;
- uint32_t debug_id;
-
- Scope *scope;
- AstNode *source_node;
-
- // for debugging purposes, these are useful to call to inspect the instruction
- void dump();
- void src();
-};
-
-struct IrInstSrc {
- IrInst base;
-
- IrInstSrcId id;
- // true if this instruction was generated by zig and not from user code
- // this matters for the "unreachable code" compile error
- bool is_gen;
- bool is_noreturn;
-
- // When analyzing IR, instructions that point to this instruction in the "old ir"
- // can find the instruction that corresponds to this value in the "new ir"
- // with this child field.
- IrInstGen *child;
- IrBasicBlockSrc *owner_bb;
-
- // for debugging purposes, these are useful to call to inspect the instruction
- void dump();
- void src();
-};
-
-struct IrInstGen {
- IrInst base;
-
- IrInstGenId id;
-
- LLVMValueRef llvm_value;
- ZigValue *value;
- IrBasicBlockGen *owner_bb;
- // Nearly any instruction can have to be stored as a local variable before suspending
- // and then loaded after resuming, in case there is an expression with a suspend point
- // in it, such as: x + await y
- IrInstGen *spill;
-
- // for debugging purposes, these are useful to call to inspect the instruction
- void dump();
- void src();
-};
-
-struct IrInstSrcDeclVar {
- IrInstSrc base;
-
- ZigVar *var;
- IrInstSrc *var_type;
- IrInstSrc *align_value;
- IrInstSrc *ptr;
-};
-
-struct IrInstGenDeclVar {
- IrInstGen base;
-
- ZigVar *var;
- IrInstGen *var_ptr;
-};
-
-struct IrInstSrcCondBr {
- IrInstSrc base;
-
- IrInstSrc *condition;
- IrBasicBlockSrc *then_block;
- IrBasicBlockSrc *else_block;
- IrInstSrc *is_comptime;
- ResultLoc *result_loc;
-};
-
-struct IrInstGenCondBr {
- IrInstGen base;
-
- IrInstGen *condition;
- IrBasicBlockGen *then_block;
- IrBasicBlockGen *else_block;
-};
-
-struct IrInstSrcBr {
- IrInstSrc base;
-
- IrBasicBlockSrc *dest_block;
- IrInstSrc *is_comptime;
-};
-
-struct IrInstGenBr {
- IrInstGen base;
-
- IrBasicBlockGen *dest_block;
-};
-
-struct IrInstSrcSwitchBrCase {
- IrInstSrc *value;
- IrBasicBlockSrc *block;
-};
-
-struct IrInstSrcSwitchBr {
- IrInstSrc base;
-
- IrInstSrc *target_value;
- IrBasicBlockSrc *else_block;
- size_t case_count;
- IrInstSrcSwitchBrCase *cases;
- IrInstSrc *is_comptime;
- IrInstSrc *switch_prongs_void;
-};
-
-struct IrInstGenSwitchBrCase {
- IrInstGen *value;
- IrBasicBlockGen *block;
-};
-
-struct IrInstGenSwitchBr {
- IrInstGen base;
-
- IrInstGen *target_value;
- IrBasicBlockGen *else_block;
- size_t case_count;
- IrInstGenSwitchBrCase *cases;
-};
-
-struct IrInstSrcSwitchVar {
- IrInstSrc base;
-
- IrInstSrc *target_value_ptr;
- IrInstSrc **prongs_ptr;
- size_t prongs_len;
-};
-
-struct IrInstSrcSwitchElseVar {
- IrInstSrc base;
-
- IrInstSrc *target_value_ptr;
- IrInstSrcSwitchBr *switch_br;
-};
-
-struct IrInstSrcSwitchTarget {
- IrInstSrc base;
-
- IrInstSrc *target_value_ptr;
-};
-
-struct IrInstSrcPhi {
- IrInstSrc base;
-
- size_t incoming_count;
- IrBasicBlockSrc **incoming_blocks;
- IrInstSrc **incoming_values;
- ResultLocPeerParent *peer_parent;
-};
-
-struct IrInstGenPhi {
- IrInstGen base;
-
- size_t incoming_count;
- IrBasicBlockGen **incoming_blocks;
- IrInstGen **incoming_values;
-};
-
-enum IrUnOp {
- IrUnOpInvalid,
- IrUnOpBinNot,
- IrUnOpNegation,
- IrUnOpNegationWrap,
- IrUnOpDereference,
- IrUnOpOptional,
-};
-
-struct IrInstSrcUnOp {
- IrInstSrc base;
-
- IrUnOp op_id;
- LVal lval;
- IrInstSrc *value;
- ResultLoc *result_loc;
-};
-
-struct IrInstGenBinaryNot {
- IrInstGen base;
- IrInstGen *operand;
-};
-
-struct IrInstGenNegation {
- IrInstGen base;
- IrInstGen *operand;
-};
-
-struct IrInstGenNegationWrapping {
- IrInstGen base;
- IrInstGen *operand;
-};
-
-enum IrBinOp {
- IrBinOpInvalid,
- IrBinOpBoolOr,
- IrBinOpBoolAnd,
- IrBinOpCmpEq,
- IrBinOpCmpNotEq,
- IrBinOpCmpLessThan,
- IrBinOpCmpGreaterThan,
- IrBinOpCmpLessOrEq,
- IrBinOpCmpGreaterOrEq,
- IrBinOpBinOr,
- IrBinOpBinXor,
- IrBinOpBinAnd,
- IrBinOpBitShiftLeftLossy,
- IrBinOpBitShiftLeftExact,
- IrBinOpBitShiftRightLossy,
- IrBinOpBitShiftRightExact,
- IrBinOpAdd,
- IrBinOpAddWrap,
- IrBinOpSub,
- IrBinOpSubWrap,
- IrBinOpMult,
- IrBinOpMultWrap,
- IrBinOpDivUnspecified,
- IrBinOpDivExact,
- IrBinOpDivTrunc,
- IrBinOpDivFloor,
- IrBinOpRemUnspecified,
- IrBinOpRemRem,
- IrBinOpRemMod,
- IrBinOpArrayCat,
- IrBinOpArrayMult,
-};
-
-struct IrInstSrcBinOp {
- IrInstSrc base;
-
- IrInstSrc *op1;
- IrInstSrc *op2;
- IrBinOp op_id;
- bool safety_check_on;
-};
-
-struct IrInstGenBinOp {
- IrInstGen base;
-
- IrInstGen *op1;
- IrInstGen *op2;
- IrBinOp op_id;
- bool safety_check_on;
-};
-
-struct IrInstSrcMergeErrSets {
- IrInstSrc base;
-
- IrInstSrc *op1;
- IrInstSrc *op2;
- Buf *type_name;
-};
-
-struct IrInstSrcLoadPtr {
- IrInstSrc base;
-
- IrInstSrc *ptr;
-};
-
-struct IrInstGenLoadPtr {
- IrInstGen base;
-
- IrInstGen *ptr;
- IrInstGen *result_loc;
-};
-
-struct IrInstSrcStorePtr {
- IrInstSrc base;
-
- IrInstSrc *ptr;
- IrInstSrc *value;
-
- bool allow_write_through_const;
-};
-
-struct IrInstGenStorePtr {
- IrInstGen base;
-
- IrInstGen *ptr;
- IrInstGen *value;
-};
-
-struct IrInstGenVectorStoreElem {
- IrInstGen base;
-
- IrInstGen *vector_ptr;
- IrInstGen *index;
- IrInstGen *value;
-};
-
-struct IrInstSrcFieldPtr {
- IrInstSrc base;
-
- IrInstSrc *container_ptr;
- Buf *field_name_buffer;
- IrInstSrc *field_name_expr;
- bool initializing;
-};
-
-struct IrInstGenStructFieldPtr {
- IrInstGen base;
-
- IrInstGen *struct_ptr;
- TypeStructField *field;
- bool is_const;
-};
-
-struct IrInstGenUnionFieldPtr {
- IrInstGen base;
-
- IrInstGen *union_ptr;
- TypeUnionField *field;
- bool safety_check_on;
- bool initializing;
-};
-
-struct IrInstSrcElemPtr {
- IrInstSrc base;
-
- IrInstSrc *array_ptr;
- IrInstSrc *elem_index;
- AstNode *init_array_type_source_node;
- PtrLen ptr_len;
- bool safety_check_on;
-};
-
-struct IrInstGenElemPtr {
- IrInstGen base;
-
- IrInstGen *array_ptr;
- IrInstGen *elem_index;
- bool safety_check_on;
-};
-
-struct IrInstSrcVarPtr {
- IrInstSrc base;
-
- ZigVar *var;
- ScopeFnDef *crossed_fndef_scope;
-};
-
-struct IrInstGenVarPtr {
- IrInstGen base;
-
- ZigVar *var;
-};
-
-// For functions that have a return type for which handle_is_ptr is true, a
-// result location pointer is the secret first parameter ("sret"). This
-// instruction returns that pointer.
-struct IrInstGenReturnPtr {
- IrInstGen base;
-};
-
-struct IrInstSrcCall {
- IrInstSrc base;
-
- IrInstSrc *fn_ref;
- ZigFn *fn_entry;
- size_t arg_count;
- IrInstSrc **args;
- IrInstSrc *ret_ptr;
- ResultLoc *result_loc;
-
- IrInstSrc *new_stack;
-
- CallModifier modifier;
- bool is_async_call_builtin;
-};
-
-// This is a pass1 instruction, used by @call when the args node is
-// a tuple or struct literal.
-struct IrInstSrcCallArgs {
- IrInstSrc base;
-
- IrInstSrc *options;
- IrInstSrc *fn_ref;
- IrInstSrc **args_ptr;
- size_t args_len;
- ResultLoc *result_loc;
-};
-
-// This is a pass1 instruction, used by @call, when the args node
-// is not a literal.
-// `args` is expected to be either a struct or a tuple.
-struct IrInstSrcCallExtra {
- IrInstSrc base;
-
- IrInstSrc *options;
- IrInstSrc *fn_ref;
- IrInstSrc *args;
- ResultLoc *result_loc;
-};
-
-// This is a pass1 instruction, used by @asyncCall, when the args node
-// is not a literal.
-// `args` is expected to be either a struct or a tuple.
-struct IrInstSrcAsyncCallExtra {
- IrInstSrc base;
-
- CallModifier modifier;
- IrInstSrc *fn_ref;
- IrInstSrc *ret_ptr;
- IrInstSrc *new_stack;
- IrInstSrc *args;
- ResultLoc *result_loc;
-};
-
-struct IrInstGenCall {
- IrInstGen base;
-
- IrInstGen *fn_ref;
- ZigFn *fn_entry;
- size_t arg_count;
- IrInstGen **args;
- IrInstGen *result_loc;
- IrInstGen *frame_result_loc;
- IrInstGen *new_stack;
-
- CallModifier modifier;
-
- bool is_async_call_builtin;
-};
-
-struct IrInstSrcConst {
- IrInstSrc base;
-
- ZigValue *value;
-};
-
-struct IrInstGenConst {
- IrInstGen base;
-};
-
-struct IrInstSrcReturn {
- IrInstSrc base;
-
- IrInstSrc *operand;
-};
-
-// When an IrExecutable is not in a function, a return instruction means that
-// the expression returns with that value, even though a return statement from
-// an AST perspective is invalid.
-struct IrInstGenReturn {
- IrInstGen base;
-
- IrInstGen *operand;
-};
-
-enum CastOp {
- CastOpNoCast, // signifies the function call expression is not a cast
- CastOpNoop, // fn call expr is a cast, but does nothing
- CastOpIntToFloat,
- CastOpFloatToInt,
- CastOpBoolToInt,
- CastOpNumLitToConcrete,
- CastOpErrSet,
- CastOpBitCast,
-};
-
-// TODO get rid of this instruction, replace with instructions for each op code
-struct IrInstGenCast {
- IrInstGen base;
-
- IrInstGen *value;
- CastOp cast_op;
-};
-
-struct IrInstSrcContainerInitList {
- IrInstSrc base;
-
- IrInstSrc *elem_type;
- size_t item_count;
- IrInstSrc **elem_result_loc_list;
- IrInstSrc *result_loc;
- AstNode *init_array_type_source_node;
-};
-
-struct IrInstSrcContainerInitFieldsField {
- Buf *name;
- AstNode *source_node;
- IrInstSrc *result_loc;
-};
-
-struct IrInstSrcContainerInitFields {
- IrInstSrc base;
-
- size_t field_count;
- IrInstSrcContainerInitFieldsField *fields;
- IrInstSrc *result_loc;
-};
-
-struct IrInstSrcUnreachable {
- IrInstSrc base;
-};
-
-struct IrInstGenUnreachable {
- IrInstGen base;
-};
-
-struct IrInstSrcTypeOf {
- IrInstSrc base;
-
- union {
- IrInstSrc *scalar; // value_count == 1
- IrInstSrc **list; // value_count > 1
- } value;
- size_t value_count;
-};
-
-struct IrInstSrcSetCold {
- IrInstSrc base;
-
- IrInstSrc *is_cold;
-};
-
-struct IrInstSrcSetRuntimeSafety {
- IrInstSrc base;
-
- IrInstSrc *safety_on;
-};
-
-struct IrInstSrcSetFloatMode {
- IrInstSrc base;
-
- IrInstSrc *scope_value;
- IrInstSrc *mode_value;
-};
-
-struct IrInstSrcArrayType {
- IrInstSrc base;
-
- IrInstSrc *size;
- IrInstSrc *sentinel;
- IrInstSrc *child_type;
-};
-
-struct IrInstSrcPtrType {
- IrInstSrc base;
-
- IrInstSrc *sentinel;
- IrInstSrc *align_value;
- IrInstSrc *child_type;
- uint32_t bit_offset_start;
- uint32_t host_int_bytes;
- PtrLen ptr_len;
- bool is_const;
- bool is_volatile;
- bool is_allow_zero;
-};
-
-struct IrInstSrcAnyFrameType {
- IrInstSrc base;
-
- IrInstSrc *payload_type;
-};
-
-struct IrInstSrcSliceType {
- IrInstSrc base;
-
- IrInstSrc *sentinel;
- IrInstSrc *align_value;
- IrInstSrc *child_type;
- bool is_const;
- bool is_volatile;
- bool is_allow_zero;
-};
-
-struct IrInstSrcAsm {
- IrInstSrc base;
-
- IrInstSrc *asm_template;
- IrInstSrc **input_list;
- IrInstSrc **output_types;
- ZigVar **output_vars;
- size_t return_count;
- bool has_side_effects;
- bool is_global;
-};
-
-struct IrInstGenAsm {
- IrInstGen base;
-
- Buf *asm_template;
- AsmToken *token_list;
- size_t token_list_len;
- IrInstGen **input_list;
- IrInstGen **output_types;
- ZigVar **output_vars;
- size_t return_count;
- bool has_side_effects;
-};
-
-struct IrInstSrcSizeOf {
- IrInstSrc base;
-
- IrInstSrc *type_value;
- bool bit_size;
-};
-
-// returns true if nonnull, returns false if null
-struct IrInstSrcTestNonNull {
- IrInstSrc base;
-
- IrInstSrc *value;
-};
-
-struct IrInstGenTestNonNull {
- IrInstGen base;
-
- IrInstGen *value;
-};
-
-// Takes a pointer to an optional value, returns a pointer
-// to the payload.
-struct IrInstSrcOptionalUnwrapPtr {
- IrInstSrc base;
-
- IrInstSrc *base_ptr;
- bool safety_check_on;
-};
-
-struct IrInstGenOptionalUnwrapPtr {
- IrInstGen base;
-
- IrInstGen *base_ptr;
- bool safety_check_on;
- bool initializing;
-};
-
-struct IrInstSrcCtz {
- IrInstSrc base;
-
- IrInstSrc *type;
- IrInstSrc *op;
-};
-
-struct IrInstGenCtz {
- IrInstGen base;
-
- IrInstGen *op;
-};
-
-struct IrInstSrcClz {
- IrInstSrc base;
-
- IrInstSrc *type;
- IrInstSrc *op;
-};
-
-struct IrInstGenClz {
- IrInstGen base;
-
- IrInstGen *op;
-};
-
-struct IrInstSrcPopCount {
- IrInstSrc base;
-
- IrInstSrc *type;
- IrInstSrc *op;
-};
-
-struct IrInstGenPopCount {
- IrInstGen base;
-
- IrInstGen *op;
-};
-
-struct IrInstGenUnionTag {
- IrInstGen base;
-
- IrInstGen *value;
-};
-
-struct IrInstSrcImport {
- IrInstSrc base;
-
- IrInstSrc *name;
-};
-
-struct IrInstSrcRef {
- IrInstSrc base;
-
- IrInstSrc *value;
-};
-
-struct IrInstGenRef {
- IrInstGen base;
-
- IrInstGen *operand;
- IrInstGen *result_loc;
-};
-
-struct IrInstSrcCompileErr {
- IrInstSrc base;
-
- IrInstSrc *msg;
-};
-
-struct IrInstSrcCompileLog {
- IrInstSrc base;
-
- size_t msg_count;
- IrInstSrc **msg_list;
-};
-
-struct IrInstSrcErrName {
- IrInstSrc base;
-
- IrInstSrc *value;
-};
-
-struct IrInstGenErrName {
- IrInstGen base;
-
- IrInstGen *value;
-};
-
-struct IrInstSrcCImport {
- IrInstSrc base;
-};
-
-struct IrInstSrcCInclude {
- IrInstSrc base;
-
- IrInstSrc *name;
-};
-
-struct IrInstSrcCDefine {
- IrInstSrc base;
-
- IrInstSrc *name;
- IrInstSrc *value;
-};
-
-struct IrInstSrcCUndef {
- IrInstSrc base;
-
- IrInstSrc *name;
-};
-
-struct IrInstSrcEmbedFile {
- IrInstSrc base;
-
- IrInstSrc *name;
-};
-
-struct IrInstSrcCmpxchg {
- IrInstSrc base;
-
- bool is_weak;
- IrInstSrc *type_value;
- IrInstSrc *ptr;
- IrInstSrc *cmp_value;
- IrInstSrc *new_value;
- IrInstSrc *success_order_value;
- IrInstSrc *failure_order_value;
- ResultLoc *result_loc;
-};
-
-struct IrInstGenCmpxchg {
- IrInstGen base;
-
- AtomicOrder success_order;
- AtomicOrder failure_order;
- IrInstGen *ptr;
- IrInstGen *cmp_value;
- IrInstGen *new_value;
- IrInstGen *result_loc;
- bool is_weak;
-};
-
-struct IrInstSrcFence {
- IrInstSrc base;
-
- IrInstSrc *order;
-};
-
-struct IrInstGenFence {
- IrInstGen base;
-
- AtomicOrder order;
-};
-
-struct IrInstSrcTruncate {
- IrInstSrc base;
-
- IrInstSrc *dest_type;
- IrInstSrc *target;
-};
-
-struct IrInstGenTruncate {
- IrInstGen base;
-
- IrInstGen *target;
-};
-
-struct IrInstSrcIntCast {
- IrInstSrc base;
-
- IrInstSrc *dest_type;
- IrInstSrc *target;
-};
-
-struct IrInstSrcFloatCast {
- IrInstSrc base;
-
- IrInstSrc *dest_type;
- IrInstSrc *target;
-};
-
-struct IrInstSrcErrSetCast {
- IrInstSrc base;
-
- IrInstSrc *dest_type;
- IrInstSrc *target;
-};
-
-struct IrInstSrcIntToFloat {
- IrInstSrc base;
-
- IrInstSrc *dest_type;
- IrInstSrc *target;
-};
-
-struct IrInstSrcFloatToInt {
- IrInstSrc base;
-
- IrInstSrc *dest_type;
- IrInstSrc *target;
-};
-
-struct IrInstSrcBoolToInt {
- IrInstSrc base;
-
- IrInstSrc *target;
-};
-
-struct IrInstSrcVectorType {
- IrInstSrc base;
-
- IrInstSrc *len;
- IrInstSrc *elem_type;
-};
-
-struct IrInstSrcBoolNot {
- IrInstSrc base;
-
- IrInstSrc *value;
-};
-
-struct IrInstGenBoolNot {
- IrInstGen base;
-
- IrInstGen *value;
-};
-
-struct IrInstSrcMemset {
- IrInstSrc base;
-
- IrInstSrc *dest_ptr;
- IrInstSrc *byte;
- IrInstSrc *count;
-};
-
-struct IrInstGenMemset {
- IrInstGen base;
-
- IrInstGen *dest_ptr;
- IrInstGen *byte;
- IrInstGen *count;
-};
-
-struct IrInstSrcMemcpy {
- IrInstSrc base;
-
- IrInstSrc *dest_ptr;
- IrInstSrc *src_ptr;
- IrInstSrc *count;
-};
-
-struct IrInstGenMemcpy {
- IrInstGen base;
-
- IrInstGen *dest_ptr;
- IrInstGen *src_ptr;
- IrInstGen *count;
-};
-
-struct IrInstSrcWasmMemorySize {
- IrInstSrc base;
-
- IrInstSrc *index;
-};
-
-struct IrInstGenWasmMemorySize {
- IrInstGen base;
-
- IrInstGen *index;
-};
-
-struct IrInstSrcWasmMemoryGrow {
- IrInstSrc base;
-
- IrInstSrc *index;
- IrInstSrc *delta;
-};
-
-struct IrInstGenWasmMemoryGrow {
- IrInstGen base;
-
- IrInstGen *index;
- IrInstGen *delta;
-};
-
-struct IrInstSrcSrc {
- IrInstSrc base;
-};
-
-struct IrInstSrcSlice {
- IrInstSrc base;
-
- IrInstSrc *ptr;
- IrInstSrc *start;
- IrInstSrc *end;
- IrInstSrc *sentinel;
- ResultLoc *result_loc;
- bool safety_check_on;
-};
-
-struct IrInstGenSlice {
- IrInstGen base;
-
- IrInstGen *ptr;
- IrInstGen *start;
- IrInstGen *end;
- IrInstGen *result_loc;
- ZigValue *sentinel;
- bool safety_check_on;
-};
-
-struct IrInstSrcBreakpoint {
- IrInstSrc base;
-};
-
-struct IrInstGenBreakpoint {
- IrInstGen base;
-};
-
-struct IrInstSrcReturnAddress {
- IrInstSrc base;
-};
-
-struct IrInstGenReturnAddress {
- IrInstGen base;
-};
-
-struct IrInstSrcFrameAddress {
- IrInstSrc base;
-};
-
-struct IrInstGenFrameAddress {
- IrInstGen base;
-};
-
-struct IrInstSrcFrameHandle {
- IrInstSrc base;
-};
-
-struct IrInstGenFrameHandle {
- IrInstGen base;
-};
-
-struct IrInstSrcFrameType {
- IrInstSrc base;
-
- IrInstSrc *fn;
-};
-
-struct IrInstSrcFrameSize {
- IrInstSrc base;
-
- IrInstSrc *fn;
-};
-
-struct IrInstGenFrameSize {
- IrInstGen base;
-
- IrInstGen *fn;
-};
-
-enum IrOverflowOp {
- IrOverflowOpAdd,
- IrOverflowOpSub,
- IrOverflowOpMul,
- IrOverflowOpShl,
-};
-
-struct IrInstSrcOverflowOp {
- IrInstSrc base;
-
- IrOverflowOp op;
- IrInstSrc *type_value;
- IrInstSrc *op1;
- IrInstSrc *op2;
- IrInstSrc *result_ptr;
-};
-
-struct IrInstGenOverflowOp {
- IrInstGen base;
-
- IrOverflowOp op;
- IrInstGen *op1;
- IrInstGen *op2;
- IrInstGen *result_ptr;
-
- // TODO can this field be removed?
- ZigType *result_ptr_type;
-};
-
-struct IrInstSrcMulAdd {
- IrInstSrc base;
-
- IrInstSrc *type_value;
- IrInstSrc *op1;
- IrInstSrc *op2;
- IrInstSrc *op3;
-};
-
-struct IrInstGenMulAdd {
- IrInstGen base;
-
- IrInstGen *op1;
- IrInstGen *op2;
- IrInstGen *op3;
-};
-
-struct IrInstSrcAlignOf {
- IrInstSrc base;
-
- IrInstSrc *type_value;
-};
-
-// returns true if error, returns false if not error
-struct IrInstSrcTestErr {
- IrInstSrc base;
-
- IrInstSrc *base_ptr;
- bool resolve_err_set;
- bool base_ptr_is_payload;
-};
-
-struct IrInstGenTestErr {
- IrInstGen base;
-
- IrInstGen *err_union;
-};
-
-// Takes an error union pointer, returns a pointer to the error code.
-struct IrInstSrcUnwrapErrCode {
- IrInstSrc base;
-
- IrInstSrc *err_union_ptr;
- bool initializing;
-};
-
-struct IrInstGenUnwrapErrCode {
- IrInstGen base;
-
- IrInstGen *err_union_ptr;
- bool initializing;
-};
-
-struct IrInstSrcUnwrapErrPayload {
- IrInstSrc base;
-
- IrInstSrc *value;
- bool safety_check_on;
- bool initializing;
-};
-
-struct IrInstGenUnwrapErrPayload {
- IrInstGen base;
-
- IrInstGen *value;
- bool safety_check_on;
- bool initializing;
-};
-
-struct IrInstGenOptionalWrap {
- IrInstGen base;
-
- IrInstGen *operand;
- IrInstGen *result_loc;
-};
-
-struct IrInstGenErrWrapPayload {
- IrInstGen base;
-
- IrInstGen *operand;
- IrInstGen *result_loc;
-};
-
-struct IrInstGenErrWrapCode {
- IrInstGen base;
-
- IrInstGen *operand;
- IrInstGen *result_loc;
-};
-
-struct IrInstSrcFnProto {
- IrInstSrc base;
-
- IrInstSrc **param_types;
- IrInstSrc *align_value;
- IrInstSrc *callconv_value;
- IrInstSrc *return_type;
- bool is_var_args;
-};
-
-// true if the target value is compile time known, false otherwise
-struct IrInstSrcTestComptime {
- IrInstSrc base;
-
- IrInstSrc *value;
-};
-
-struct IrInstSrcPtrCast {
- IrInstSrc base;
-
- IrInstSrc *dest_type;
- IrInstSrc *ptr;
- bool safety_check_on;
-};
-
-struct IrInstGenPtrCast {
- IrInstGen base;
-
- IrInstGen *ptr;
- bool safety_check_on;
-};
-
-struct IrInstSrcImplicitCast {
- IrInstSrc base;
-
- IrInstSrc *operand;
- ResultLocCast *result_loc_cast;
-};
-
-struct IrInstSrcBitCast {
- IrInstSrc base;
-
- IrInstSrc *operand;
- ResultLocBitCast *result_loc_bit_cast;
-};
-
-struct IrInstGenBitCast {
- IrInstGen base;
-
- IrInstGen *operand;
-};
-
-struct IrInstGenWidenOrShorten {
- IrInstGen base;
-
- IrInstGen *target;
-};
-
-struct IrInstSrcPtrToInt {
- IrInstSrc base;
-
- IrInstSrc *target;
-};
-
-struct IrInstGenPtrToInt {
- IrInstGen base;
-
- IrInstGen *target;
-};
-
-struct IrInstSrcIntToPtr {
- IrInstSrc base;
-
- IrInstSrc *dest_type;
- IrInstSrc *target;
-};
-
-struct IrInstGenIntToPtr {
- IrInstGen base;
-
- IrInstGen *target;
-};
-
-struct IrInstSrcIntToEnum {
- IrInstSrc base;
-
- IrInstSrc *dest_type;
- IrInstSrc *target;
-};
-
-struct IrInstGenIntToEnum {
- IrInstGen base;
-
- IrInstGen *target;
-};
-
-struct IrInstSrcEnumToInt {
- IrInstSrc base;
-
- IrInstSrc *target;
-};
-
-struct IrInstSrcIntToErr {
- IrInstSrc base;
-
- IrInstSrc *target;
-};
-
-struct IrInstGenIntToErr {
- IrInstGen base;
-
- IrInstGen *target;
-};
-
-struct IrInstSrcErrToInt {
- IrInstSrc base;
-
- IrInstSrc *target;
-};
-
-struct IrInstGenErrToInt {
- IrInstGen base;
-
- IrInstGen *target;
-};
-
-struct IrInstSrcCheckSwitchProngsRange {
- IrInstSrc *start;
- IrInstSrc *end;
-};
-
-struct IrInstSrcCheckSwitchProngs {
- IrInstSrc base;
-
- IrInstSrc *target_value;
- IrInstSrcCheckSwitchProngsRange *ranges;
- size_t range_count;
- AstNode* else_prong;
- bool have_underscore_prong;
-};
-
-struct IrInstSrcCheckStatementIsVoid {
- IrInstSrc base;
-
- IrInstSrc *statement_value;
-};
-
-struct IrInstSrcTypeName {
- IrInstSrc base;
-
- IrInstSrc *type_value;
-};
-
-struct IrInstSrcDeclRef {
- IrInstSrc base;
-
- LVal lval;
- Tld *tld;
-};
-
-struct IrInstSrcPanic {
- IrInstSrc base;
-
- IrInstSrc *msg;
-};
-
-struct IrInstGenPanic {
- IrInstGen base;
-
- IrInstGen *msg;
-};
-
-struct IrInstSrcTagName {
- IrInstSrc base;
-
- IrInstSrc *target;
-};
-
-struct IrInstGenTagName {
- IrInstGen base;
-
- IrInstGen *target;
-};
-
-struct IrInstSrcTagType {
- IrInstSrc base;
-
- IrInstSrc *target;
-};
-
-struct IrInstSrcFieldParentPtr {
- IrInstSrc base;
-
- IrInstSrc *type_value;
- IrInstSrc *field_name;
- IrInstSrc *field_ptr;
-};
-
-struct IrInstGenFieldParentPtr {
- IrInstGen base;
-
- IrInstGen *field_ptr;
- TypeStructField *field;
-};
-
-struct IrInstSrcByteOffsetOf {
- IrInstSrc base;
-
- IrInstSrc *type_value;
- IrInstSrc *field_name;
-};
-
-struct IrInstSrcBitOffsetOf {
- IrInstSrc base;
-
- IrInstSrc *type_value;
- IrInstSrc *field_name;
-};
-
-struct IrInstSrcTypeInfo {
- IrInstSrc base;
-
- IrInstSrc *type_value;
-};
-
-struct IrInstSrcType {
- IrInstSrc base;
-
- IrInstSrc *type_info;
-};
-
-struct IrInstSrcHasField {
- IrInstSrc base;
-
- IrInstSrc *container_type;
- IrInstSrc *field_name;
-};
-
-struct IrInstSrcSetEvalBranchQuota {
- IrInstSrc base;
-
- IrInstSrc *new_quota;
-};
-
-struct IrInstSrcAlignCast {
- IrInstSrc base;
-
- IrInstSrc *align_bytes;
- IrInstSrc *target;
-};
-
-struct IrInstGenAlignCast {
- IrInstGen base;
-
- IrInstGen *target;
-};
-
-struct IrInstSrcSetAlignStack {
- IrInstSrc base;
-
- IrInstSrc *align_bytes;
-};
-
-struct IrInstSrcArgType {
- IrInstSrc base;
-
- IrInstSrc *fn_type;
- IrInstSrc *arg_index;
- bool allow_var;
-};
-
-struct IrInstSrcExport {
- IrInstSrc base;
-
- IrInstSrc *target;
- IrInstSrc *options;
-};
-
-enum IrInstErrorReturnTraceOptional {
- IrInstErrorReturnTraceNull,
- IrInstErrorReturnTraceNonNull,
-};
-
-struct IrInstSrcErrorReturnTrace {
- IrInstSrc base;
-
- IrInstErrorReturnTraceOptional optional;
-};
-
-struct IrInstGenErrorReturnTrace {
- IrInstGen base;
-
- IrInstErrorReturnTraceOptional optional;
-};
-
-struct IrInstSrcErrorUnion {
- IrInstSrc base;
-
- IrInstSrc *err_set;
- IrInstSrc *payload;
- Buf *type_name;
-};
-
-struct IrInstSrcAtomicRmw {
- IrInstSrc base;
-
- IrInstSrc *operand_type;
- IrInstSrc *ptr;
- IrInstSrc *op;
- IrInstSrc *operand;
- IrInstSrc *ordering;
-};
-
-struct IrInstGenAtomicRmw {
- IrInstGen base;
-
- IrInstGen *ptr;
- IrInstGen *operand;
- AtomicRmwOp op;
- AtomicOrder ordering;
-};
-
-struct IrInstSrcAtomicLoad {
- IrInstSrc base;
-
- IrInstSrc *operand_type;
- IrInstSrc *ptr;
- IrInstSrc *ordering;
-};
-
-struct IrInstGenAtomicLoad {
- IrInstGen base;
-
- IrInstGen *ptr;
- AtomicOrder ordering;
-};
-
-struct IrInstSrcAtomicStore {
- IrInstSrc base;
-
- IrInstSrc *operand_type;
- IrInstSrc *ptr;
- IrInstSrc *value;
- IrInstSrc *ordering;
-};
-
-struct IrInstGenAtomicStore {
- IrInstGen base;
-
- IrInstGen *ptr;
- IrInstGen *value;
- AtomicOrder ordering;
-};
-
-struct IrInstSrcSaveErrRetAddr {
- IrInstSrc base;
-};
-
-struct IrInstGenSaveErrRetAddr {
- IrInstGen base;
-};
-
-struct IrInstSrcAddImplicitReturnType {
- IrInstSrc base;
-
- IrInstSrc *value;
- ResultLocReturn *result_loc_ret;
-};
-
-// For float ops that take a single argument
-struct IrInstSrcFloatOp {
- IrInstSrc base;
-
- IrInstSrc *operand;
- BuiltinFnId fn_id;
-};
-
-struct IrInstGenFloatOp {
- IrInstGen base;
-
- IrInstGen *operand;
- BuiltinFnId fn_id;
-};
-
-struct IrInstSrcCheckRuntimeScope {
- IrInstSrc base;
-
- IrInstSrc *scope_is_comptime;
- IrInstSrc *is_comptime;
-};
-
-struct IrInstSrcBswap {
- IrInstSrc base;
-
- IrInstSrc *type;
- IrInstSrc *op;
-};
-
-struct IrInstGenBswap {
- IrInstGen base;
-
- IrInstGen *op;
-};
-
-struct IrInstSrcBitReverse {
- IrInstSrc base;
-
- IrInstSrc *type;
- IrInstSrc *op;
-};
-
-struct IrInstGenBitReverse {
- IrInstGen base;
-
- IrInstGen *op;
-};
-
-struct IrInstGenArrayToVector {
- IrInstGen base;
-
- IrInstGen *array;
-};
-
-struct IrInstGenVectorToArray {
- IrInstGen base;
-
- IrInstGen *vector;
- IrInstGen *result_loc;
-};
-
-struct IrInstSrcShuffleVector {
- IrInstSrc base;
-
- IrInstSrc *scalar_type;
- IrInstSrc *a;
- IrInstSrc *b;
- IrInstSrc *mask; // This is in zig-format, not llvm format
-};
-
-struct IrInstGenShuffleVector {
- IrInstGen base;
-
- IrInstGen *a;
- IrInstGen *b;
- IrInstGen *mask; // This is in zig-format, not llvm format
-};
-
-struct IrInstSrcSplat {
- IrInstSrc base;
-
- IrInstSrc *len;
- IrInstSrc *scalar;
-};
-
-struct IrInstGenSplat {
- IrInstGen base;
-
- IrInstGen *scalar;
-};
-
-struct IrInstGenAssertZero {
- IrInstGen base;
-
- IrInstGen *target;
-};
-
-struct IrInstGenAssertNonNull {
- IrInstGen base;
-
- IrInstGen *target;
-};
-
-struct IrInstSrcUnionInitNamedField {
- IrInstSrc base;
-
- IrInstSrc *union_type;
- IrInstSrc *field_name;
- IrInstSrc *field_result_loc;
- IrInstSrc *result_loc;
-};
-
-struct IrInstSrcHasDecl {
- IrInstSrc base;
-
- IrInstSrc *container;
- IrInstSrc *name;
-};
-
-struct IrInstSrcUndeclaredIdent {
- IrInstSrc base;
-
- Buf *name;
-};
-
-struct IrInstSrcAlloca {
- IrInstSrc base;
-
- IrInstSrc *align;
- IrInstSrc *is_comptime;
- const char *name_hint;
-};
-
-struct IrInstGenAlloca {
- IrInstGen base;
-
- uint32_t align;
- const char *name_hint;
- size_t field_index;
-};
-
-struct IrInstSrcEndExpr {
- IrInstSrc base;
-
- IrInstSrc *value;
- ResultLoc *result_loc;
-};
-
-// This one is for writing through the result pointer.
-struct IrInstSrcResolveResult {
- IrInstSrc base;
-
- ResultLoc *result_loc;
- IrInstSrc *ty;
-};
-
-struct IrInstSrcResetResult {
- IrInstSrc base;
-
- ResultLoc *result_loc;
-};
-
-struct IrInstGenPtrOfArrayToSlice {
- IrInstGen base;
-
- IrInstGen *operand;
- IrInstGen *result_loc;
-};
-
-struct IrInstSrcSuspendBegin {
- IrInstSrc base;
-};
-
-struct IrInstGenSuspendBegin {
- IrInstGen base;
-
- LLVMBasicBlockRef resume_bb;
-};
-
-struct IrInstSrcSuspendFinish {
- IrInstSrc base;
-
- IrInstSrcSuspendBegin *begin;
-};
-
-struct IrInstGenSuspendFinish {
- IrInstGen base;
-
- IrInstGenSuspendBegin *begin;
-};
-
-struct IrInstSrcAwait {
- IrInstSrc base;
-
- IrInstSrc *frame;
- ResultLoc *result_loc;
- bool is_nosuspend;
-};
-
-struct IrInstGenAwait {
- IrInstGen base;
-
- IrInstGen *frame;
- IrInstGen *result_loc;
- ZigFn *target_fn;
- bool is_nosuspend;
-};
-
-struct IrInstSrcResume {
- IrInstSrc base;
-
- IrInstSrc *frame;
-};
-
-struct IrInstGenResume {
- IrInstGen base;
-
- IrInstGen *frame;
-};
-
-enum SpillId {
- SpillIdInvalid,
- SpillIdRetErrCode,
-};
-
-struct IrInstSrcSpillBegin {
- IrInstSrc base;
-
- IrInstSrc *operand;
- SpillId spill_id;
-};
-
-struct IrInstGenSpillBegin {
- IrInstGen base;
-
- SpillId spill_id;
- IrInstGen *operand;
-};
-
-struct IrInstSrcSpillEnd {
- IrInstSrc base;
-
- IrInstSrcSpillBegin *begin;
-};
-
-struct IrInstGenSpillEnd {
- IrInstGen base;
-
- IrInstGenSpillBegin *begin;
-};
-
-struct IrInstGenVectorExtractElem {
- IrInstGen base;
-
- IrInstGen *vector;
- IrInstGen *index;
-};
-
-enum ResultLocId {
- ResultLocIdInvalid,
- ResultLocIdNone,
- ResultLocIdVar,
- ResultLocIdReturn,
- ResultLocIdPeer,
- ResultLocIdPeerParent,
- ResultLocIdInstruction,
- ResultLocIdBitCast,
- ResultLocIdCast,
-};
-
-// Additions to this struct may need to be handled in
-// ir_reset_result
-struct ResultLoc {
- ResultLocId id;
- bool written;
- bool allow_write_through_const;
- IrInstGen *resolved_loc; // result ptr
- IrInstSrc *source_instruction;
- IrInstGen *gen_instruction; // value to store to the result loc
- ZigType *implicit_elem_type;
-};
-
-struct ResultLocNone {
- ResultLoc base;
-};
-
-struct ResultLocVar {
- ResultLoc base;
-
- ZigVar *var;
-};
-
-struct ResultLocReturn {
- ResultLoc base;
-
- bool implicit_return_type_done;
-};
-
-struct IrSuspendPosition {
- size_t basic_block_index;
- size_t instruction_index;
-};
-
-struct ResultLocPeerParent {
- ResultLoc base;
-
- bool skipped;
- bool done_resuming;
- IrBasicBlockSrc *end_bb;
- ResultLoc *parent;
- ZigList peers;
- ZigType *resolved_type;
- IrInstSrc *is_comptime;
-};
-
-struct ResultLocPeer {
- ResultLoc base;
-
- ResultLocPeerParent *parent;
- IrBasicBlockSrc *next_bb;
- IrSuspendPosition suspend_pos;
-};
-
-// The result location is the source instruction
-struct ResultLocInstruction {
- ResultLoc base;
-};
-
-// The source_instruction is the destination type
-struct ResultLocBitCast {
- ResultLoc base;
-
- ResultLoc *parent;
-};
-
-// The source_instruction is the destination type
-struct ResultLocCast {
- ResultLoc base;
-
- ResultLoc *parent;
-};
-
-static const size_t slice_ptr_index = 0;
-static const size_t slice_len_index = 1;
-
-static const size_t maybe_child_index = 0;
-static const size_t maybe_null_index = 1;
-
-static const size_t err_union_payload_index = 0;
-static const size_t err_union_err_index = 1;
-
-// label (grep this): [fn_frame_struct_layout]
-static const size_t frame_fn_ptr_index = 0;
-static const size_t frame_resume_index = 1;
-static const size_t frame_awaiter_index = 2;
-static const size_t frame_ret_start = 3;
-
-// TODO https://github.com/ziglang/zig/issues/3056
-// We require this to be a power of 2 so that we can use shifting rather than
-// remainder division.
-static const size_t stack_trace_ptr_count = 32; // Must be a power of 2.
-
-#define NAMESPACE_SEP_CHAR '.'
-#define NAMESPACE_SEP_STR "."
-
-#define CACHE_OUT_SUBDIR "o"
-#define CACHE_HASH_SUBDIR "h"
-
-enum FloatMode {
- FloatModeStrict,
- FloatModeOptimized,
-};
-
-enum FnWalkId {
- FnWalkIdAttrs,
- FnWalkIdCall,
- FnWalkIdTypes,
- FnWalkIdVars,
- FnWalkIdInits,
-};
-
-struct FnWalkAttrs {
- ZigFn *fn;
- LLVMValueRef llvm_fn;
- unsigned gen_i;
-};
-
-struct FnWalkCall {
- ZigList *gen_param_values;
- ZigList *gen_param_types;
- IrInstGenCall *inst;
- bool is_var_args;
-};
-
-struct FnWalkTypes {
- ZigList *param_di_types;
- ZigList *gen_param_types;
-};
-
-struct FnWalkVars {
- ZigType *import;
- LLVMValueRef llvm_fn;
- ZigFn *fn;
- ZigVar *var;
- unsigned gen_i;
-};
-
-struct FnWalkInits {
- LLVMValueRef llvm_fn;
- ZigFn *fn;
- unsigned gen_i;
-};
-
-struct FnWalk {
- FnWalkId id;
- union {
- FnWalkAttrs attrs;
- FnWalkCall call;
- FnWalkTypes types;
- FnWalkVars vars;
- FnWalkInits inits;
- } data;
-};
-
-#endif
diff --git a/src/analyze.cpp b/src/analyze.cpp
deleted file mode 100644
index 6c6f198e7a33d7fb5dcbdfbb32ead29d1f2f7a82..0000000000000000000000000000000000000000
--- a/src/analyze.cpp
+++ /dev/null
@@ -1,9941 +0,0 @@
-/*
- * Copyright (c) 2015 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#include "analyze.hpp"
-#include "ast_render.hpp"
-#include "codegen.hpp"
-#include "config.h"
-#include "error.hpp"
-#include "ir.hpp"
-#include "ir_print.hpp"
-#include "os.hpp"
-#include "parser.hpp"
-#include "softfloat.hpp"
-#include "zig_llvm.h"
-
-
-static const size_t default_backward_branch_quota = 1000;
-
-static Error ATTRIBUTE_MUST_USE resolve_struct_type(CodeGen *g, ZigType *struct_type);
-
-static Error ATTRIBUTE_MUST_USE resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type);
-static Error ATTRIBUTE_MUST_USE resolve_struct_alignment(CodeGen *g, ZigType *struct_type);
-static Error ATTRIBUTE_MUST_USE resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type);
-static Error ATTRIBUTE_MUST_USE resolve_union_zero_bits(CodeGen *g, ZigType *union_type);
-static Error ATTRIBUTE_MUST_USE resolve_union_alignment(CodeGen *g, ZigType *union_type);
-static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry);
-static void resolve_llvm_types(CodeGen *g, ZigType *type, ResolveStatus wanted_resolve_status);
-static void preview_use_decl(CodeGen *g, TldUsingNamespace *using_namespace, ScopeDecls *dest_decls_scope);
-static void resolve_use_decl(CodeGen *g, TldUsingNamespace *tld_using_namespace, ScopeDecls *dest_decls_scope);
-static void analyze_fn_async(CodeGen *g, ZigFn *fn, bool resolve_frame);
-
-// nullptr means not analyzed yet; this one means currently being analyzed
-static const AstNode *inferred_async_checking = reinterpret_cast(0x1);
-// this one means analyzed and it's not async
-static const AstNode *inferred_async_none = reinterpret_cast(0x2);
-
-static bool is_top_level_struct(ZigType *import) {
- return import->id == ZigTypeIdStruct && import->data.structure.root_struct != nullptr;
-}
-
-static ErrorMsg *add_error_note_token(CodeGen *g, ErrorMsg *parent_msg, ZigType *owner, Token *token, Buf *msg) {
- assert(is_top_level_struct(owner));
- RootStruct *root_struct = owner->data.structure.root_struct;
-
- ErrorMsg *err = err_msg_create_with_line(root_struct->path, token->start_line, token->start_column,
- root_struct->source_code, root_struct->line_offsets, msg);
-
- err_msg_add_note(parent_msg, err);
- return err;
-}
-
-ErrorMsg *add_token_error(CodeGen *g, ZigType *owner, Token *token, Buf *msg) {
- assert(is_top_level_struct(owner));
- RootStruct *root_struct = owner->data.structure.root_struct;
- ErrorMsg *err = err_msg_create_with_line(root_struct->path, token->start_line, token->start_column,
- root_struct->source_code, root_struct->line_offsets, msg);
-
- g->errors.append(err);
- g->trace_err = err;
- return err;
-}
-
-ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
- Token fake_token;
- fake_token.start_line = node->line;
- fake_token.start_column = node->column;
- node->already_traced_this_node = true;
- return add_token_error(g, node->owner, &fake_token, msg);
-}
-
-ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, const AstNode *node, Buf *msg) {
- Token fake_token;
- fake_token.start_line = node->line;
- fake_token.start_column = node->column;
- return add_error_note_token(g, parent_msg, node->owner, &fake_token, msg);
-}
-
-ZigType *new_type_table_entry(ZigTypeId id) {
- ZigType *entry = heap::c_allocator.create();
- entry->id = id;
- return entry;
-}
-
-static ScopeDecls **get_container_scope_ptr(ZigType *type_entry) {
- if (type_entry->id == ZigTypeIdStruct) {
- return &type_entry->data.structure.decls_scope;
- } else if (type_entry->id == ZigTypeIdEnum) {
- return &type_entry->data.enumeration.decls_scope;
- } else if (type_entry->id == ZigTypeIdUnion) {
- return &type_entry->data.unionation.decls_scope;
- }
- zig_unreachable();
-}
-
-static ScopeExpr *find_expr_scope(Scope *scope) {
- for (;;) {
- switch (scope->id) {
- case ScopeIdExpr:
- return reinterpret_cast(scope);
- case ScopeIdDefer:
- case ScopeIdDeferExpr:
- case ScopeIdDecls:
- case ScopeIdFnDef:
- case ScopeIdCompTime:
- case ScopeIdNoSuspend:
- case ScopeIdVarDecl:
- case ScopeIdCImport:
- case ScopeIdSuspend:
- case ScopeIdTypeOf:
- case ScopeIdBlock:
- return nullptr;
- case ScopeIdLoop:
- case ScopeIdRuntime:
- scope = scope->parent;
- continue;
- }
- }
-}
-
-static void update_progress_display(CodeGen *g) {
- stage2_progress_update_node(g->sub_progress_node,
- g->resolve_queue_index + g->fn_defs_index,
- g->resolve_queue.length + g->fn_defs.length);
-}
-
-ScopeDecls *get_container_scope(ZigType *type_entry) {
- return *get_container_scope_ptr(type_entry);
-}
-
-void init_scope(CodeGen *g, Scope *dest, ScopeId id, AstNode *source_node, Scope *parent) {
- dest->codegen = g;
- dest->id = id;
- dest->source_node = source_node;
- dest->parent = parent;
-}
-
-ScopeDecls *create_decls_scope(CodeGen *g, AstNode *node, Scope *parent, ZigType *container_type,
- ZigType *import, Buf *bare_name)
-{
- ScopeDecls *scope = heap::c_allocator.create();
- init_scope(g, &scope->base, ScopeIdDecls, node, parent);
- scope->decl_table.init(4);
- scope->container_type = container_type;
- scope->import = import;
- scope->bare_name = bare_name;
- return scope;
-}
-
-ScopeBlock *create_block_scope(CodeGen *g, AstNode *node, Scope *parent) {
- assert(node->type == NodeTypeBlock);
- ScopeBlock *scope = heap::c_allocator.create();
- init_scope(g, &scope->base, ScopeIdBlock, node, parent);
- scope->name = node->data.block.name;
- return scope;
-}
-
-ScopeDefer *create_defer_scope(CodeGen *g, AstNode *node, Scope *parent) {
- assert(node->type == NodeTypeDefer);
- ScopeDefer *scope = heap::c_allocator.create();
- init_scope(g, &scope->base, ScopeIdDefer, node, parent);
- return scope;
-}
-
-ScopeDeferExpr *create_defer_expr_scope(CodeGen *g, AstNode *node, Scope *parent) {
- assert(node->type == NodeTypeDefer);
- ScopeDeferExpr *scope = heap::c_allocator.create();
- init_scope(g, &scope->base, ScopeIdDeferExpr, node, parent);
- return scope;
-}
-
-Scope *create_var_scope(CodeGen *g, AstNode *node, Scope *parent, ZigVar *var) {
- ScopeVarDecl *scope = heap::c_allocator.create();
- init_scope(g, &scope->base, ScopeIdVarDecl, node, parent);
- scope->var = var;
- return &scope->base;
-}
-
-ScopeCImport *create_cimport_scope(CodeGen *g, AstNode *node, Scope *parent) {
- assert(node->type == NodeTypeFnCallExpr);
- ScopeCImport *scope = heap::c_allocator.create();
- init_scope(g, &scope->base, ScopeIdCImport, node, parent);
- buf_resize(&scope->buf, 0);
- return scope;
-}
-
-ScopeLoop *create_loop_scope(CodeGen *g, AstNode *node, Scope *parent) {
- ScopeLoop *scope = heap::c_allocator.create();
- init_scope(g, &scope->base, ScopeIdLoop, node, parent);
- if (node->type == NodeTypeWhileExpr) {
- scope->name = node->data.while_expr.name;
- } else if (node->type == NodeTypeForExpr) {
- scope->name = node->data.for_expr.name;
- } else {
- zig_unreachable();
- }
- return scope;
-}
-
-Scope *create_runtime_scope(CodeGen *g, AstNode *node, Scope *parent, IrInstSrc *is_comptime) {
- ScopeRuntime *scope = heap::c_allocator.create();
- scope->is_comptime = is_comptime;
- init_scope(g, &scope->base, ScopeIdRuntime, node, parent);
- return &scope->base;
-}
-
-ScopeSuspend *create_suspend_scope(CodeGen *g, AstNode *node, Scope *parent) {
- assert(node->type == NodeTypeSuspend);
- ScopeSuspend *scope = heap::c_allocator.create();
- init_scope(g, &scope->base, ScopeIdSuspend, node, parent);
- return scope;
-}
-
-ScopeFnDef *create_fndef_scope(CodeGen *g, AstNode *node, Scope *parent, ZigFn *fn_entry) {
- ScopeFnDef *scope = heap::c_allocator.create();
- init_scope(g, &scope->base, ScopeIdFnDef, node, parent);
- scope->fn_entry = fn_entry;
- return scope;
-}
-
-Scope *create_comptime_scope(CodeGen *g, AstNode *node, Scope *parent) {
- ScopeCompTime *scope = heap::c_allocator.create();
- init_scope(g, &scope->base, ScopeIdCompTime, node, parent);
- return &scope->base;
-}
-
-Scope *create_nosuspend_scope(CodeGen *g, AstNode *node, Scope *parent) {
- ScopeNoSuspend *scope = heap::c_allocator.create();
- init_scope(g, &scope->base, ScopeIdNoSuspend, node, parent);
- return &scope->base;
-}
-
-Scope *create_typeof_scope(CodeGen *g, AstNode *node, Scope *parent) {
- ScopeTypeOf *scope = heap::c_allocator.create();
- init_scope(g, &scope->base, ScopeIdTypeOf, node, parent);
- return &scope->base;
-}
-
-ScopeExpr *create_expr_scope(CodeGen *g, AstNode *node, Scope *parent) {
- ScopeExpr *scope = heap::c_allocator.create();
- init_scope(g, &scope->base, ScopeIdExpr, node, parent);
- ScopeExpr *parent_expr = find_expr_scope(parent);
- if (parent_expr != nullptr) {
- size_t new_len = parent_expr->children_len + 1;
- parent_expr->children_ptr = heap::c_allocator.reallocate_nonzero(
- parent_expr->children_ptr, parent_expr->children_len, new_len);
- parent_expr->children_ptr[parent_expr->children_len] = scope;
- parent_expr->children_len = new_len;
- }
- return scope;
-}
-
-ZigType *get_scope_import(Scope *scope) {
- while (scope) {
- if (scope->id == ScopeIdDecls) {
- ScopeDecls *decls_scope = (ScopeDecls *)scope;
- assert(is_top_level_struct(decls_scope->import));
- return decls_scope->import;
- }
- scope = scope->parent;
- }
- zig_unreachable();
-}
-
-ScopeTypeOf *get_scope_typeof(Scope *scope) {
- while (scope) {
- switch (scope->id) {
- case ScopeIdTypeOf:
- return reinterpret_cast(scope);
- case ScopeIdFnDef:
- case ScopeIdDecls:
- return nullptr;
- default:
- scope = scope->parent;
- continue;
- }
- }
- zig_unreachable();
-}
-
-static ZigType *new_container_type_entry(CodeGen *g, ZigTypeId id, AstNode *source_node, Scope *parent_scope,
- Buf *bare_name)
-{
- ZigType *entry = new_type_table_entry(id);
- *get_container_scope_ptr(entry) = create_decls_scope(g, source_node, parent_scope, entry,
- get_scope_import(parent_scope), bare_name);
- return entry;
-}
-
-static uint8_t bits_needed_for_unsigned(uint64_t x) {
- if (x == 0) {
- return 0;
- }
- uint8_t base = log2_u64(x);
- uint64_t upper = (((uint64_t)1) << base) - 1;
- return (upper >= x) ? base : (base + 1);
-}
-
-AstNode *type_decl_node(ZigType *type_entry) {
- switch (type_entry->id) {
- case ZigTypeIdInvalid:
- zig_unreachable();
- case ZigTypeIdStruct:
- return type_entry->data.structure.decl_node;
- case ZigTypeIdEnum:
- return type_entry->data.enumeration.decl_node;
- case ZigTypeIdUnion:
- return type_entry->data.unionation.decl_node;
- case ZigTypeIdFnFrame:
- return type_entry->data.frame.fn->proto_node;
- case ZigTypeIdOpaque:
- case ZigTypeIdMetaType:
- case ZigTypeIdVoid:
- case ZigTypeIdBool:
- case ZigTypeIdUnreachable:
- case ZigTypeIdInt:
- case ZigTypeIdFloat:
- case ZigTypeIdPointer:
- case ZigTypeIdArray:
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdEnumLiteral:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdOptional:
- case ZigTypeIdErrorUnion:
- case ZigTypeIdErrorSet:
- case ZigTypeIdFn:
- case ZigTypeIdBoundFn:
- case ZigTypeIdVector:
- case ZigTypeIdAnyFrame:
- return nullptr;
- }
- zig_unreachable();
-}
-
-bool type_is_resolved(ZigType *type_entry, ResolveStatus status) {
- switch (type_entry->id) {
- case ZigTypeIdInvalid:
- zig_unreachable();
- case ZigTypeIdStruct:
- return type_entry->data.structure.resolve_status >= status;
- case ZigTypeIdUnion:
- return type_entry->data.unionation.resolve_status >= status;
- case ZigTypeIdEnum:
- return type_entry->data.enumeration.resolve_status >= status;
- case ZigTypeIdFnFrame:
- switch (status) {
- case ResolveStatusInvalid:
- zig_unreachable();
- case ResolveStatusBeingInferred:
- zig_unreachable();
- case ResolveStatusUnstarted:
- case ResolveStatusZeroBitsKnown:
- return true;
- case ResolveStatusAlignmentKnown:
- case ResolveStatusSizeKnown:
- return type_entry->data.frame.locals_struct != nullptr;
- case ResolveStatusLLVMFwdDecl:
- case ResolveStatusLLVMFull:
- return type_entry->llvm_type != nullptr;
- }
- zig_unreachable();
- case ZigTypeIdOpaque:
- return status < ResolveStatusSizeKnown;
- case ZigTypeIdPointer:
- switch (status) {
- case ResolveStatusInvalid:
- zig_unreachable();
- case ResolveStatusBeingInferred:
- zig_unreachable();
- case ResolveStatusUnstarted:
- return true;
- case ResolveStatusZeroBitsKnown:
- case ResolveStatusAlignmentKnown:
- case ResolveStatusSizeKnown:
- return type_entry->abi_size != SIZE_MAX;
- case ResolveStatusLLVMFwdDecl:
- case ResolveStatusLLVMFull:
- return type_entry->llvm_type != nullptr;
- }
- zig_unreachable();
- case ZigTypeIdMetaType:
- case ZigTypeIdVoid:
- case ZigTypeIdBool:
- case ZigTypeIdUnreachable:
- case ZigTypeIdInt:
- case ZigTypeIdFloat:
- case ZigTypeIdArray:
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdEnumLiteral:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdOptional:
- case ZigTypeIdErrorUnion:
- case ZigTypeIdErrorSet:
- case ZigTypeIdFn:
- case ZigTypeIdBoundFn:
- case ZigTypeIdVector:
- case ZigTypeIdAnyFrame:
- return true;
- }
- zig_unreachable();
-}
-
-bool type_is_complete(ZigType *type_entry) {
- return type_is_resolved(type_entry, ResolveStatusSizeKnown);
-}
-
-uint64_t type_size(CodeGen *g, ZigType *type_entry) {
- assert(type_is_resolved(type_entry, ResolveStatusSizeKnown));
- return type_entry->abi_size;
-}
-
-uint64_t type_size_bits(CodeGen *g, ZigType *type_entry) {
- assert(type_is_resolved(type_entry, ResolveStatusSizeKnown));
- return type_entry->size_in_bits;
-}
-
-uint32_t get_abi_alignment(CodeGen *g, ZigType *type_entry) {
- assert(type_is_resolved(type_entry, ResolveStatusAlignmentKnown));
- return type_entry->abi_align;
-}
-
-static bool is_slice(ZigType *type) {
- return type->id == ZigTypeIdStruct && type->data.structure.special == StructSpecialSlice;
-}
-
-ZigType *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x) {
- return get_int_type(g, false, bits_needed_for_unsigned(x));
-}
-
-ZigType *get_any_frame_type(CodeGen *g, ZigType *result_type) {
- if (result_type != nullptr && result_type->any_frame_parent != nullptr) {
- return result_type->any_frame_parent;
- } else if (result_type == nullptr && g->builtin_types.entry_any_frame != nullptr) {
- return g->builtin_types.entry_any_frame;
- }
-
- ZigType *entry = new_type_table_entry(ZigTypeIdAnyFrame);
- entry->abi_size = g->builtin_types.entry_usize->abi_size;
- entry->size_in_bits = g->builtin_types.entry_usize->size_in_bits;
- entry->abi_align = g->builtin_types.entry_usize->abi_align;
- entry->data.any_frame.result_type = result_type;
- buf_init_from_str(&entry->name, "anyframe");
- if (result_type != nullptr) {
- buf_appendf(&entry->name, "->%s", buf_ptr(&result_type->name));
- }
-
- if (result_type != nullptr) {
- result_type->any_frame_parent = entry;
- } else if (result_type == nullptr) {
- g->builtin_types.entry_any_frame = entry;
- }
- return entry;
-}
-
-ZigType *get_fn_frame_type(CodeGen *g, ZigFn *fn) {
- if (fn->frame_type != nullptr) {
- return fn->frame_type;
- }
-
- ZigType *entry = new_type_table_entry(ZigTypeIdFnFrame);
- buf_resize(&entry->name, 0);
- buf_appendf(&entry->name, "@Frame(%s)", buf_ptr(&fn->symbol_name));
-
- entry->data.frame.fn = fn;
-
- // Async function frames are always non-zero bits because they always have a resume index.
- entry->abi_size = SIZE_MAX;
- entry->size_in_bits = SIZE_MAX;
-
- fn->frame_type = entry;
- return entry;
-}
-
-static void append_ptr_type_attrs(Buf *type_name, ZigType *ptr_type) {
- const char *const_str = ptr_type->data.pointer.is_const ? "const " : "";
- const char *volatile_str = ptr_type->data.pointer.is_volatile ? "volatile " : "";
- const char *allow_zero_str;
- if (ptr_type->data.pointer.ptr_len == PtrLenC) {
- assert(ptr_type->data.pointer.allow_zero);
- allow_zero_str = "";
- } else {
- allow_zero_str = ptr_type->data.pointer.allow_zero ? "allowzero " : "";
- }
- if (ptr_type->data.pointer.explicit_alignment != 0 || ptr_type->data.pointer.host_int_bytes != 0 ||
- ptr_type->data.pointer.vector_index != VECTOR_INDEX_NONE)
- {
- buf_appendf(type_name, "align(");
- if (ptr_type->data.pointer.explicit_alignment != 0) {
- buf_appendf(type_name, "%" PRIu32, ptr_type->data.pointer.explicit_alignment);
- }
- if (ptr_type->data.pointer.host_int_bytes != 0) {
- buf_appendf(type_name, ":%" PRIu32 ":%" PRIu32, ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes);
- }
- if (ptr_type->data.pointer.vector_index == VECTOR_INDEX_RUNTIME) {
- buf_appendf(type_name, ":?");
- } else if (ptr_type->data.pointer.vector_index != VECTOR_INDEX_NONE) {
- buf_appendf(type_name, ":%" PRIu32, ptr_type->data.pointer.vector_index);
- }
- buf_appendf(type_name, ") ");
- }
- buf_appendf(type_name, "%s%s%s", const_str, volatile_str, allow_zero_str);
- if (ptr_type->data.pointer.inferred_struct_field != nullptr) {
- buf_appendf(type_name, " field '%s' of %s)",
- buf_ptr(ptr_type->data.pointer.inferred_struct_field->field_name),
- buf_ptr(&ptr_type->data.pointer.inferred_struct_field->inferred_struct_type->name));
- } else {
- buf_appendf(type_name, "%s", buf_ptr(&ptr_type->data.pointer.child_type->name));
- }
-}
-
-ZigType *get_pointer_to_type_extra2(CodeGen *g, ZigType *child_type, bool is_const,
- bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment,
- uint32_t bit_offset_in_host, uint32_t host_int_bytes, bool allow_zero,
- uint32_t vector_index, InferredStructField *inferred_struct_field, ZigValue *sentinel)
-{
- assert(ptr_len != PtrLenC || allow_zero);
- assert(!type_is_invalid(child_type));
- assert(ptr_len == PtrLenSingle || child_type->id != ZigTypeIdOpaque);
-
- if (byte_alignment != 0) {
- uint32_t abi_alignment = get_abi_alignment(g, child_type);
- if (byte_alignment == abi_alignment)
- byte_alignment = 0;
- }
-
- if (host_int_bytes != 0 && vector_index == VECTOR_INDEX_NONE) {
- uint32_t child_type_bits = type_size_bits(g, child_type);
- if (host_int_bytes * 8 == child_type_bits) {
- assert(bit_offset_in_host == 0);
- host_int_bytes = 0;
- }
- }
-
- TypeId type_id = {};
- ZigType **parent_pointer = nullptr;
- if (host_int_bytes != 0 || is_volatile || byte_alignment != 0 || ptr_len != PtrLenSingle ||
- allow_zero || vector_index != VECTOR_INDEX_NONE || inferred_struct_field != nullptr ||
- sentinel != nullptr)
- {
- type_id.id = ZigTypeIdPointer;
- type_id.data.pointer.codegen = g;
- type_id.data.pointer.child_type = child_type;
- type_id.data.pointer.is_const = is_const;
- type_id.data.pointer.is_volatile = is_volatile;
- type_id.data.pointer.alignment = byte_alignment;
- type_id.data.pointer.bit_offset_in_host = bit_offset_in_host;
- type_id.data.pointer.host_int_bytes = host_int_bytes;
- type_id.data.pointer.ptr_len = ptr_len;
- type_id.data.pointer.allow_zero = allow_zero;
- type_id.data.pointer.vector_index = vector_index;
- type_id.data.pointer.inferred_struct_field = inferred_struct_field;
- type_id.data.pointer.sentinel = sentinel;
-
- auto existing_entry = g->type_table.maybe_get(type_id);
- if (existing_entry)
- return existing_entry->value;
- } else {
- assert(bit_offset_in_host == 0);
- parent_pointer = &child_type->pointer_parent[(is_const ? 1 : 0)];
- if (*parent_pointer) {
- assert((*parent_pointer)->data.pointer.explicit_alignment == 0);
- return *parent_pointer;
- }
- }
-
- ZigType *entry = new_type_table_entry(ZigTypeIdPointer);
-
- buf_resize(&entry->name, 0);
- if (inferred_struct_field != nullptr) {
- buf_appendf(&entry->name, "(");
- }
- switch (ptr_len) {
- case PtrLenSingle:
- assert(sentinel == nullptr);
- buf_appendf(&entry->name, "*");
- break;
- case PtrLenUnknown:
- buf_appendf(&entry->name, "[*");
- break;
- case PtrLenC:
- assert(sentinel == nullptr);
- buf_appendf(&entry->name, "[*c]");
- break;
- }
- if (sentinel != nullptr) {
- buf_appendf(&entry->name, ":");
- render_const_value(g, &entry->name, sentinel);
- }
- switch (ptr_len) {
- case PtrLenSingle:
- case PtrLenC:
- break;
- case PtrLenUnknown:
- buf_appendf(&entry->name, "]");
- break;
- }
-
- if (inferred_struct_field != nullptr) {
- entry->abi_size = SIZE_MAX;
- entry->size_in_bits = SIZE_MAX;
- entry->abi_align = UINT32_MAX;
- } else if (type_is_resolved(child_type, ResolveStatusZeroBitsKnown)) {
- if (type_has_bits(g, child_type)) {
- entry->abi_size = g->builtin_types.entry_usize->abi_size;
- entry->size_in_bits = g->builtin_types.entry_usize->size_in_bits;
- entry->abi_align = g->builtin_types.entry_usize->abi_align;
- } else {
- assert(byte_alignment == 0);
- entry->abi_size = 0;
- entry->size_in_bits = 0;
- entry->abi_align = 0;
- }
- } else {
- entry->abi_size = SIZE_MAX;
- entry->size_in_bits = SIZE_MAX;
- entry->abi_align = UINT32_MAX;
- }
-
- entry->data.pointer.ptr_len = ptr_len;
- entry->data.pointer.child_type = child_type;
- entry->data.pointer.is_const = is_const;
- entry->data.pointer.is_volatile = is_volatile;
- entry->data.pointer.explicit_alignment = byte_alignment;
- entry->data.pointer.bit_offset_in_host = bit_offset_in_host;
- entry->data.pointer.host_int_bytes = host_int_bytes;
- entry->data.pointer.allow_zero = allow_zero;
- entry->data.pointer.vector_index = vector_index;
- entry->data.pointer.inferred_struct_field = inferred_struct_field;
- entry->data.pointer.sentinel = sentinel;
-
- append_ptr_type_attrs(&entry->name, entry);
-
- if (parent_pointer) {
- *parent_pointer = entry;
- } else {
- g->type_table.put(type_id, entry);
- }
- return entry;
-}
-
-ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_const,
- bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment,
- uint32_t bit_offset_in_host, uint32_t host_int_bytes, bool allow_zero)
-{
- return get_pointer_to_type_extra2(g, child_type, is_const, is_volatile, ptr_len,
- byte_alignment, bit_offset_in_host, host_int_bytes, allow_zero, VECTOR_INDEX_NONE, nullptr, nullptr);
-}
-
-ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const) {
- return get_pointer_to_type_extra2(g, child_type, is_const, false, PtrLenSingle, 0, 0, 0, false,
- VECTOR_INDEX_NONE, nullptr, nullptr);
-}
-
-ZigType *get_optional_type(CodeGen *g, ZigType *child_type) {
- ZigType *result = get_optional_type2(g, child_type);
- if (result == nullptr) {
- codegen_report_errors_and_exit(g);
- }
- return result;
-}
-
-ZigType *get_optional_type2(CodeGen *g, ZigType *child_type) {
- if (child_type->optional_parent != nullptr) {
- return child_type->optional_parent;
- }
-
- Error err;
- if ((err = type_resolve(g, child_type, ResolveStatusSizeKnown))) {
- return nullptr;
- }
-
- ZigType *entry = new_type_table_entry(ZigTypeIdOptional);
-
- buf_resize(&entry->name, 0);
- buf_appendf(&entry->name, "?%s", buf_ptr(&child_type->name));
-
- if (!type_has_bits(g, child_type)) {
- entry->size_in_bits = g->builtin_types.entry_bool->size_in_bits;
- entry->abi_size = g->builtin_types.entry_bool->abi_size;
- entry->abi_align = g->builtin_types.entry_bool->abi_align;
- } else if (type_is_nonnull_ptr(g, child_type) || child_type->id == ZigTypeIdErrorSet) {
- // This is an optimization but also is necessary for calling C
- // functions where all pointers are optional pointers.
- // Function types are technically pointers.
- entry->size_in_bits = child_type->size_in_bits;
- entry->abi_size = child_type->abi_size;
- entry->abi_align = child_type->abi_align;
- } else {
- // This value only matters if the type is legal in a packed struct, which is not
- // true for optional types which did not fit the above 2 categories (zero bit child type,
- // or nonnull ptr child type, or error set child type).
- entry->size_in_bits = child_type->size_in_bits + 1;
-
- // We're going to make a struct with the child type as the first field,
- // and a bool as the second. Since the child type's abi alignment is guaranteed
- // to be >= the bool's abi size (1 byte), the added size is exactly equal to the
- // child type's ABI alignment.
- assert(child_type->abi_align >= g->builtin_types.entry_bool->abi_size);
- entry->abi_align = child_type->abi_align;
- entry->abi_size = child_type->abi_size + child_type->abi_align;
- }
-
- entry->data.maybe.child_type = child_type;
- entry->data.maybe.resolve_status = ResolveStatusSizeKnown;
-
- child_type->optional_parent = entry;
- return entry;
-}
-
-static size_t align_forward(size_t addr, size_t alignment) {
- return (addr + alignment - 1) & ~(alignment - 1);
-}
-
-static size_t next_field_offset(size_t offset, size_t align_from_zero, size_t field_size, size_t next_field_align) {
- // Convert offset to a pretend address which has the specified alignment.
- size_t addr = offset + align_from_zero;
- // March the address forward to respect the field alignment.
- size_t aligned_addr = align_forward(addr + field_size, next_field_align);
- // Convert back from pretend address to offset.
- return aligned_addr - align_from_zero;
-}
-
-ZigType *get_error_union_type(CodeGen *g, ZigType *err_set_type, ZigType *payload_type) {
- assert(err_set_type->id == ZigTypeIdErrorSet);
- assert(!type_is_invalid(payload_type));
-
- TypeId type_id = {};
- type_id.id = ZigTypeIdErrorUnion;
- type_id.data.error_union.err_set_type = err_set_type;
- type_id.data.error_union.payload_type = payload_type;
-
- auto existing_entry = g->type_table.maybe_get(type_id);
- if (existing_entry) {
- return existing_entry->value;
- }
-
- ZigType *entry = new_type_table_entry(ZigTypeIdErrorUnion);
- assert(type_is_resolved(payload_type, ResolveStatusSizeKnown));
-
- buf_resize(&entry->name, 0);
- buf_appendf(&entry->name, "%s!%s", buf_ptr(&err_set_type->name), buf_ptr(&payload_type->name));
-
- entry->data.error_union.err_set_type = err_set_type;
- entry->data.error_union.payload_type = payload_type;
-
- if (!type_has_bits(g, payload_type)) {
- if (type_has_bits(g, err_set_type)) {
- entry->size_in_bits = err_set_type->size_in_bits;
- entry->abi_size = err_set_type->abi_size;
- entry->abi_align = err_set_type->abi_align;
- } else {
- entry->size_in_bits = 0;
- entry->abi_size = 0;
- entry->abi_align = 0;
- }
- } else if (!type_has_bits(g, err_set_type)) {
- entry->size_in_bits = payload_type->size_in_bits;
- entry->abi_size = payload_type->abi_size;
- entry->abi_align = payload_type->abi_align;
- } else {
- entry->abi_align = max(err_set_type->abi_align, payload_type->abi_align);
- size_t field_sizes[2];
- size_t field_aligns[2];
- field_sizes[err_union_err_index] = err_set_type->abi_size;
- field_aligns[err_union_err_index] = err_set_type->abi_align;
- field_sizes[err_union_payload_index] = payload_type->abi_size;
- field_aligns[err_union_payload_index] = payload_type->abi_align;
- size_t field2_offset = next_field_offset(0, entry->abi_align, field_sizes[0], field_aligns[1]);
- entry->abi_size = next_field_offset(field2_offset, entry->abi_align, field_sizes[1], entry->abi_align);
- entry->size_in_bits = entry->abi_size * 8;
- entry->data.error_union.pad_bytes = entry->abi_size - (field2_offset + field_sizes[1]);
- }
-
- g->type_table.put(type_id, entry);
- return entry;
-}
-
-ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size, ZigValue *sentinel) {
- Error err;
-
- TypeId type_id = {};
- type_id.id = ZigTypeIdArray;
- type_id.data.array.codegen = g;
- type_id.data.array.child_type = child_type;
- type_id.data.array.size = array_size;
- type_id.data.array.sentinel = sentinel;
- auto existing_entry = g->type_table.maybe_get(type_id);
- if (existing_entry) {
- return existing_entry->value;
- }
-
- size_t full_array_size = array_size + ((sentinel != nullptr) ? 1 : 0);
-
- if (full_array_size != 0 && (err = type_resolve(g, child_type, ResolveStatusSizeKnown))) {
- codegen_report_errors_and_exit(g);
- }
-
- ZigType *entry = new_type_table_entry(ZigTypeIdArray);
-
- buf_resize(&entry->name, 0);
- buf_appendf(&entry->name, "[%" ZIG_PRI_u64, array_size);
- if (sentinel != nullptr) {
- buf_appendf(&entry->name, ":");
- render_const_value(g, &entry->name, sentinel);
- }
- buf_appendf(&entry->name, "]%s", buf_ptr(&child_type->name));
-
- entry->size_in_bits = child_type->size_in_bits * full_array_size;
- entry->abi_align = (full_array_size == 0) ? 0 : child_type->abi_align;
- entry->abi_size = child_type->abi_size * full_array_size;
-
- entry->data.array.child_type = child_type;
- entry->data.array.len = array_size;
- entry->data.array.sentinel = sentinel;
-
- g->type_table.put(type_id, entry);
- return entry;
-}
-
-ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) {
- Error err;
- assert(ptr_type->id == ZigTypeIdPointer);
- assert(ptr_type->data.pointer.ptr_len == PtrLenUnknown);
-
- ZigType **parent_pointer = &ptr_type->data.pointer.slice_parent;
- if (*parent_pointer) {
- return *parent_pointer;
- }
-
- // We use the pointer type's abi size below, so we have to resolve it now.
- if ((err = type_resolve(g, ptr_type, ResolveStatusSizeKnown))) {
- codegen_report_errors_and_exit(g);
- }
-
- ZigType *entry = new_type_table_entry(ZigTypeIdStruct);
-
- buf_resize(&entry->name, 0);
- buf_appendf(&entry->name, "[");
- if (ptr_type->data.pointer.sentinel != nullptr) {
- buf_appendf(&entry->name, ":");
- render_const_value(g, &entry->name, ptr_type->data.pointer.sentinel);
- }
- buf_appendf(&entry->name, "]");
- append_ptr_type_attrs(&entry->name, ptr_type);
-
- unsigned element_count = 2;
- Buf *ptr_field_name = buf_create_from_str("ptr");
- Buf *len_field_name = buf_create_from_str("len");
-
- entry->data.structure.resolve_status = ResolveStatusSizeKnown;
- entry->data.structure.layout = ContainerLayoutAuto;
- entry->data.structure.special = StructSpecialSlice;
- entry->data.structure.src_field_count = element_count;
- entry->data.structure.gen_field_count = element_count;
- entry->data.structure.fields = alloc_type_struct_fields(element_count);
- entry->data.structure.fields_by_name.init(element_count);
- entry->data.structure.fields[slice_ptr_index]->name = ptr_field_name;
- entry->data.structure.fields[slice_ptr_index]->type_entry = ptr_type;
- entry->data.structure.fields[slice_ptr_index]->src_index = slice_ptr_index;
- entry->data.structure.fields[slice_ptr_index]->gen_index = 0;
- entry->data.structure.fields[slice_ptr_index]->offset = 0;
- entry->data.structure.fields[slice_len_index]->name = len_field_name;
- entry->data.structure.fields[slice_len_index]->type_entry = g->builtin_types.entry_usize;
- entry->data.structure.fields[slice_len_index]->src_index = slice_len_index;
- entry->data.structure.fields[slice_len_index]->gen_index = 1;
- entry->data.structure.fields[slice_len_index]->offset = ptr_type->abi_size;
-
- entry->data.structure.fields_by_name.put(ptr_field_name, entry->data.structure.fields[slice_ptr_index]);
- entry->data.structure.fields_by_name.put(len_field_name, entry->data.structure.fields[slice_len_index]);
-
- switch (type_requires_comptime(g, ptr_type)) {
- case ReqCompTimeInvalid:
- zig_unreachable();
- case ReqCompTimeNo:
- break;
- case ReqCompTimeYes:
- entry->data.structure.requires_comptime = true;
- }
-
- if (!type_has_bits(g, ptr_type)) {
- entry->data.structure.gen_field_count = 1;
- entry->data.structure.fields[slice_ptr_index]->gen_index = SIZE_MAX;
- entry->data.structure.fields[slice_len_index]->gen_index = 0;
- }
-
- if (type_has_bits(g, ptr_type)) {
- entry->size_in_bits = ptr_type->size_in_bits + g->builtin_types.entry_usize->size_in_bits;
- entry->abi_size = ptr_type->abi_size + g->builtin_types.entry_usize->abi_size;
- entry->abi_align = ptr_type->abi_align;
- } else {
- entry->size_in_bits = g->builtin_types.entry_usize->size_in_bits;
- entry->abi_size = g->builtin_types.entry_usize->abi_size;
- entry->abi_align = g->builtin_types.entry_usize->abi_align;
- }
-
- *parent_pointer = entry;
- return entry;
-}
-
-ZigType *get_opaque_type(CodeGen *g, Scope *scope, AstNode *source_node, const char *full_name, Buf *bare_name) {
- ZigType *entry = new_type_table_entry(ZigTypeIdOpaque);
-
- buf_init_from_str(&entry->name, full_name);
-
- ZigType *import = scope ? get_scope_import(scope) : nullptr;
- unsigned line = source_node ? (unsigned)(source_node->line + 1) : 0;
-
- entry->llvm_type = LLVMInt8Type();
- entry->llvm_di_type = ZigLLVMCreateDebugForwardDeclType(g->dbuilder,
- ZigLLVMTag_DW_structure_type(), full_name,
- import ? ZigLLVMFileToScope(import->data.structure.root_struct->di_file) : nullptr,
- import ? import->data.structure.root_struct->di_file : nullptr,
- line);
- entry->data.opaque.bare_name = bare_name;
-
- // The actual size is unknown, but the value must not be 0 because that
- // is how type_has_bits is determined.
- entry->abi_size = SIZE_MAX;
- entry->size_in_bits = SIZE_MAX;
- entry->abi_align = 1;
-
- return entry;
-}
-
-ZigType *get_bound_fn_type(CodeGen *g, ZigFn *fn_entry) {
- ZigType *fn_type = fn_entry->type_entry;
- assert(fn_type->id == ZigTypeIdFn);
- if (fn_type->data.fn.bound_fn_parent)
- return fn_type->data.fn.bound_fn_parent;
-
- ZigType *bound_fn_type = new_type_table_entry(ZigTypeIdBoundFn);
- bound_fn_type->data.bound_fn.fn_type = fn_type;
-
- buf_resize(&bound_fn_type->name, 0);
- buf_appendf(&bound_fn_type->name, "(bound %s)", buf_ptr(&fn_type->name));
-
- fn_type->data.fn.bound_fn_parent = bound_fn_type;
- return bound_fn_type;
-}
-
-const char *calling_convention_name(CallingConvention cc) {
- switch (cc) {
- case CallingConventionUnspecified: return "Unspecified";
- case CallingConventionC: return "C";
- case CallingConventionCold: return "Cold";
- case CallingConventionNaked: return "Naked";
- case CallingConventionAsync: return "Async";
- case CallingConventionInterrupt: return "Interrupt";
- case CallingConventionSignal: return "Signal";
- case CallingConventionStdcall: return "Stdcall";
- case CallingConventionFastcall: return "Fastcall";
- case CallingConventionVectorcall: return "Vectorcall";
- case CallingConventionThiscall: return "Thiscall";
- case CallingConventionAPCS: return "Apcs";
- case CallingConventionAAPCS: return "Aapcs";
- case CallingConventionAAPCSVFP: return "Aapcsvfp";
- }
- zig_unreachable();
-}
-
-bool calling_convention_allows_zig_types(CallingConvention cc) {
- switch (cc) {
- case CallingConventionUnspecified:
- case CallingConventionAsync:
- return true;
- case CallingConventionC:
- case CallingConventionCold:
- case CallingConventionNaked:
- case CallingConventionInterrupt:
- case CallingConventionSignal:
- case CallingConventionStdcall:
- case CallingConventionFastcall:
- case CallingConventionVectorcall:
- case CallingConventionThiscall:
- case CallingConventionAPCS:
- case CallingConventionAAPCS:
- case CallingConventionAAPCSVFP:
- return false;
- }
- zig_unreachable();
-}
-
-ZigType *get_stack_trace_type(CodeGen *g) {
- if (g->stack_trace_type == nullptr) {
- g->stack_trace_type = get_builtin_type(g, "StackTrace");
- assertNoError(type_resolve(g, g->stack_trace_type, ResolveStatusZeroBitsKnown));
- }
- return g->stack_trace_type;
-}
-
-bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id) {
- if (fn_type_id->cc == CallingConventionUnspecified) {
- return handle_is_ptr(g, fn_type_id->return_type);
- }
- if (fn_type_id->cc != CallingConventionC) {
- return false;
- }
- if (type_is_c_abi_int_bail(g, fn_type_id->return_type)) {
- return false;
- }
- if (g->zig_target->arch == ZigLLVM_x86 ||
- g->zig_target->arch == ZigLLVM_x86_64 ||
- target_is_arm(g->zig_target) ||
- target_is_riscv(g->zig_target) ||
- target_is_wasm(g->zig_target) ||
- target_is_ppc(g->zig_target))
- {
- X64CABIClass abi_class = type_c_abi_x86_64_class(g, fn_type_id->return_type);
- return abi_class == X64CABIClass_MEMORY || abi_class == X64CABIClass_MEMORY_nobyval;
- } else if (g->zig_target->arch == ZigLLVM_mips || g->zig_target->arch == ZigLLVM_mipsel) {
- return false;
- }
- zig_panic("TODO implement C ABI for this architecture. See https://github.com/ziglang/zig/issues/1481");
-}
-
-ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
- Error err;
- auto table_entry = g->fn_type_table.maybe_get(fn_type_id);
- if (table_entry) {
- return table_entry->value;
- }
- if (fn_type_id->return_type != nullptr) {
- if ((err = type_resolve(g, fn_type_id->return_type, ResolveStatusSizeKnown)))
- return g->builtin_types.entry_invalid;
- assert(fn_type_id->return_type->id != ZigTypeIdOpaque);
- } else {
- zig_panic("TODO implement inferred return types https://github.com/ziglang/zig/issues/447");
- }
-
- ZigType *fn_type = new_type_table_entry(ZigTypeIdFn);
- fn_type->data.fn.fn_type_id = *fn_type_id;
-
- // populate the name of the type
- buf_resize(&fn_type->name, 0);
- buf_appendf(&fn_type->name, "fn(");
- for (size_t i = 0; i < fn_type_id->param_count; i += 1) {
- FnTypeParamInfo *param_info = &fn_type_id->param_info[i];
-
- ZigType *param_type = param_info->type;
- const char *comma = (i == 0) ? "" : ", ";
- const char *noalias_str = param_info->is_noalias ? "noalias " : "";
- buf_appendf(&fn_type->name, "%s%s%s", comma, noalias_str, buf_ptr(¶m_type->name));
- }
-
- if (fn_type_id->is_var_args) {
- const char *comma = (fn_type_id->param_count == 0) ? "" : ", ";
- buf_appendf(&fn_type->name, "%s...", comma);
- }
- buf_appendf(&fn_type->name, ")");
- if (fn_type_id->alignment != 0) {
- buf_appendf(&fn_type->name, " align(%" PRIu32 ")", fn_type_id->alignment);
- }
- if (fn_type_id->cc != CallingConventionUnspecified) {
- buf_appendf(&fn_type->name, " callconv(.%s)", calling_convention_name(fn_type_id->cc));
- }
- buf_appendf(&fn_type->name, " %s", buf_ptr(&fn_type_id->return_type->name));
-
- // The fn_type is a pointer; not to be confused with the raw function type.
- fn_type->size_in_bits = g->builtin_types.entry_usize->size_in_bits;
- fn_type->abi_size = g->builtin_types.entry_usize->abi_size;
- fn_type->abi_align = g->builtin_types.entry_usize->abi_align;
-
- g->fn_type_table.put(&fn_type->data.fn.fn_type_id, fn_type);
-
- return fn_type;
-}
-
-static ZigTypeId container_to_type(ContainerKind kind) {
- switch (kind) {
- case ContainerKindStruct:
- return ZigTypeIdStruct;
- case ContainerKindEnum:
- return ZigTypeIdEnum;
- case ContainerKindUnion:
- return ZigTypeIdUnion;
- }
- zig_unreachable();
-}
-
-// This is like get_partial_container_type except it's for the implicit root struct of files.
-static ZigType *get_root_container_type(CodeGen *g, const char *full_name, Buf *bare_name,
- RootStruct *root_struct)
-{
- ZigType *entry = new_type_table_entry(ZigTypeIdStruct);
- entry->data.structure.decls_scope = create_decls_scope(g, nullptr, nullptr, entry, entry, bare_name);
- entry->data.structure.root_struct = root_struct;
- entry->data.structure.layout = ContainerLayoutAuto;
-
- if (full_name[0] == '\0') {
- buf_init_from_str(&entry->name, "(root)");
- } else {
- buf_init_from_str(&entry->name, full_name);
- }
-
- return entry;
-}
-
-ZigType *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind,
- AstNode *decl_node, const char *full_name, Buf *bare_name, ContainerLayout layout)
-{
- ZigTypeId type_id = container_to_type(kind);
- ZigType *entry = new_container_type_entry(g, type_id, decl_node, scope, bare_name);
-
- switch (kind) {
- case ContainerKindStruct:
- entry->data.structure.decl_node = decl_node;
- entry->data.structure.layout = layout;
- break;
- case ContainerKindEnum:
- entry->data.enumeration.decl_node = decl_node;
- entry->data.enumeration.layout = layout;
- break;
- case ContainerKindUnion:
- entry->data.unionation.decl_node = decl_node;
- entry->data.unionation.layout = layout;
- break;
- }
-
- buf_init_from_str(&entry->name, full_name);
-
- return entry;
-}
-
-ZigValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *type_entry,
- Buf *type_name, UndefAllowed undef)
-{
- Error err;
-
- ZigValue *result = g->pass1_arena->create();
- ZigValue *result_ptr = g->pass1_arena->create();
- result->special = ConstValSpecialUndef;
- result->type = (type_entry == nullptr) ? g->builtin_types.entry_anytype : type_entry;
- result_ptr->special = ConstValSpecialStatic;
- result_ptr->type = get_pointer_to_type(g, result->type, false);
- result_ptr->data.x_ptr.mut = ConstPtrMutComptimeVar;
- result_ptr->data.x_ptr.special = ConstPtrSpecialRef;
- result_ptr->data.x_ptr.data.ref.pointee = result;
-
- size_t backward_branch_count = 0;
- size_t backward_branch_quota = default_backward_branch_quota;
- if ((err = ir_eval_const_value(g, scope, node, result_ptr,
- &backward_branch_count, &backward_branch_quota,
- nullptr, nullptr, node, type_name, nullptr, nullptr, undef)))
- {
- return g->invalid_inst_gen->value;
- }
- return result;
-}
-
-Error type_val_resolve_zero_bits(CodeGen *g, ZigValue *type_val, ZigType *parent_type,
- ZigValue *parent_type_val, bool *is_zero_bits)
-{
- Error err;
- if (type_val->special != ConstValSpecialLazy) {
- assert(type_val->special == ConstValSpecialStatic);
-
- // Self-referencing types via pointers are allowed and have non-zero size
- ZigType *ty = type_val->data.x_type;
- while (ty->id == ZigTypeIdPointer &&
- !ty->data.pointer.resolve_loop_flag_zero_bits)
- {
- ty = ty->data.pointer.child_type;
- }
-
- if ((ty->id == ZigTypeIdStruct && ty->data.structure.resolve_loop_flag_zero_bits) ||
- (ty->id == ZigTypeIdUnion && ty->data.unionation.resolve_loop_flag_zero_bits) ||
- (ty->id == ZigTypeIdPointer && ty->data.pointer.resolve_loop_flag_zero_bits))
- {
- *is_zero_bits = false;
- return ErrorNone;
- }
-
- if ((err = type_resolve(g, type_val->data.x_type, ResolveStatusZeroBitsKnown)))
- return err;
-
- *is_zero_bits = (type_val->data.x_type->abi_size == 0);
- return ErrorNone;
- }
- switch (type_val->data.x_lazy->id) {
- case LazyValueIdInvalid:
- case LazyValueIdAlignOf:
- case LazyValueIdSizeOf:
- case LazyValueIdTypeInfoDecls:
- zig_unreachable();
- case LazyValueIdPtrType: {
- LazyValuePtrType *lazy_ptr_type = reinterpret_cast(type_val->data.x_lazy);
-
- if (parent_type_val == lazy_ptr_type->elem_type->value) {
- // Does a struct which contains a pointer field to itself have bits? Yes.
- *is_zero_bits = false;
- return ErrorNone;
- } else {
- if (parent_type_val == nullptr) {
- parent_type_val = type_val;
- }
- return type_val_resolve_zero_bits(g, lazy_ptr_type->elem_type->value, parent_type,
- parent_type_val, is_zero_bits);
- }
- }
- case LazyValueIdArrayType: {
- LazyValueArrayType *lazy_array_type =
- reinterpret_cast(type_val->data.x_lazy);
-
- // The sentinel counts as an extra element
- if (lazy_array_type->length == 0 && lazy_array_type->sentinel == nullptr) {
- *is_zero_bits = true;
- return ErrorNone;
- }
-
- if ((err = type_val_resolve_zero_bits(g, lazy_array_type->elem_type->value,
- parent_type, nullptr, is_zero_bits)))
- return err;
-
- return ErrorNone;
- }
- case LazyValueIdOptType:
- case LazyValueIdSliceType:
- case LazyValueIdErrUnionType:
- *is_zero_bits = false;
- return ErrorNone;
- case LazyValueIdFnType: {
- LazyValueFnType *lazy_fn_type = reinterpret_cast(type_val->data.x_lazy);
- *is_zero_bits = lazy_fn_type->is_generic;
- return ErrorNone;
- }
- }
- zig_unreachable();
-}
-
-Error type_val_resolve_is_opaque_type(CodeGen *g, ZigValue *type_val, bool *is_opaque_type) {
- if (type_val->special != ConstValSpecialLazy) {
- assert(type_val->special == ConstValSpecialStatic);
- if (type_val->data.x_type == g->builtin_types.entry_anytype) {
- *is_opaque_type = false;
- return ErrorNone;
- }
- *is_opaque_type = (type_val->data.x_type->id == ZigTypeIdOpaque);
- return ErrorNone;
- }
- switch (type_val->data.x_lazy->id) {
- case LazyValueIdInvalid:
- case LazyValueIdAlignOf:
- case LazyValueIdSizeOf:
- case LazyValueIdTypeInfoDecls:
- zig_unreachable();
- case LazyValueIdSliceType:
- case LazyValueIdPtrType:
- case LazyValueIdFnType:
- case LazyValueIdOptType:
- case LazyValueIdErrUnionType:
- case LazyValueIdArrayType:
- *is_opaque_type = false;
- return ErrorNone;
- }
- zig_unreachable();
-}
-
-static ReqCompTime type_val_resolve_requires_comptime(CodeGen *g, ZigValue *type_val) {
- if (type_val->special != ConstValSpecialLazy) {
- return type_requires_comptime(g, type_val->data.x_type);
- }
- switch (type_val->data.x_lazy->id) {
- case LazyValueIdInvalid:
- case LazyValueIdAlignOf:
- case LazyValueIdSizeOf:
- case LazyValueIdTypeInfoDecls:
- zig_unreachable();
- case LazyValueIdSliceType: {
- LazyValueSliceType *lazy_slice_type = reinterpret_cast(type_val->data.x_lazy);
- return type_val_resolve_requires_comptime(g, lazy_slice_type->elem_type->value);
- }
- case LazyValueIdPtrType: {
- LazyValuePtrType *lazy_ptr_type = reinterpret_cast(type_val->data.x_lazy);
- return type_val_resolve_requires_comptime(g, lazy_ptr_type->elem_type->value);
- }
- case LazyValueIdOptType: {
- LazyValueOptType *lazy_opt_type = reinterpret_cast(type_val->data.x_lazy);
- return type_val_resolve_requires_comptime(g, lazy_opt_type->payload_type->value);
- }
- case LazyValueIdArrayType: {
- LazyValueArrayType *lazy_array_type = reinterpret_cast(type_val->data.x_lazy);
- return type_val_resolve_requires_comptime(g, lazy_array_type->elem_type->value);
- }
- case LazyValueIdFnType: {
- LazyValueFnType *lazy_fn_type = reinterpret_cast(type_val->data.x_lazy);
- if (lazy_fn_type->is_generic)
- return ReqCompTimeYes;
- switch (type_val_resolve_requires_comptime(g, lazy_fn_type->return_type->value)) {
- case ReqCompTimeInvalid:
- return ReqCompTimeInvalid;
- case ReqCompTimeYes:
- return ReqCompTimeYes;
- case ReqCompTimeNo:
- break;
- }
- size_t param_count = lazy_fn_type->proto_node->data.fn_proto.params.length;
- for (size_t i = 0; i < param_count; i += 1) {
- AstNode *param_node = lazy_fn_type->proto_node->data.fn_proto.params.at(i);
- bool param_is_var_args = param_node->data.param_decl.is_var_args;
- if (param_is_var_args) break;
- switch (type_val_resolve_requires_comptime(g, lazy_fn_type->param_types[i]->value)) {
- case ReqCompTimeInvalid:
- return ReqCompTimeInvalid;
- case ReqCompTimeYes:
- return ReqCompTimeYes;
- case ReqCompTimeNo:
- break;
- }
- }
- return ReqCompTimeNo;
- }
- case LazyValueIdErrUnionType: {
- LazyValueErrUnionType *lazy_err_union_type =
- reinterpret_cast(type_val->data.x_lazy);
- return type_val_resolve_requires_comptime(g, lazy_err_union_type->payload_type->value);
- }
- }
- zig_unreachable();
-}
-
-Error type_val_resolve_abi_size(CodeGen *g, AstNode *source_node, ZigValue *type_val,
- size_t *abi_size, size_t *size_in_bits)
-{
- Error err;
-
-start_over:
- if (type_val->special != ConstValSpecialLazy) {
- assert(type_val->special == ConstValSpecialStatic);
- ZigType *ty = type_val->data.x_type;
- if ((err = type_resolve(g, ty, ResolveStatusSizeKnown)))
- return err;
- *abi_size = ty->abi_size;
- *size_in_bits = ty->size_in_bits;
- return ErrorNone;
- }
- switch (type_val->data.x_lazy->id) {
- case LazyValueIdInvalid:
- case LazyValueIdAlignOf:
- case LazyValueIdSizeOf:
- case LazyValueIdTypeInfoDecls:
- zig_unreachable();
- case LazyValueIdSliceType: {
- LazyValueSliceType *lazy_slice_type = reinterpret_cast(type_val->data.x_lazy);
- bool is_zero_bits;
- if ((err = type_val_resolve_zero_bits(g, lazy_slice_type->elem_type->value, nullptr,
- nullptr, &is_zero_bits)))
- {
- return err;
- }
- if (is_zero_bits) {
- *abi_size = g->builtin_types.entry_usize->abi_size;
- *size_in_bits = g->builtin_types.entry_usize->size_in_bits;
- } else {
- *abi_size = g->builtin_types.entry_usize->abi_size * 2;
- *size_in_bits = g->builtin_types.entry_usize->size_in_bits * 2;
- }
- return ErrorNone;
- }
- case LazyValueIdPtrType: {
- LazyValuePtrType *lazy_ptr_type = reinterpret_cast(type_val->data.x_lazy);
- bool is_zero_bits;
- if ((err = type_val_resolve_zero_bits(g, lazy_ptr_type->elem_type->value, nullptr,
- nullptr, &is_zero_bits)))
- {
- return err;
- }
- if (is_zero_bits) {
- *abi_size = 0;
- *size_in_bits = 0;
- } else {
- *abi_size = g->builtin_types.entry_usize->abi_size;
- *size_in_bits = g->builtin_types.entry_usize->size_in_bits;
- }
- return ErrorNone;
- }
- case LazyValueIdFnType:
- *abi_size = g->builtin_types.entry_usize->abi_size;
- *size_in_bits = g->builtin_types.entry_usize->size_in_bits;
- return ErrorNone;
- case LazyValueIdOptType:
- case LazyValueIdErrUnionType:
- case LazyValueIdArrayType:
- if ((err = ir_resolve_lazy(g, source_node, type_val)))
- return err;
- goto start_over;
- }
- zig_unreachable();
-}
-
-Error type_val_resolve_abi_align(CodeGen *g, AstNode *source_node, ZigValue *type_val, uint32_t *abi_align) {
- Error err;
- if (type_val->special != ConstValSpecialLazy) {
- assert(type_val->special == ConstValSpecialStatic);
- ZigType *ty = type_val->data.x_type;
- if (ty->id == ZigTypeIdPointer) {
- *abi_align = g->builtin_types.entry_usize->abi_align;
- return ErrorNone;
- }
- if ((err = type_resolve(g, ty, ResolveStatusAlignmentKnown)))
- return err;
- *abi_align = ty->abi_align;
- return ErrorNone;
- }
- switch (type_val->data.x_lazy->id) {
- case LazyValueIdInvalid:
- case LazyValueIdAlignOf:
- case LazyValueIdSizeOf:
- case LazyValueIdTypeInfoDecls:
- zig_unreachable();
- case LazyValueIdSliceType:
- case LazyValueIdPtrType:
- case LazyValueIdFnType:
- *abi_align = g->builtin_types.entry_usize->abi_align;
- return ErrorNone;
- case LazyValueIdOptType: {
- if ((err = ir_resolve_lazy(g, nullptr, type_val)))
- return err;
-
- return type_val_resolve_abi_align(g, source_node, type_val, abi_align);
- }
- case LazyValueIdArrayType: {
- LazyValueArrayType *lazy_array_type =
- reinterpret_cast(type_val->data.x_lazy);
- return type_val_resolve_abi_align(g, source_node, lazy_array_type->elem_type->value, abi_align);
- }
- case LazyValueIdErrUnionType: {
- LazyValueErrUnionType *lazy_err_union_type =
- reinterpret_cast(type_val->data.x_lazy);
- uint32_t payload_abi_align;
- if ((err = type_val_resolve_abi_align(g, source_node, lazy_err_union_type->payload_type->value,
- &payload_abi_align)))
- {
- return err;
- }
- *abi_align = (payload_abi_align > g->err_tag_type->abi_align) ?
- payload_abi_align : g->err_tag_type->abi_align;
- return ErrorNone;
- }
- }
- zig_unreachable();
-}
-
-static OnePossibleValue type_val_resolve_has_one_possible_value(CodeGen *g, ZigValue *type_val) {
- if (type_val->special != ConstValSpecialLazy) {
- return type_has_one_possible_value(g, type_val->data.x_type);
- }
- switch (type_val->data.x_lazy->id) {
- case LazyValueIdInvalid:
- case LazyValueIdAlignOf:
- case LazyValueIdSizeOf:
- case LazyValueIdTypeInfoDecls:
- zig_unreachable();
- case LazyValueIdSliceType: // it has the len field
- case LazyValueIdOptType: // it has the optional bit
- case LazyValueIdFnType:
- return OnePossibleValueNo;
- case LazyValueIdArrayType: {
- LazyValueArrayType *lazy_array_type =
- reinterpret_cast(type_val->data.x_lazy);
- if (lazy_array_type->length == 0)
- return OnePossibleValueYes;
- return type_val_resolve_has_one_possible_value(g, lazy_array_type->elem_type->value);
- }
- case LazyValueIdPtrType: {
- Error err;
- bool zero_bits;
- if ((err = type_val_resolve_zero_bits(g, type_val, nullptr, nullptr, &zero_bits))) {
- return OnePossibleValueInvalid;
- }
- if (zero_bits) {
- return OnePossibleValueYes;
- } else {
- return OnePossibleValueNo;
- }
- }
- case LazyValueIdErrUnionType: {
- LazyValueErrUnionType *lazy_err_union_type =
- reinterpret_cast(type_val->data.x_lazy);
- switch (type_val_resolve_has_one_possible_value(g, lazy_err_union_type->err_set_type->value)) {
- case OnePossibleValueInvalid:
- return OnePossibleValueInvalid;
- case OnePossibleValueNo:
- return OnePossibleValueNo;
- case OnePossibleValueYes:
- return type_val_resolve_has_one_possible_value(g, lazy_err_union_type->payload_type->value);
- }
- }
- }
- zig_unreachable();
-}
-
-ZigType *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node) {
- Error err;
- // Hot path for simple identifiers, to avoid unnecessary memory allocations.
- if (node->type == NodeTypeSymbol) {
- Buf *variable_name = node->data.symbol_expr.symbol;
- if (buf_eql_str(variable_name, "_"))
- goto abort_hot_path;
- ZigType *primitive_type;
- if ((err = get_primitive_type(g, variable_name, &primitive_type))) {
- goto abort_hot_path;
- } else {
- return primitive_type;
- }
-abort_hot_path:;
- }
- ZigValue *result = analyze_const_value(g, scope, node, g->builtin_types.entry_type,
- nullptr, UndefBad);
- if (type_is_invalid(result->type))
- return g->builtin_types.entry_invalid;
- src_assert(result->special == ConstValSpecialStatic, node);
- src_assert(result->data.x_type != nullptr, node);
- return result->data.x_type;
-}
-
-ZigType *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
- ZigType *fn_type = new_type_table_entry(ZigTypeIdFn);
- buf_resize(&fn_type->name, 0);
- buf_appendf(&fn_type->name, "fn(");
- size_t i = 0;
- for (; i < fn_type_id->next_param_index; i += 1) {
- const char *comma_str = (i == 0) ? "" : ",";
- buf_appendf(&fn_type->name, "%s%s", comma_str,
- buf_ptr(&fn_type_id->param_info[i].type->name));
- }
- for (; i < fn_type_id->param_count; i += 1) {
- const char *comma_str = (i == 0) ? "" : ",";
- buf_appendf(&fn_type->name, "%sanytype", comma_str);
- }
- buf_append_str(&fn_type->name, ")");
- if (fn_type_id->cc != CallingConventionUnspecified) {
- buf_appendf(&fn_type->name, " callconv(.%s)", calling_convention_name(fn_type_id->cc));
- }
- buf_append_str(&fn_type->name, " anytype");
-
- fn_type->data.fn.fn_type_id = *fn_type_id;
- fn_type->data.fn.is_generic = true;
- fn_type->abi_size = 0;
- fn_type->size_in_bits = 0;
- fn_type->abi_align = 0;
- return fn_type;
-}
-
-CallingConvention cc_from_fn_proto(AstNodeFnProto *fn_proto) {
- // Compatible with the C ABI
- if (fn_proto->is_extern || fn_proto->is_export)
- return CallingConventionC;
-
- return CallingConventionUnspecified;
-}
-
-void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, CallingConvention cc, size_t param_count_alloc) {
- assert(proto_node->type == NodeTypeFnProto);
- AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
-
- fn_type_id->cc = cc;
- fn_type_id->param_count = fn_proto->params.length;
- fn_type_id->param_info = heap::c_allocator.allocate(param_count_alloc);
- fn_type_id->next_param_index = 0;
- fn_type_id->is_var_args = fn_proto->is_var_args;
-}
-
-static bool analyze_const_align(CodeGen *g, Scope *scope, AstNode *node, uint32_t *result) {
- ZigValue *align_result = analyze_const_value(g, scope, node, get_align_amt_type(g),
- nullptr, UndefBad);
- if (type_is_invalid(align_result->type))
- return false;
-
- uint32_t align_bytes = bigint_as_u32(&align_result->data.x_bigint);
- if (align_bytes == 0) {
- add_node_error(g, node, buf_sprintf("alignment must be >= 1"));
- return false;
- }
- if (!is_power_of_2(align_bytes)) {
- add_node_error(g, node, buf_sprintf("alignment value %" PRIu32 " is not a power of 2", align_bytes));
- return false;
- }
-
- *result = align_bytes;
- return true;
-}
-
-static bool analyze_const_string(CodeGen *g, Scope *scope, AstNode *node, Buf **out_buffer) {
- ZigType *ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false,
- PtrLenUnknown, 0, 0, 0, false);
- ZigType *str_type = get_slice_type(g, ptr_type);
- ZigValue *result_val = analyze_const_value(g, scope, node, str_type, nullptr, UndefBad);
- if (type_is_invalid(result_val->type))
- return false;
-
- ZigValue *ptr_field = result_val->data.x_struct.fields[slice_ptr_index];
- ZigValue *len_field = result_val->data.x_struct.fields[slice_len_index];
-
- assert(ptr_field->data.x_ptr.special == ConstPtrSpecialBaseArray);
- ZigValue *array_val = ptr_field->data.x_ptr.data.base_array.array_val;
- if (array_val->data.x_array.special == ConstArraySpecialBuf) {
- *out_buffer = array_val->data.x_array.data.s_buf;
- return true;
- }
- expand_undef_array(g, array_val);
- size_t len = bigint_as_usize(&len_field->data.x_bigint);
- Buf *result = buf_alloc();
- buf_resize(result, len);
- for (size_t i = 0; i < len; i += 1) {
- size_t new_index = ptr_field->data.x_ptr.data.base_array.elem_index + i;
- ZigValue *char_val = &array_val->data.x_array.data.s_none.elements[new_index];
- if (char_val->special == ConstValSpecialUndef) {
- add_node_error(g, node, buf_sprintf("use of undefined value"));
- return false;
- }
- uint64_t big_c = bigint_as_u64(&char_val->data.x_bigint);
- assert(big_c <= UINT8_MAX);
- uint8_t c = (uint8_t)big_c;
- buf_ptr(result)[i] = c;
- }
- *out_buffer = result;
- return true;
-}
-
-static Error emit_error_unless_type_allowed_in_packed_container(CodeGen *g, ZigType *type_entry,
- AstNode *source_node, const char* container_name)
-{
- Error err;
- switch (type_entry->id) {
- case ZigTypeIdInvalid:
- zig_unreachable();
- case ZigTypeIdMetaType:
- case ZigTypeIdUnreachable:
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdEnumLiteral:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdErrorUnion:
- case ZigTypeIdErrorSet:
- case ZigTypeIdBoundFn:
- case ZigTypeIdOpaque:
- case ZigTypeIdFnFrame:
- case ZigTypeIdAnyFrame:
- add_node_error(g, source_node,
- buf_sprintf("type '%s' not allowed in packed %s; no guaranteed in-memory representation",
- buf_ptr(&type_entry->name), container_name));
- return ErrorSemanticAnalyzeFail;
- case ZigTypeIdVoid:
- case ZigTypeIdBool:
- case ZigTypeIdInt:
- case ZigTypeIdFloat:
- case ZigTypeIdPointer:
- case ZigTypeIdFn:
- case ZigTypeIdVector:
- return ErrorNone;
- case ZigTypeIdArray: {
- ZigType *elem_type = type_entry->data.array.child_type;
- if ((err = emit_error_unless_type_allowed_in_packed_container(g, elem_type, source_node, container_name)))
- return err;
- // TODO revisit this when doing https://github.com/ziglang/zig/issues/1512
- if (type_size(g, type_entry) * 8 == type_size_bits(g, type_entry))
- return ErrorNone;
- add_node_error(g, source_node,
- buf_sprintf("array of '%s' not allowed in packed %s due to padding bits",
- buf_ptr(&elem_type->name), container_name));
- return ErrorSemanticAnalyzeFail;
- }
- case ZigTypeIdStruct:
- switch (type_entry->data.structure.layout) {
- case ContainerLayoutPacked:
- case ContainerLayoutExtern:
- return ErrorNone;
- case ContainerLayoutAuto:
- add_node_error(g, source_node,
- buf_sprintf("non-packed, non-extern struct '%s' not allowed in packed %s; no guaranteed in-memory representation",
- buf_ptr(&type_entry->name), container_name));
- return ErrorSemanticAnalyzeFail;
- }
- zig_unreachable();
- case ZigTypeIdUnion:
- switch (type_entry->data.unionation.layout) {
- case ContainerLayoutPacked:
- case ContainerLayoutExtern:
- return ErrorNone;
- case ContainerLayoutAuto:
- add_node_error(g, source_node,
- buf_sprintf("non-packed, non-extern union '%s' not allowed in packed %s; no guaranteed in-memory representation",
- buf_ptr(&type_entry->name), container_name));
- return ErrorSemanticAnalyzeFail;
- }
- zig_unreachable();
- case ZigTypeIdOptional: {
- ZigType *ptr_type;
- if ((err = get_codegen_ptr_type(g, type_entry, &ptr_type))) return err;
- if (ptr_type != nullptr) return ErrorNone;
-
- add_node_error(g, source_node,
- buf_sprintf("type '%s' not allowed in packed %s; no guaranteed in-memory representation",
- buf_ptr(&type_entry->name), container_name));
- return ErrorSemanticAnalyzeFail;
- }
- case ZigTypeIdEnum: {
- AstNode *decl_node = type_entry->data.enumeration.decl_node;
- if (decl_node->data.container_decl.init_arg_expr != nullptr) {
- return ErrorNone;
- }
- ErrorMsg *msg = add_node_error(g, source_node,
- buf_sprintf("type '%s' not allowed in packed %s; no guaranteed in-memory representation",
- buf_ptr(&type_entry->name), container_name));
- add_error_note(g, msg, decl_node,
- buf_sprintf("enum declaration does not specify an integer tag type"));
- return ErrorSemanticAnalyzeFail;
- }
- }
- zig_unreachable();
-}
-
-static Error emit_error_unless_type_allowed_in_packed_struct(CodeGen *g, ZigType *type_entry,
- AstNode *source_node)
-{
- return emit_error_unless_type_allowed_in_packed_container(g, type_entry, source_node, "struct");
-}
-
-static Error emit_error_unless_type_allowed_in_packed_union(CodeGen *g, ZigType *type_entry,
- AstNode *source_node)
-{
- return emit_error_unless_type_allowed_in_packed_container(g, type_entry, source_node, "union");
-}
-
-Error type_allowed_in_extern(CodeGen *g, ZigType *type_entry, bool *result) {
- Error err;
- switch (type_entry->id) {
- case ZigTypeIdInvalid:
- zig_unreachable();
- case ZigTypeIdMetaType:
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdEnumLiteral:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdErrorUnion:
- case ZigTypeIdErrorSet:
- case ZigTypeIdBoundFn:
- case ZigTypeIdVoid:
- case ZigTypeIdFnFrame:
- case ZigTypeIdAnyFrame:
- *result = false;
- return ErrorNone;
- case ZigTypeIdOpaque:
- case ZigTypeIdUnreachable:
- case ZigTypeIdBool:
- *result = true;
- return ErrorNone;
- case ZigTypeIdInt:
- switch (type_entry->data.integral.bit_count) {
- case 8:
- case 16:
- case 32:
- case 64:
- case 128:
- *result = true;
- return ErrorNone;
- default:
- *result = false;
- return ErrorNone;
- }
- case ZigTypeIdVector:
- return type_allowed_in_extern(g, type_entry->data.vector.elem_type, result);
- case ZigTypeIdFloat:
- *result = true;
- return ErrorNone;
- case ZigTypeIdArray:
- return type_allowed_in_extern(g, type_entry->data.array.child_type, result);
- case ZigTypeIdFn:
- *result = !calling_convention_allows_zig_types(type_entry->data.fn.fn_type_id.cc);
- return ErrorNone;
- case ZigTypeIdPointer:
- if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown)))
- return err;
- if (!type_has_bits(g, type_entry)) {
- *result = false;
- return ErrorNone;
- }
- *result = true;
- return ErrorNone;
- case ZigTypeIdStruct:
- *result = type_entry->data.structure.layout == ContainerLayoutExtern ||
- type_entry->data.structure.layout == ContainerLayoutPacked;
- return ErrorNone;
- case ZigTypeIdOptional: {
- ZigType *child_type = type_entry->data.maybe.child_type;
- if (child_type->id != ZigTypeIdPointer && child_type->id != ZigTypeIdFn) {
- *result = false;
- return ErrorNone;
- }
- if (!type_is_nonnull_ptr(g, child_type)) {
- *result = false;
- return ErrorNone;
- }
- return type_allowed_in_extern(g, child_type, result);
- }
- case ZigTypeIdEnum:
- *result = type_entry->data.enumeration.layout == ContainerLayoutExtern ||
- type_entry->data.enumeration.layout == ContainerLayoutPacked;
- return ErrorNone;
- case ZigTypeIdUnion:
- *result = type_entry->data.unionation.layout == ContainerLayoutExtern ||
- type_entry->data.unionation.layout == ContainerLayoutPacked;
- return ErrorNone;
- }
- zig_unreachable();
-}
-
-ZigType *get_auto_err_set_type(CodeGen *g, ZigFn *fn_entry) {
- ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet);
- buf_resize(&err_set_type->name, 0);
- buf_appendf(&err_set_type->name, "@typeInfo(@typeInfo(@TypeOf(%s)).Fn.return_type.?).ErrorUnion.error_set", buf_ptr(&fn_entry->symbol_name));
- err_set_type->data.error_set.err_count = 0;
- err_set_type->data.error_set.errors = nullptr;
- err_set_type->data.error_set.infer_fn = fn_entry;
- err_set_type->data.error_set.incomplete = true;
- err_set_type->size_in_bits = g->builtin_types.entry_global_error_set->size_in_bits;
- err_set_type->abi_align = g->builtin_types.entry_global_error_set->abi_align;
- err_set_type->abi_size = g->builtin_types.entry_global_error_set->abi_size;
-
- return err_set_type;
-}
-
-static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_scope, ZigFn *fn_entry,
- CallingConvention cc)
-{
- assert(proto_node->type == NodeTypeFnProto);
- AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
- Error err;
-
- FnTypeId fn_type_id = {0};
- init_fn_type_id(&fn_type_id, proto_node, cc, proto_node->data.fn_proto.params.length);
-
- for (; fn_type_id.next_param_index < fn_type_id.param_count; fn_type_id.next_param_index += 1) {
- AstNode *param_node = fn_proto->params.at(fn_type_id.next_param_index);
- assert(param_node->type == NodeTypeParamDecl);
-
- bool param_is_comptime = param_node->data.param_decl.is_comptime;
- bool param_is_var_args = param_node->data.param_decl.is_var_args;
-
- if (param_is_comptime) {
- if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
- add_node_error(g, param_node,
- buf_sprintf("comptime parameter not allowed in function with calling convention '%s'",
- calling_convention_name(fn_type_id.cc)));
- return g->builtin_types.entry_invalid;
- }
- if (param_node->data.param_decl.type != nullptr) {
- ZigType *type_entry = analyze_type_expr(g, child_scope, param_node->data.param_decl.type);
- if (type_is_invalid(type_entry)) {
- return g->builtin_types.entry_invalid;
- }
- FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index];
- param_info->type = type_entry;
- param_info->is_noalias = param_node->data.param_decl.is_noalias;
- fn_type_id.next_param_index += 1;
- }
-
- return get_generic_fn_type(g, &fn_type_id);
- } else if (param_is_var_args) {
- if (fn_type_id.cc == CallingConventionC) {
- fn_type_id.param_count = fn_type_id.next_param_index;
- continue;
- } else {
- add_node_error(g, param_node,
- buf_sprintf("var args only allowed in functions with C calling convention"));
- return g->builtin_types.entry_invalid;
- }
- } else if (param_node->data.param_decl.anytype_token != nullptr) {
- if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
- add_node_error(g, param_node,
- buf_sprintf("parameter of type 'anytype' not allowed in function with calling convention '%s'",
- calling_convention_name(fn_type_id.cc)));
- return g->builtin_types.entry_invalid;
- }
- return get_generic_fn_type(g, &fn_type_id);
- }
-
- ZigType *type_entry = analyze_type_expr(g, child_scope, param_node->data.param_decl.type);
- if (type_is_invalid(type_entry)) {
- return g->builtin_types.entry_invalid;
- }
- if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
- if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown)))
- return g->builtin_types.entry_invalid;
- if (!type_has_bits(g, type_entry)) {
- add_node_error(g, param_node->data.param_decl.type,
- buf_sprintf("parameter of type '%s' has 0 bits; not allowed in function with calling convention '%s'",
- buf_ptr(&type_entry->name), calling_convention_name(fn_type_id.cc)));
- return g->builtin_types.entry_invalid;
- }
- }
-
- if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
- bool ok_type;
- if ((err = type_allowed_in_extern(g, type_entry, &ok_type)))
- return g->builtin_types.entry_invalid;
- if (!ok_type) {
- add_node_error(g, param_node->data.param_decl.type,
- buf_sprintf("parameter of type '%s' not allowed in function with calling convention '%s'",
- buf_ptr(&type_entry->name),
- calling_convention_name(fn_type_id.cc)));
- return g->builtin_types.entry_invalid;
- }
- }
-
- if(!is_valid_param_type(type_entry)){
- if(type_entry->id == ZigTypeIdOpaque){
- add_node_error(g, param_node->data.param_decl.type,
- buf_sprintf("parameter of opaque type '%s' not allowed", buf_ptr(&type_entry->name)));
- } else {
- add_node_error(g, param_node->data.param_decl.type,
- buf_sprintf("parameter of type '%s' not allowed", buf_ptr(&type_entry->name)));
- }
-
- return g->builtin_types.entry_invalid;
- }
-
- switch (type_requires_comptime(g, type_entry)) {
- case ReqCompTimeNo:
- break;
- case ReqCompTimeYes:
- add_node_error(g, param_node->data.param_decl.type,
- buf_sprintf("parameter of type '%s' must be declared comptime",
- buf_ptr(&type_entry->name)));
- return g->builtin_types.entry_invalid;
- case ReqCompTimeInvalid:
- return g->builtin_types.entry_invalid;
- }
-
- FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index];
- param_info->type = type_entry;
- param_info->is_noalias = param_node->data.param_decl.is_noalias;
- }
-
- if (fn_proto->align_expr != nullptr) {
- if (target_is_wasm(g->zig_target)) {
- // In Wasm, specifying alignment of function pointers makes little sense
- // since function pointers are in fact indices to a Wasm table, therefore
- // any alignment check on those is invalid. This can cause unexpected
- // behaviour when checking expected alignment with `@ptrToInt(fn_ptr)`
- // or similar. This commit proposes to make `align` expressions a
- // compile error when compiled to Wasm architecture.
- //
- // Some references:
- // [1] [Mozilla: WebAssembly Tables](https://developer.mozilla.org/en-US/docs/WebAssembly/Understanding_the_text_format#WebAssembly_tables)
- // [2] [Sunfishcode's Wasm Ref Manual](https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#indirect-call)
- add_node_error(g, fn_proto->align_expr,
- buf_sprintf("align(N) expr is not allowed on function prototypes in wasm32/wasm64"));
- return g->builtin_types.entry_invalid;
- }
- if (!analyze_const_align(g, child_scope, fn_proto->align_expr, &fn_type_id.alignment)) {
- return g->builtin_types.entry_invalid;
- }
- fn_entry->align_bytes = fn_type_id.alignment;
- }
-
- if (fn_proto->return_anytype_token != nullptr) {
- if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
- add_node_error(g, fn_proto->return_type,
- buf_sprintf("return type 'anytype' not allowed in function with calling convention '%s'",
- calling_convention_name(fn_type_id.cc)));
- return g->builtin_types.entry_invalid;
- }
- add_node_error(g, proto_node,
- buf_sprintf("TODO implement inferred return types https://github.com/ziglang/zig/issues/447"));
- return g->builtin_types.entry_invalid;
- }
-
- ZigType *specified_return_type = analyze_type_expr(g, child_scope, fn_proto->return_type);
- if (type_is_invalid(specified_return_type)) {
- fn_type_id.return_type = g->builtin_types.entry_invalid;
- return g->builtin_types.entry_invalid;
- }
-
- if(!is_valid_return_type(specified_return_type)){
- ErrorMsg* msg = add_node_error(g, fn_proto->return_type,
- buf_sprintf("%s return type '%s' not allowed", type_id_name(specified_return_type->id), buf_ptr(&specified_return_type->name)));
- Tld *tld = find_decl(g, &fn_entry->fndef_scope->base, &specified_return_type->name);
- if (tld != nullptr) {
- add_error_note(g, msg, tld->source_node, buf_sprintf("type declared here"));
- }
- return g->builtin_types.entry_invalid;
- }
-
- if (fn_proto->auto_err_set) {
- ZigType *inferred_err_set_type = get_auto_err_set_type(g, fn_entry);
- if ((err = type_resolve(g, specified_return_type, ResolveStatusSizeKnown)))
- return g->builtin_types.entry_invalid;
- fn_type_id.return_type = get_error_union_type(g, inferred_err_set_type, specified_return_type);
- } else {
- fn_type_id.return_type = specified_return_type;
- }
-
- if (!calling_convention_allows_zig_types(fn_type_id.cc) &&
- fn_type_id.return_type->id != ZigTypeIdVoid)
- {
- if ((err = type_resolve(g, fn_type_id.return_type, ResolveStatusSizeKnown)))
- return g->builtin_types.entry_invalid;
- bool ok_type;
- if ((err = type_allowed_in_extern(g, fn_type_id.return_type, &ok_type)))
- return g->builtin_types.entry_invalid;
- if (!ok_type) {
- add_node_error(g, fn_proto->return_type,
- buf_sprintf("return type '%s' not allowed in function with calling convention '%s'",
- buf_ptr(&fn_type_id.return_type->name),
- calling_convention_name(fn_type_id.cc)));
- return g->builtin_types.entry_invalid;
- }
- }
-
- switch (type_requires_comptime(g, fn_type_id.return_type)) {
- case ReqCompTimeInvalid:
- return g->builtin_types.entry_invalid;
- case ReqCompTimeYes:
- return get_generic_fn_type(g, &fn_type_id);
- case ReqCompTimeNo:
- break;
- }
-
- return get_fn_type(g, &fn_type_id);
-}
-
-bool is_valid_return_type(ZigType* type) {
- switch (type->id) {
- case ZigTypeIdInvalid:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdOpaque:
- return false;
- default:
- return true;
- }
- zig_unreachable();
-}
-
-bool is_valid_param_type(ZigType* type) {
- switch (type->id) {
- case ZigTypeIdInvalid:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdOpaque:
- case ZigTypeIdUnreachable:
- return false;
- default:
- return true;
- }
- zig_unreachable();
-}
-
-bool type_is_invalid(ZigType *type_entry) {
- switch (type_entry->id) {
- case ZigTypeIdInvalid:
- return true;
- case ZigTypeIdStruct:
- return type_entry->data.structure.resolve_status == ResolveStatusInvalid;
- case ZigTypeIdUnion:
- return type_entry->data.unionation.resolve_status == ResolveStatusInvalid;
- case ZigTypeIdEnum:
- return type_entry->data.enumeration.resolve_status == ResolveStatusInvalid;
- case ZigTypeIdFnFrame:
- return type_entry->data.frame.reported_loop_err;
- default:
- return false;
- }
- zig_unreachable();
-}
-
-struct SrcField {
- const char *name;
- ZigType *ty;
- unsigned align;
-};
-
-static ZigType *get_struct_type(CodeGen *g, const char *type_name, SrcField fields[], size_t field_count,
- unsigned min_abi_align)
-{
- ZigType *struct_type = new_type_table_entry(ZigTypeIdStruct);
-
- buf_init_from_str(&struct_type->name, type_name);
-
- struct_type->data.structure.src_field_count = field_count;
- struct_type->data.structure.gen_field_count = 0;
- struct_type->data.structure.resolve_status = ResolveStatusSizeKnown;
- struct_type->data.structure.fields = alloc_type_struct_fields(field_count);
- struct_type->data.structure.fields_by_name.init(field_count);
-
- size_t abi_align = min_abi_align;
- for (size_t i = 0; i < field_count; i += 1) {
- TypeStructField *field = struct_type->data.structure.fields[i];
- field->name = buf_create_from_str(fields[i].name);
- field->type_entry = fields[i].ty;
- field->src_index = i;
- field->align = fields[i].align;
-
- if (type_has_bits(g, field->type_entry)) {
- assert(type_is_resolved(field->type_entry, ResolveStatusSizeKnown));
- unsigned field_abi_align = max(field->align, field->type_entry->abi_align);
- if (field_abi_align > abi_align) {
- abi_align = field_abi_align;
- }
- }
-
- auto prev_entry = struct_type->data.structure.fields_by_name.put_unique(field->name, field);
- assert(prev_entry == nullptr);
- }
-
- size_t next_offset = 0;
- for (size_t i = 0; i < field_count; i += 1) {
- TypeStructField *field = struct_type->data.structure.fields[i];
- if (!type_has_bits(g, field->type_entry))
- continue;
-
- field->offset = next_offset;
-
- // find the next non-zero-byte field for offset calculations
- size_t next_src_field_index = i + 1;
- for (; next_src_field_index < field_count; next_src_field_index += 1) {
- if (type_has_bits(g, struct_type->data.structure.fields[next_src_field_index]->type_entry))
- break;
- }
- size_t next_abi_align;
- if (next_src_field_index == field_count) {
- next_abi_align = abi_align;
- } else {
- next_abi_align = max(fields[next_src_field_index].align,
- struct_type->data.structure.fields[next_src_field_index]->type_entry->abi_align);
- }
- next_offset = next_field_offset(next_offset, abi_align, field->type_entry->abi_size, next_abi_align);
- }
-
- struct_type->abi_align = abi_align;
- struct_type->abi_size = next_offset;
- struct_type->size_in_bits = next_offset * 8;
-
- return struct_type;
-}
-
-static size_t get_store_size_bytes(size_t size_in_bits) {
- return (size_in_bits + 7) / 8;
-}
-
-static size_t get_abi_align_bytes(size_t size_in_bits, size_t pointer_size_bytes) {
- size_t store_size_bytes = get_store_size_bytes(size_in_bits);
- if (store_size_bytes >= pointer_size_bytes)
- return pointer_size_bytes;
- return round_to_next_power_of_2(store_size_bytes);
-}
-
-static size_t get_abi_size_bytes(size_t size_in_bits, size_t pointer_size_bytes) {
- size_t store_size_bytes = get_store_size_bytes(size_in_bits);
- size_t abi_align = get_abi_align_bytes(size_in_bits, pointer_size_bytes);
- return align_forward(store_size_bytes, abi_align);
-}
-
-ZigType *resolve_struct_field_type(CodeGen *g, TypeStructField *struct_field) {
- Error err;
- if (struct_field->type_entry == nullptr) {
- if ((err = ir_resolve_lazy(g, struct_field->decl_node, struct_field->type_val))) {
- return nullptr;
- }
- struct_field->type_entry = struct_field->type_val->data.x_type;
- }
- return struct_field->type_entry;
-}
-
-static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) {
- assert(struct_type->id == ZigTypeIdStruct);
-
- Error err;
-
- if (struct_type->data.structure.resolve_status == ResolveStatusInvalid)
- return ErrorSemanticAnalyzeFail;
- if (struct_type->data.structure.resolve_status >= ResolveStatusSizeKnown)
- return ErrorNone;
-
- if ((err = resolve_struct_alignment(g, struct_type)))
- return err;
-
- AstNode *decl_node = struct_type->data.structure.decl_node;
-
- if (struct_type->data.structure.resolve_loop_flag_other) {
- if (struct_type->data.structure.resolve_status != ResolveStatusInvalid) {
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- add_node_error(g, decl_node,
- buf_sprintf("struct '%s' depends on itself", buf_ptr(&struct_type->name)));
- }
- return ErrorSemanticAnalyzeFail;
- }
-
- assert(struct_type->data.structure.fields || struct_type->data.structure.src_field_count == 0);
-
- size_t field_count = struct_type->data.structure.src_field_count;
-
- bool packed = (struct_type->data.structure.layout == ContainerLayoutPacked);
- struct_type->data.structure.resolve_loop_flag_other = true;
-
- uint32_t *host_int_bytes = packed ? heap::c_allocator.allocate(struct_type->data.structure.gen_field_count) : nullptr;
-
- size_t packed_bits_offset = 0;
- size_t next_offset = 0;
- size_t first_packed_bits_offset_misalign = SIZE_MAX;
- size_t gen_field_index = 0;
- size_t size_in_bits = 0;
- size_t abi_align = struct_type->abi_align;
-
- // Calculate offsets
- for (size_t i = 0; i < field_count; i += 1) {
- TypeStructField *field = struct_type->data.structure.fields[i];
- if (field->gen_index == SIZE_MAX)
- continue;
-
- field->gen_index = gen_field_index;
- field->offset = next_offset;
-
- if (packed) {
- ZigType *field_type = resolve_struct_field_type(g, field);
- if (field_type == nullptr) {
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- if ((err = type_resolve(g, field->type_entry, ResolveStatusSizeKnown))) {
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return err;
- }
- if ((err = emit_error_unless_type_allowed_in_packed_struct(g, field->type_entry, field->decl_node))) {
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return err;
- }
-
- size_t field_size_in_bits = type_size_bits(g, field_type);
- size_t next_packed_bits_offset = packed_bits_offset + field_size_in_bits;
-
- size_in_bits += field_size_in_bits;
-
- if (first_packed_bits_offset_misalign != SIZE_MAX) {
- // this field is not byte-aligned; it is part of the previous field with a bit offset
- field->bit_offset_in_host = packed_bits_offset - first_packed_bits_offset_misalign;
-
- size_t full_bit_count = next_packed_bits_offset - first_packed_bits_offset_misalign;
- size_t full_abi_size = get_abi_size_bytes(full_bit_count, g->pointer_size_bytes);
- if (full_abi_size * 8 == full_bit_count) {
- // next field recovers ABI alignment
- host_int_bytes[gen_field_index] = full_abi_size;
- gen_field_index += 1;
- // TODO: https://github.com/ziglang/zig/issues/1512
- next_offset = next_field_offset(next_offset, abi_align, full_abi_size, 1);
- size_in_bits = next_offset * 8;
-
- first_packed_bits_offset_misalign = SIZE_MAX;
- }
- } else if (get_abi_size_bytes(field_type->size_in_bits, g->pointer_size_bytes) * 8 != field_size_in_bits) {
- first_packed_bits_offset_misalign = packed_bits_offset;
- field->bit_offset_in_host = 0;
- } else {
- // This is a byte-aligned field (both start and end) in a packed struct.
- host_int_bytes[gen_field_index] = field_type->size_in_bits / 8;
- field->bit_offset_in_host = 0;
- gen_field_index += 1;
- // TODO: https://github.com/ziglang/zig/issues/1512
- next_offset = next_field_offset(next_offset, abi_align, field_type->size_in_bits / 8, 1);
- size_in_bits = next_offset * 8;
- }
- packed_bits_offset = next_packed_bits_offset;
- } else {
- size_t field_abi_size;
- size_t field_size_in_bits;
- if ((err = type_val_resolve_abi_size(g, field->decl_node, field->type_val,
- &field_abi_size, &field_size_in_bits)))
- {
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return err;
- }
-
- gen_field_index += 1;
- size_t next_src_field_index = i + 1;
- for (; next_src_field_index < field_count; next_src_field_index += 1) {
- if (struct_type->data.structure.fields[next_src_field_index]->gen_index != SIZE_MAX) {
- break;
- }
- }
- size_t next_align = (next_src_field_index == field_count) ?
- abi_align : struct_type->data.structure.fields[next_src_field_index]->align;
- next_offset = next_field_offset(next_offset, abi_align, field_abi_size, next_align);
- size_in_bits = next_offset * 8;
- }
- }
- if (first_packed_bits_offset_misalign != SIZE_MAX) {
- size_t full_bit_count = packed_bits_offset - first_packed_bits_offset_misalign;
- size_t full_abi_size = get_abi_size_bytes(full_bit_count, g->pointer_size_bytes);
- next_offset = next_field_offset(next_offset, abi_align, full_abi_size, abi_align);
- host_int_bytes[gen_field_index] = full_abi_size;
- gen_field_index += 1;
- }
-
- struct_type->abi_size = next_offset;
- struct_type->size_in_bits = size_in_bits;
- struct_type->data.structure.resolve_status = ResolveStatusSizeKnown;
- struct_type->data.structure.gen_field_count = (uint32_t)gen_field_index;
- struct_type->data.structure.resolve_loop_flag_other = false;
- struct_type->data.structure.host_int_bytes = host_int_bytes;
-
-
- // Resolve types for fields
- for (size_t i = 0; i < field_count; i += 1) {
- TypeStructField *field = struct_type->data.structure.fields[i];
- ZigType *field_type = resolve_struct_field_type(g, field);
- if (field_type == nullptr) {
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
-
- if ((err = type_resolve(g, field_type, ResolveStatusSizeKnown))) {
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return err;
- }
-
- if (struct_type->data.structure.layout == ContainerLayoutExtern) {
- bool ok_type;
- if ((err = type_allowed_in_extern(g, field_type, &ok_type))) {
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- if (!ok_type) {
- add_node_error(g, field->decl_node,
- buf_sprintf("extern structs cannot contain fields of type '%s'",
- buf_ptr(&field_type->name)));
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- }
- }
-
- return ErrorNone;
-}
-
-static Error resolve_union_alignment(CodeGen *g, ZigType *union_type) {
- assert(union_type->id == ZigTypeIdUnion);
-
- Error err;
-
- if (union_type->data.unionation.resolve_status == ResolveStatusInvalid)
- return ErrorSemanticAnalyzeFail;
- if (union_type->data.unionation.resolve_status >= ResolveStatusAlignmentKnown)
- return ErrorNone;
- if ((err = resolve_union_zero_bits(g, union_type)))
- return err;
- if (union_type->data.unionation.resolve_status >= ResolveStatusAlignmentKnown)
- return ErrorNone;
-
- AstNode *decl_node = union_type->data.structure.decl_node;
-
- if (union_type->data.unionation.resolve_loop_flag_other) {
- if (union_type->data.unionation.resolve_status != ResolveStatusInvalid) {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- add_node_error(g, decl_node,
- buf_sprintf("union '%s' depends on itself", buf_ptr(&union_type->name)));
- }
- return ErrorSemanticAnalyzeFail;
- }
-
- // set temporary flag
- union_type->data.unionation.resolve_loop_flag_other = true;
-
- TypeUnionField *most_aligned_union_member = nullptr;
- uint32_t field_count = union_type->data.unionation.src_field_count;
- bool packed = union_type->data.unionation.layout == ContainerLayoutPacked;
-
- for (uint32_t i = 0; i < field_count; i += 1) {
- TypeUnionField *field = &union_type->data.unionation.fields[i];
- if (field->gen_index == UINT32_MAX)
- continue;
-
- AstNode *align_expr = nullptr;
- if (union_type->data.unionation.decl_node->type == NodeTypeContainerDecl) {
- align_expr = field->decl_node->data.struct_field.align_expr;
- }
- if (align_expr != nullptr) {
- if (!analyze_const_align(g, &union_type->data.unionation.decls_scope->base, align_expr,
- &field->align))
- {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- add_node_error(g, field->decl_node,
- buf_create_from_str("TODO implement field alignment syntax for unions. https://github.com/ziglang/zig/issues/3125"));
- } else if (packed) {
- field->align = 1;
- } else if (field->type_entry != nullptr) {
- if ((err = type_resolve(g, field->type_entry, ResolveStatusAlignmentKnown))) {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return err;
- }
- field->align = field->type_entry->abi_align;
- } else {
- if ((err = type_val_resolve_abi_align(g, field->decl_node, field->type_val, &field->align))) {
- if (g->trace_err != nullptr) {
- g->trace_err = add_error_note(g, g->trace_err, field->decl_node,
- buf_create_from_str("while checking this field"));
- }
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return err;
- }
- if (union_type->data.unionation.resolve_status == ResolveStatusInvalid)
- return ErrorSemanticAnalyzeFail;
- }
-
- if (most_aligned_union_member == nullptr || field->align > most_aligned_union_member->align) {
- most_aligned_union_member = field;
- }
- }
-
- // unset temporary flag
- union_type->data.unionation.resolve_loop_flag_other = false;
- union_type->data.unionation.resolve_status = ResolveStatusAlignmentKnown;
- union_type->data.unionation.most_aligned_union_member = most_aligned_union_member;
-
- ZigType *tag_type = union_type->data.unionation.tag_type;
- if (tag_type != nullptr && type_has_bits(g, tag_type)) {
- if ((err = type_resolve(g, tag_type, ResolveStatusAlignmentKnown))) {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- if (most_aligned_union_member == nullptr) {
- union_type->abi_align = tag_type->abi_align;
- union_type->data.unionation.gen_tag_index = SIZE_MAX;
- union_type->data.unionation.gen_union_index = SIZE_MAX;
- } else if (tag_type->abi_align > most_aligned_union_member->align) {
- union_type->abi_align = tag_type->abi_align;
- union_type->data.unionation.gen_tag_index = 0;
- union_type->data.unionation.gen_union_index = 1;
- } else {
- union_type->abi_align = most_aligned_union_member->align;
- union_type->data.unionation.gen_union_index = 0;
- union_type->data.unionation.gen_tag_index = 1;
- }
- } else {
- assert(most_aligned_union_member != nullptr);
- union_type->abi_align = most_aligned_union_member->align;
- union_type->data.unionation.gen_union_index = SIZE_MAX;
- union_type->data.unionation.gen_tag_index = SIZE_MAX;
- }
-
- return ErrorNone;
-}
-
-ZigType *resolve_union_field_type(CodeGen *g, TypeUnionField *union_field) {
- Error err;
- if (union_field->type_entry == nullptr) {
- if ((err = ir_resolve_lazy(g, union_field->decl_node, union_field->type_val))) {
- return nullptr;
- }
- union_field->type_entry = union_field->type_val->data.x_type;
- }
- return union_field->type_entry;
-}
-
-static Error resolve_union_type(CodeGen *g, ZigType *union_type) {
- assert(union_type->id == ZigTypeIdUnion);
-
- Error err;
-
- if (union_type->data.unionation.resolve_status == ResolveStatusInvalid)
- return ErrorSemanticAnalyzeFail;
- if (union_type->data.unionation.resolve_status >= ResolveStatusSizeKnown)
- return ErrorNone;
-
- if ((err = resolve_union_alignment(g, union_type)))
- return err;
-
- AstNode *decl_node = union_type->data.unionation.decl_node;
-
- uint32_t field_count = union_type->data.unionation.src_field_count;
- TypeUnionField *most_aligned_union_member = union_type->data.unionation.most_aligned_union_member;
-
- assert(union_type->data.unionation.fields);
-
- size_t union_abi_size = 0;
- size_t union_size_in_bits = 0;
-
- if (union_type->data.unionation.resolve_loop_flag_other) {
- if (union_type->data.unionation.resolve_status != ResolveStatusInvalid) {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- add_node_error(g, decl_node,
- buf_sprintf("union '%s' depends on itself", buf_ptr(&union_type->name)));
- }
- return ErrorSemanticAnalyzeFail;
- }
-
- // set temporary flag
- union_type->data.unionation.resolve_loop_flag_other = true;
-
- const bool is_packed = union_type->data.unionation.layout == ContainerLayoutPacked;
-
- for (uint32_t i = 0; i < field_count; i += 1) {
- TypeUnionField *union_field = &union_type->data.unionation.fields[i];
- ZigType *field_type = resolve_union_field_type(g, union_field);
- if (field_type == nullptr) {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
-
- if ((err = type_resolve(g, field_type, ResolveStatusSizeKnown))) {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
-
- if (is_packed) {
- if ((err = emit_error_unless_type_allowed_in_packed_union(g, field_type, union_field->decl_node))) {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return err;
- }
- }
-
- if (type_is_invalid(union_type))
- return ErrorSemanticAnalyzeFail;
-
- if (!type_has_bits(g, field_type))
- continue;
-
- union_abi_size = max(union_abi_size, field_type->abi_size);
- union_size_in_bits = max(union_size_in_bits, field_type->size_in_bits);
- }
-
- // The union itself for now has to be treated as being independently aligned.
- // See https://github.com/ziglang/zig/issues/2166.
- if (most_aligned_union_member != nullptr) {
- union_abi_size = align_forward(union_abi_size, most_aligned_union_member->align);
- }
-
- // unset temporary flag
- union_type->data.unionation.resolve_loop_flag_other = false;
- union_type->data.unionation.resolve_status = ResolveStatusSizeKnown;
- union_type->data.unionation.union_abi_size = union_abi_size;
-
- ZigType *tag_type = union_type->data.unionation.tag_type;
- if (tag_type != nullptr && type_has_bits(g, tag_type)) {
- if ((err = type_resolve(g, tag_type, ResolveStatusSizeKnown))) {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- if (most_aligned_union_member == nullptr) {
- union_type->abi_size = tag_type->abi_size;
- union_type->size_in_bits = tag_type->size_in_bits;
- } else {
- size_t field_sizes[2];
- size_t field_aligns[2];
- field_sizes[union_type->data.unionation.gen_tag_index] = tag_type->abi_size;
- field_aligns[union_type->data.unionation.gen_tag_index] = tag_type->abi_align;
- field_sizes[union_type->data.unionation.gen_union_index] = union_abi_size;
- field_aligns[union_type->data.unionation.gen_union_index] = most_aligned_union_member->align;
- size_t field2_offset = next_field_offset(0, union_type->abi_align, field_sizes[0], field_aligns[1]);
- union_type->abi_size = next_field_offset(field2_offset, union_type->abi_align, field_sizes[1], union_type->abi_align);
- union_type->size_in_bits = union_type->abi_size * 8;
- }
- } else {
- union_type->abi_size = union_abi_size;
- union_type->size_in_bits = union_size_in_bits;
- }
-
- return ErrorNone;
-}
-
-static Error type_is_valid_extern_enum_tag(CodeGen *g, ZigType *ty, bool *result) {
- // Only integer types are allowed by the C ABI
- if(ty->id != ZigTypeIdInt) {
- *result = false;
- return ErrorNone;
- }
-
- // According to the ANSI C standard the enumeration type should be either a
- // signed char, a signed integer or an unsigned one. But GCC/Clang allow
- // other integral types as a compiler extension so let's accomodate them
- // aswell.
- return type_allowed_in_extern(g, ty, result);
-}
-
-static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
- Error err;
- assert(enum_type->id == ZigTypeIdEnum);
-
- if (enum_type->data.enumeration.resolve_status == ResolveStatusInvalid)
- return ErrorSemanticAnalyzeFail;
- if (enum_type->data.enumeration.resolve_status >= ResolveStatusZeroBitsKnown)
- return ErrorNone;
-
- AstNode *decl_node = enum_type->data.enumeration.decl_node;
-
- if (enum_type->data.enumeration.resolve_loop_flag) {
- if (enum_type->data.enumeration.resolve_status != ResolveStatusInvalid) {
- enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
- add_node_error(g, decl_node,
- buf_sprintf("enum '%s' depends on itself",
- buf_ptr(&enum_type->name)));
- }
- return ErrorSemanticAnalyzeFail;
- }
-
- enum_type->data.enumeration.resolve_loop_flag = true;
-
- uint32_t field_count;
- if (decl_node->type == NodeTypeContainerDecl) {
- assert(!enum_type->data.enumeration.fields);
- field_count = (uint32_t)decl_node->data.container_decl.fields.length;
- } else {
- field_count = enum_type->data.enumeration.src_field_count + enum_type->data.enumeration.non_exhaustive;
- }
-
- if (field_count == 0) {
- add_node_error(g, decl_node, buf_sprintf("enums must have 1 or more fields"));
- enum_type->data.enumeration.src_field_count = field_count;
- enum_type->data.enumeration.fields = nullptr;
- enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
-
- Scope *scope = &enum_type->data.enumeration.decls_scope->base;
-
- ZigType *tag_int_type;
- if (enum_type->data.enumeration.layout == ContainerLayoutExtern) {
- tag_int_type = get_c_int_type(g, CIntTypeInt);
- } else {
- tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1);
- }
-
- enum_type->size_in_bits = tag_int_type->size_in_bits;
- enum_type->abi_size = tag_int_type->abi_size;
- enum_type->abi_align = tag_int_type->abi_align;
-
- ZigType *wanted_tag_int_type = nullptr;
- if (decl_node->type == NodeTypeContainerDecl) {
- if (decl_node->data.container_decl.init_arg_expr != nullptr) {
- wanted_tag_int_type = analyze_type_expr(g, scope, decl_node->data.container_decl.init_arg_expr);
- }
- } else {
- wanted_tag_int_type = enum_type->data.enumeration.tag_int_type;
- }
-
- if (wanted_tag_int_type != nullptr) {
- if (type_is_invalid(wanted_tag_int_type)) {
- enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
- } else if (wanted_tag_int_type->id != ZigTypeIdInt &&
- wanted_tag_int_type->id != ZigTypeIdComptimeInt) {
- enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
- add_node_error(g, decl_node->data.container_decl.init_arg_expr,
- buf_sprintf("expected integer, found '%s'", buf_ptr(&wanted_tag_int_type->name)));
- } else {
- if (enum_type->data.enumeration.layout == ContainerLayoutExtern) {
- bool ok_type;
- if ((err = type_is_valid_extern_enum_tag(g, wanted_tag_int_type, &ok_type))) {
- enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
- return err;
- }
- if (!ok_type) {
- enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
- ErrorMsg *msg = add_node_error(g, decl_node->data.container_decl.init_arg_expr,
- buf_sprintf("'%s' is not a valid tag type for an extern enum",
- buf_ptr(&wanted_tag_int_type->name)));
- add_error_note(g, msg, decl_node->data.container_decl.init_arg_expr,
- buf_sprintf("any integral type of size 8, 16, 32, 64 or 128 bit is valid"));
- return ErrorSemanticAnalyzeFail;
- }
- }
- tag_int_type = wanted_tag_int_type;
- }
- }
-
- enum_type->data.enumeration.tag_int_type = tag_int_type;
- enum_type->size_in_bits = tag_int_type->size_in_bits;
- enum_type->abi_size = tag_int_type->abi_size;
- enum_type->abi_align = tag_int_type->abi_align;
-
- BigInt bi_one;
- bigint_init_unsigned(&bi_one, 1);
-
- if (decl_node->type == NodeTypeContainerDecl) {
- AstNode *last_field_node = decl_node->data.container_decl.fields.at(field_count - 1);
- if (buf_eql_str(last_field_node->data.struct_field.name, "_")) {
- if (last_field_node->data.struct_field.value != nullptr) {
- add_node_error(g, last_field_node, buf_sprintf("value assigned to '_' field of non-exhaustive enum"));
- enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
- }
- if (decl_node->data.container_decl.init_arg_expr == nullptr) {
- add_node_error(g, decl_node, buf_sprintf("non-exhaustive enum must specify size"));
- enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
- }
- enum_type->data.enumeration.non_exhaustive = true;
- } else {
- enum_type->data.enumeration.non_exhaustive = false;
- }
- }
-
- if (enum_type->data.enumeration.non_exhaustive) {
- field_count -= 1;
- if (field_count > 1 && log2_u64(field_count) == enum_type->size_in_bits) {
- add_node_error(g, decl_node, buf_sprintf("non-exhaustive enum specifies every value"));
- enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
- }
- }
-
- if (decl_node->type == NodeTypeContainerDecl) {
- enum_type->data.enumeration.src_field_count = field_count;
- enum_type->data.enumeration.fields = heap::c_allocator.allocate(field_count);
- enum_type->data.enumeration.fields_by_name.init(field_count);
-
- HashMap occupied_tag_values = {};
- occupied_tag_values.init(field_count);
-
- TypeEnumField *last_enum_field = nullptr;
-
- for (uint32_t field_i = 0; field_i < field_count; field_i += 1) {
- AstNode *field_node = decl_node->data.container_decl.fields.at(field_i);
- TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i];
- type_enum_field->name = field_node->data.struct_field.name;
- type_enum_field->decl_index = field_i;
- type_enum_field->decl_node = field_node;
-
- if (field_node->data.struct_field.type != nullptr) {
- ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.type,
- buf_sprintf("structs and unions, not enums, support field types"));
- add_error_note(g, msg, decl_node,
- buf_sprintf("consider 'union(enum)' here"));
- } else if (field_node->data.struct_field.align_expr != nullptr) {
- ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.align_expr,
- buf_sprintf("structs and unions, not enums, support field alignment"));
- add_error_note(g, msg, decl_node,
- buf_sprintf("consider 'union(enum)' here"));
- }
-
- if (buf_eql_str(type_enum_field->name, "_")) {
- add_node_error(g, field_node, buf_sprintf("'_' field of non-exhaustive enum must be last"));
- enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
- }
-
- auto field_entry = enum_type->data.enumeration.fields_by_name.put_unique(type_enum_field->name, type_enum_field);
- if (field_entry != nullptr) {
- ErrorMsg *msg = add_node_error(g, field_node,
- buf_sprintf("duplicate enum field: '%s'", buf_ptr(type_enum_field->name)));
- add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here"));
- enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
- continue;
- }
-
- AstNode *tag_value = field_node->data.struct_field.value;
-
- if (tag_value != nullptr) {
- // A user-specified value is available
- ZigValue *result = analyze_const_value(g, scope, tag_value, tag_int_type,
- nullptr, UndefBad);
- if (type_is_invalid(result->type)) {
- enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
- continue;
- }
-
- assert(result->special != ConstValSpecialRuntime);
- assert(result->type->id == ZigTypeIdInt || result->type->id == ZigTypeIdComptimeInt);
-
- bigint_init_bigint(&type_enum_field->value, &result->data.x_bigint);
- } else {
- // No value was explicitly specified: allocate the last value + 1
- // or, if this is the first element, zero
- if (last_enum_field != nullptr) {
- bigint_add(&type_enum_field->value, &last_enum_field->value, &bi_one);
- } else {
- bigint_init_unsigned(&type_enum_field->value, 0);
- }
-
- // Make sure we can represent this number with tag_int_type
- if (!bigint_fits_in_bits(&type_enum_field->value,
- tag_int_type->size_in_bits,
- tag_int_type->data.integral.is_signed)) {
- enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
-
- Buf *val_buf = buf_alloc();
- bigint_append_buf(val_buf, &type_enum_field->value, 10);
- add_node_error(g, field_node,
- buf_sprintf("enumeration value %s too large for type '%s'",
- buf_ptr(val_buf), buf_ptr(&tag_int_type->name)));
-
- break;
- }
- }
-
- // Make sure the value is unique
- auto entry = occupied_tag_values.put_unique(type_enum_field->value, field_node);
- if (entry != nullptr && enum_type->data.enumeration.layout != ContainerLayoutExtern) {
- enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
-
- Buf *val_buf = buf_alloc();
- bigint_append_buf(val_buf, &type_enum_field->value, 10);
-
- ErrorMsg *msg = add_node_error(g, field_node,
- buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf)));
- add_error_note(g, msg, entry->value,
- buf_sprintf("other occurrence here"));
- }
-
- last_enum_field = type_enum_field;
- }
- occupied_tag_values.deinit();
- }
-
- if (enum_type->data.enumeration.resolve_status == ResolveStatusInvalid)
- return ErrorSemanticAnalyzeFail;
-
- enum_type->data.enumeration.resolve_loop_flag = false;
- enum_type->data.enumeration.resolve_status = ResolveStatusSizeKnown;
-
- return ErrorNone;
-}
-
-static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) {
- assert(struct_type->id == ZigTypeIdStruct);
-
- Error err;
-
- if (struct_type->data.structure.resolve_status == ResolveStatusInvalid)
- return ErrorSemanticAnalyzeFail;
- if (struct_type->data.structure.resolve_status >= ResolveStatusZeroBitsKnown)
- return ErrorNone;
-
- AstNode *decl_node = struct_type->data.structure.decl_node;
-
- if (struct_type->data.structure.resolve_loop_flag_zero_bits) {
- if (struct_type->data.structure.resolve_status != ResolveStatusInvalid) {
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- add_node_error(g, decl_node,
- buf_sprintf("struct '%s' depends on itself",
- buf_ptr(&struct_type->name)));
- }
- return ErrorSemanticAnalyzeFail;
- }
- struct_type->data.structure.resolve_loop_flag_zero_bits = true;
-
- size_t field_count;
- if (decl_node->type == NodeTypeContainerDecl) {
- field_count = decl_node->data.container_decl.fields.length;
- struct_type->data.structure.src_field_count = (uint32_t)field_count;
-
- src_assert(struct_type->data.structure.fields == nullptr, decl_node);
- struct_type->data.structure.fields = alloc_type_struct_fields(field_count);
- } else if (is_anon_container(struct_type) || struct_type->data.structure.created_by_at_type) {
- field_count = struct_type->data.structure.src_field_count;
-
- src_assert(field_count == 0 || struct_type->data.structure.fields != nullptr, decl_node);
- } else zig_unreachable();
-
- struct_type->data.structure.fields_by_name.init(field_count);
-
- Scope *scope = &struct_type->data.structure.decls_scope->base;
-
- size_t gen_field_index = 0;
- for (size_t i = 0; i < field_count; i += 1) {
- TypeStructField *type_struct_field = struct_type->data.structure.fields[i];
-
- AstNode *field_node;
- if (decl_node->type == NodeTypeContainerDecl) {
- field_node = decl_node->data.container_decl.fields.at(i);
- type_struct_field->name = field_node->data.struct_field.name;
- type_struct_field->decl_node = field_node;
- if (field_node->data.struct_field.comptime_token != nullptr) {
- if (field_node->data.struct_field.value == nullptr) {
- add_token_error(g, field_node->owner,
- field_node->data.struct_field.comptime_token,
- buf_sprintf("comptime struct field missing initialization value"));
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- type_struct_field->is_comptime = true;
- }
-
- if (field_node->data.struct_field.type == nullptr) {
- add_node_error(g, field_node, buf_sprintf("struct field missing type"));
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- } else if (is_anon_container(struct_type) || struct_type->data.structure.created_by_at_type) {
- field_node = type_struct_field->decl_node;
-
- src_assert(type_struct_field->type_entry != nullptr, field_node);
- } else zig_unreachable();
-
- auto field_entry = struct_type->data.structure.fields_by_name.put_unique(type_struct_field->name, type_struct_field);
- if (field_entry != nullptr) {
- ErrorMsg *msg = add_node_error(g, field_node,
- buf_sprintf("duplicate struct field: '%s'", buf_ptr(type_struct_field->name)));
- add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here"));
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
-
- ZigValue *field_type_val;
- if (decl_node->type == NodeTypeContainerDecl) {
- field_type_val = analyze_const_value(g, scope,
- field_node->data.struct_field.type, g->builtin_types.entry_type, nullptr, LazyOkNoUndef);
- if (type_is_invalid(field_type_val->type)) {
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- assert(field_type_val->special != ConstValSpecialRuntime);
- type_struct_field->type_val = field_type_val;
- if (struct_type->data.structure.resolve_status == ResolveStatusInvalid)
- return ErrorSemanticAnalyzeFail;
- } else if (is_anon_container(struct_type) || struct_type->data.structure.created_by_at_type) {
- field_type_val = type_struct_field->type_val;
- } else zig_unreachable();
-
- bool field_is_opaque_type;
- if ((err = type_val_resolve_is_opaque_type(g, field_type_val, &field_is_opaque_type))) {
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- if (field_is_opaque_type) {
- add_node_error(g, field_node,
- buf_sprintf("opaque types have unknown size and therefore cannot be directly embedded in structs"));
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
-
- type_struct_field->src_index = i;
- type_struct_field->gen_index = SIZE_MAX;
-
- if (type_struct_field->is_comptime)
- continue;
-
- switch (type_val_resolve_requires_comptime(g, field_type_val)) {
- case ReqCompTimeYes:
- struct_type->data.structure.requires_comptime = true;
- break;
- case ReqCompTimeInvalid:
- if (g->trace_err != nullptr) {
- g->trace_err = add_error_note(g, g->trace_err, field_node,
- buf_create_from_str("while checking this field"));
- }
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- case ReqCompTimeNo:
- break;
- }
-
- bool field_is_zero_bits;
- if ((err = type_val_resolve_zero_bits(g, field_type_val, struct_type, nullptr, &field_is_zero_bits))) {
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- if (field_is_zero_bits)
- continue;
-
- type_struct_field->gen_index = gen_field_index;
- gen_field_index += 1;
- }
-
- struct_type->data.structure.resolve_loop_flag_zero_bits = false;
- struct_type->data.structure.gen_field_count = (uint32_t)gen_field_index;
- if (gen_field_index != 0) {
- struct_type->abi_size = SIZE_MAX;
- struct_type->size_in_bits = SIZE_MAX;
- }
-
- if (struct_type->data.structure.resolve_status == ResolveStatusInvalid)
- return ErrorSemanticAnalyzeFail;
-
- struct_type->data.structure.resolve_status = ResolveStatusZeroBitsKnown;
- return ErrorNone;
-}
-
-static Error resolve_struct_alignment(CodeGen *g, ZigType *struct_type) {
- assert(struct_type->id == ZigTypeIdStruct);
-
- Error err;
-
- if (struct_type->data.structure.resolve_status == ResolveStatusInvalid)
- return ErrorSemanticAnalyzeFail;
- if (struct_type->data.structure.resolve_status >= ResolveStatusAlignmentKnown)
- return ErrorNone;
- if ((err = resolve_struct_zero_bits(g, struct_type)))
- return err;
- if (struct_type->data.structure.resolve_status >= ResolveStatusAlignmentKnown)
- return ErrorNone;
-
- AstNode *decl_node = struct_type->data.structure.decl_node;
-
- if (struct_type->data.structure.resolve_loop_flag_other) {
- if (struct_type->data.structure.resolve_status != ResolveStatusInvalid) {
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- add_node_error(g, decl_node,
- buf_sprintf("struct '%s' depends on itself", buf_ptr(&struct_type->name)));
- }
- return ErrorSemanticAnalyzeFail;
- }
-
- struct_type->data.structure.resolve_loop_flag_other = true;
-
- size_t field_count = struct_type->data.structure.src_field_count;
- bool packed = struct_type->data.structure.layout == ContainerLayoutPacked;
-
- for (size_t i = 0; i < field_count; i += 1) {
- TypeStructField *field = struct_type->data.structure.fields[i];
- if (field->gen_index == SIZE_MAX)
- continue;
-
- AstNode *align_expr = (field->decl_node->type == NodeTypeStructField) ?
- field->decl_node->data.struct_field.align_expr : nullptr;
- if (align_expr != nullptr) {
- if (!analyze_const_align(g, &struct_type->data.structure.decls_scope->base, align_expr,
- &field->align))
- {
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- } else if (packed) {
- field->align = 1;
- } else {
- if ((err = type_val_resolve_abi_align(g, field->decl_node, field->type_val, &field->align))) {
- if (g->trace_err != nullptr) {
- g->trace_err = add_error_note(g, g->trace_err, field->decl_node,
- buf_create_from_str("while checking this field"));
- }
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return err;
- }
- if (struct_type->data.structure.resolve_status == ResolveStatusInvalid)
- return ErrorSemanticAnalyzeFail;
- }
-
- if (field->align > struct_type->abi_align) {
- struct_type->abi_align = field->align;
- }
- }
-
- if (!type_has_bits(g, struct_type)) {
- assert(struct_type->abi_align == 0);
- }
-
- struct_type->data.structure.resolve_loop_flag_other = false;
-
- if (struct_type->data.structure.resolve_status == ResolveStatusInvalid) {
- return ErrorSemanticAnalyzeFail;
- }
-
- struct_type->data.structure.resolve_status = ResolveStatusAlignmentKnown;
- return ErrorNone;
-}
-
-static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
- assert(union_type->id == ZigTypeIdUnion);
-
- Error err;
-
- if (union_type->data.unionation.resolve_status == ResolveStatusInvalid)
- return ErrorSemanticAnalyzeFail;
-
- if (union_type->data.unionation.resolve_status >= ResolveStatusZeroBitsKnown)
- return ErrorNone;
-
- AstNode *decl_node = union_type->data.unionation.decl_node;
-
- if (union_type->data.unionation.resolve_loop_flag_zero_bits) {
- if (union_type->data.unionation.resolve_status != ResolveStatusInvalid) {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- add_node_error(g, decl_node,
- buf_sprintf("union '%s' depends on itself",
- buf_ptr(&union_type->name)));
- }
- return ErrorSemanticAnalyzeFail;
- }
-
- union_type->data.unionation.resolve_loop_flag_zero_bits = true;
-
- uint32_t field_count;
- if (decl_node->type == NodeTypeContainerDecl) {
- assert(union_type->data.unionation.fields == nullptr);
- field_count = (uint32_t)decl_node->data.container_decl.fields.length;
- union_type->data.unionation.src_field_count = field_count;
- union_type->data.unionation.fields = heap::c_allocator.allocate(field_count);
- union_type->data.unionation.fields_by_name.init(field_count);
- } else {
- field_count = union_type->data.unionation.src_field_count;
- assert(field_count == 0 || union_type->data.unionation.fields != nullptr);
- }
-
- if (field_count == 0) {
- add_node_error(g, decl_node, buf_sprintf("unions must have 1 or more fields"));
- union_type->data.unionation.src_field_count = field_count;
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
-
- Scope *scope = &union_type->data.unionation.decls_scope->base;
-
- HashMap occupied_tag_values = {};
-
- bool is_auto_enum; // union(enum) or union(enum(expr))
- bool is_explicit_enum; // union(expr)
- AstNode *enum_type_node; // expr in union(enum(expr)) or union(expr)
- if (decl_node->type == NodeTypeContainerDecl) {
- is_auto_enum = decl_node->data.container_decl.auto_enum;
- is_explicit_enum = decl_node->data.container_decl.init_arg_expr != nullptr;
- enum_type_node = decl_node->data.container_decl.init_arg_expr;
- } else {
- is_auto_enum = false;
- is_explicit_enum = union_type->data.unionation.tag_type != nullptr;
- enum_type_node = nullptr;
- }
- union_type->data.unionation.have_explicit_tag_type = is_auto_enum || is_explicit_enum;
-
- bool is_auto_layout = union_type->data.unionation.layout == ContainerLayoutAuto;
- bool want_safety = (field_count >= 2)
- && (is_auto_layout || is_explicit_enum)
- && !(g->build_mode == BuildModeFastRelease || g->build_mode == BuildModeSmallRelease);
- ZigType *tag_type;
- bool create_enum_type = is_auto_enum || (!is_explicit_enum && want_safety);
- bool *covered_enum_fields;
- bool *is_zero_bits = heap::c_allocator.allocate(field_count);
- ZigLLVMDIEnumerator **di_enumerators;
- if (create_enum_type) {
- occupied_tag_values.init(field_count);
-
- di_enumerators = heap::c_allocator.allocate(field_count);
-
- ZigType *tag_int_type;
- if (enum_type_node != nullptr) {
- tag_int_type = analyze_type_expr(g, scope, enum_type_node);
- if (type_is_invalid(tag_int_type)) {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- if (tag_int_type->id != ZigTypeIdInt && tag_int_type->id != ZigTypeIdComptimeInt) {
- add_node_error(g, enum_type_node,
- buf_sprintf("expected integer tag type, found '%s'", buf_ptr(&tag_int_type->name)));
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- } else {
- tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1);
- }
-
- tag_type = new_type_table_entry(ZigTypeIdEnum);
- buf_resize(&tag_type->name, 0);
- buf_appendf(&tag_type->name, "@TagType(%s)", buf_ptr(&union_type->name));
- tag_type->llvm_type = tag_int_type->llvm_type;
- tag_type->llvm_di_type = tag_int_type->llvm_di_type;
- tag_type->abi_size = tag_int_type->abi_size;
- tag_type->abi_align = tag_int_type->abi_align;
- tag_type->size_in_bits = tag_int_type->size_in_bits;
-
- tag_type->data.enumeration.tag_int_type = tag_int_type;
- tag_type->data.enumeration.resolve_status = ResolveStatusSizeKnown;
- tag_type->data.enumeration.decl_node = decl_node;
- tag_type->data.enumeration.layout = ContainerLayoutAuto;
- tag_type->data.enumeration.src_field_count = field_count;
- tag_type->data.enumeration.fields = heap::c_allocator.allocate(field_count);
- tag_type->data.enumeration.fields_by_name.init(field_count);
- tag_type->data.enumeration.decls_scope = union_type->data.unionation.decls_scope;
- } else if (enum_type_node != nullptr) {
- ZigType *enum_type = analyze_type_expr(g, scope, enum_type_node);
- if (type_is_invalid(enum_type)) {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- if (enum_type->id != ZigTypeIdEnum) {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- add_node_error(g, enum_type_node,
- buf_sprintf("expected enum tag type, found '%s'", buf_ptr(&enum_type->name)));
- return ErrorSemanticAnalyzeFail;
- }
- if ((err = type_resolve(g, enum_type, ResolveStatusAlignmentKnown))) {
- assert(g->errors.length != 0);
- return err;
- }
- tag_type = enum_type;
- } else {
- if (decl_node->type == NodeTypeContainerDecl) {
- tag_type = nullptr;
- } else {
- tag_type = union_type->data.unionation.tag_type;
- }
- }
- if (tag_type != nullptr) {
- covered_enum_fields = heap::c_allocator.allocate(tag_type->data.enumeration.src_field_count);
- }
- union_type->data.unionation.tag_type = tag_type;
-
- for (uint32_t i = 0; i < field_count; i += 1) {
- TypeUnionField *union_field = &union_type->data.unionation.fields[i];
- if (decl_node->type == NodeTypeContainerDecl) {
- AstNode *field_node = decl_node->data.container_decl.fields.at(i);
- union_field->name = field_node->data.struct_field.name;
- union_field->decl_node = field_node;
- union_field->gen_index = UINT32_MAX;
- is_zero_bits[i] = false;
-
- auto field_entry = union_type->data.unionation.fields_by_name.put_unique(union_field->name, union_field);
- if (field_entry != nullptr) {
- ErrorMsg *msg = add_node_error(g, union_field->decl_node,
- buf_sprintf("duplicate union field: '%s'", buf_ptr(union_field->name)));
- add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here"));
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
-
- if (field_node->data.struct_field.type == nullptr) {
- if (is_auto_enum || is_explicit_enum) {
- union_field->type_entry = g->builtin_types.entry_void;
- is_zero_bits[i] = true;
- } else {
- add_node_error(g, field_node, buf_sprintf("union field missing type"));
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- } else {
- ZigValue *field_type_val = analyze_const_value(g, scope,
- field_node->data.struct_field.type, g->builtin_types.entry_type, nullptr, LazyOkNoUndef);
- if (type_is_invalid(field_type_val->type)) {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- assert(field_type_val->special != ConstValSpecialRuntime);
- union_field->type_val = field_type_val;
- }
-
- if (field_node->data.struct_field.value != nullptr && !is_auto_enum) {
- ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.value,
- buf_create_from_str("untagged union field assignment"));
- add_error_note(g, msg, decl_node, buf_create_from_str("consider 'union(enum)' here"));
- }
- }
-
- if (union_field->type_val != nullptr) {
- bool field_is_opaque_type;
- if ((err = type_val_resolve_is_opaque_type(g, union_field->type_val, &field_is_opaque_type))) {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- if (field_is_opaque_type) {
- add_node_error(g, union_field->decl_node,
- buf_create_from_str(
- "opaque types have unknown size and therefore cannot be directly embedded in unions"));
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
-
- switch (type_val_resolve_requires_comptime(g, union_field->type_val)) {
- case ReqCompTimeInvalid:
- if (g->trace_err != nullptr) {
- g->trace_err = add_error_note(g, g->trace_err, union_field->decl_node,
- buf_create_from_str("while checking this field"));
- }
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- case ReqCompTimeYes:
- union_type->data.unionation.requires_comptime = true;
- break;
- case ReqCompTimeNo:
- break;
- }
-
- if ((err = type_val_resolve_zero_bits(g, union_field->type_val, union_type, nullptr, &is_zero_bits[i]))) {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- }
-
- if (create_enum_type) {
- di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(union_field->name), i);
- union_field->enum_field = &tag_type->data.enumeration.fields[i];
- union_field->enum_field->name = union_field->name;
- union_field->enum_field->decl_index = i;
- union_field->enum_field->decl_node = union_field->decl_node;
-
- auto prev_entry = tag_type->data.enumeration.fields_by_name.put_unique(union_field->enum_field->name, union_field->enum_field);
- assert(prev_entry == nullptr); // caught by union de-duplicator above
-
- AstNode *tag_value = decl_node->type == NodeTypeContainerDecl
- ? union_field->decl_node->data.struct_field.value : nullptr;
-
- // In this first pass we resolve explicit tag values.
- // In a second pass we will fill in the unspecified ones.
- if (tag_value != nullptr) {
- ZigType *tag_int_type = tag_type->data.enumeration.tag_int_type;
- ZigValue *result = analyze_const_value(g, scope, tag_value, tag_int_type,
- nullptr, UndefBad);
- if (type_is_invalid(result->type)) {
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- assert(result->special != ConstValSpecialRuntime);
- assert(result->type->id == ZigTypeIdInt);
- auto entry = occupied_tag_values.put_unique(result->data.x_bigint, tag_value);
- if (entry == nullptr) {
- bigint_init_bigint(&union_field->enum_field->value, &result->data.x_bigint);
- } else {
- Buf *val_buf = buf_alloc();
- bigint_append_buf(val_buf, &result->data.x_bigint, 10);
-
- ErrorMsg *msg = add_node_error(g, tag_value,
- buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf)));
- add_error_note(g, msg, entry->value,
- buf_sprintf("other occurrence here"));
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- }
- } else if (tag_type != nullptr) {
- union_field->enum_field = find_enum_type_field(tag_type, union_field->name);
- if (union_field->enum_field == nullptr) {
- ErrorMsg *msg = add_node_error(g, union_field->decl_node,
- buf_sprintf("enum field not found: '%s'", buf_ptr(union_field->name)));
- add_error_note(g, msg, tag_type->data.enumeration.decl_node,
- buf_sprintf("enum declared here"));
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- covered_enum_fields[union_field->enum_field->decl_index] = true;
- } else {
- union_field->enum_field = heap::c_allocator.create();
- union_field->enum_field->name = union_field->name;
- union_field->enum_field->decl_index = i;
- bigint_init_unsigned(&union_field->enum_field->value, i);
- }
- assert(union_field->enum_field != nullptr);
- }
-
- uint32_t gen_field_index = 0;
- for (uint32_t i = 0; i < field_count; i += 1) {
- TypeUnionField *union_field = &union_type->data.unionation.fields[i];
- if (!is_zero_bits[i]) {
- union_field->gen_index = gen_field_index;
- gen_field_index += 1;
- }
- }
-
- bool src_have_tag = is_auto_enum || is_explicit_enum;
-
- if (src_have_tag && union_type->data.unionation.layout != ContainerLayoutAuto) {
- const char *qual_str;
- switch (union_type->data.unionation.layout) {
- case ContainerLayoutAuto:
- zig_unreachable();
- case ContainerLayoutPacked:
- qual_str = "packed";
- break;
- case ContainerLayoutExtern:
- qual_str = "extern";
- break;
- }
- AstNode *source_node = enum_type_node != nullptr ? enum_type_node : decl_node;
- add_node_error(g, source_node,
- buf_sprintf("%s union does not support enum tag type", qual_str));
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
-
- if (create_enum_type) {
- if (decl_node->type == NodeTypeContainerDecl) {
- // Now iterate again and populate the unspecified tag values
- uint32_t next_maybe_unoccupied_index = 0;
-
- for (uint32_t field_i = 0; field_i < field_count; field_i += 1) {
- AstNode *field_node = decl_node->data.container_decl.fields.at(field_i);
- TypeUnionField *union_field = &union_type->data.unionation.fields[field_i];
- AstNode *tag_value = field_node->data.struct_field.value;
-
- if (tag_value == nullptr) {
- if (occupied_tag_values.size() == 0) {
- bigint_init_unsigned(&union_field->enum_field->value, next_maybe_unoccupied_index);
- next_maybe_unoccupied_index += 1;
- } else {
- BigInt proposed_value;
- for (;;) {
- bigint_init_unsigned(&proposed_value, next_maybe_unoccupied_index);
- next_maybe_unoccupied_index += 1;
- auto entry = occupied_tag_values.put_unique(proposed_value, field_node);
- if (entry != nullptr) {
- continue;
- }
- break;
- }
- bigint_init_bigint(&union_field->enum_field->value, &proposed_value);
- }
- }
- }
- }
- } else if (tag_type != nullptr) {
- for (uint32_t i = 0; i < tag_type->data.enumeration.src_field_count; i += 1) {
- TypeEnumField *enum_field = &tag_type->data.enumeration.fields[i];
- if (!covered_enum_fields[i]) {
- ErrorMsg *msg = add_node_error(g, decl_node,
- buf_sprintf("enum field missing: '%s'", buf_ptr(enum_field->name)));
- if (decl_node->type == NodeTypeContainerDecl) {
- AstNode *enum_decl_node = tag_type->data.enumeration.decl_node;
- AstNode *field_node = enum_decl_node->data.container_decl.fields.at(i);
- add_error_note(g, msg, field_node,
- buf_sprintf("declared here"));
- }
- union_type->data.unionation.resolve_status = ResolveStatusInvalid;
- }
- }
- }
-
- if (union_type->data.unionation.resolve_status == ResolveStatusInvalid) {
- return ErrorSemanticAnalyzeFail;
- }
-
- union_type->data.unionation.resolve_loop_flag_zero_bits = false;
-
- union_type->data.unionation.gen_field_count = gen_field_index;
- bool zero_bits = gen_field_index == 0 && (field_count < 2 || !src_have_tag);
- if (!zero_bits) {
- union_type->abi_size = SIZE_MAX;
- union_type->size_in_bits = SIZE_MAX;
- }
- union_type->data.unionation.resolve_status = zero_bits ? ResolveStatusSizeKnown : ResolveStatusZeroBitsKnown;
-
- return ErrorNone;
-}
-
-void append_namespace_qualification(CodeGen *g, Buf *buf, ZigType *container_type) {
- if (g->root_import == container_type || buf_len(&container_type->name) == 0) return;
- buf_append_buf(buf, &container_type->name);
- buf_append_char(buf, NAMESPACE_SEP_CHAR);
-}
-
-static void get_fully_qualified_decl_name(CodeGen *g, Buf *buf, Tld *tld, bool is_test) {
- buf_resize(buf, 0);
-
- Scope *scope = tld->parent_scope;
- while (scope->id != ScopeIdDecls) {
- scope = scope->parent;
- }
- ScopeDecls *decls_scope = reinterpret_cast(scope);
- append_namespace_qualification(g, buf, decls_scope->container_type);
- if (is_test) {
- buf_append_str(buf, "test \"");
- buf_append_buf(buf, tld->name);
- buf_append_char(buf, '"');
- } else {
- buf_append_buf(buf, tld->name);
- }
-}
-
-static ZigFn *create_fn_raw(CodeGen *g, FnInline inline_value) {
- ZigFn *fn_entry = heap::c_allocator.create();
- fn_entry->ir_executable = heap::c_allocator.create();
-
- 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->fn_inline = inline_value;
-
- return fn_entry;
-}
-
-ZigFn *create_fn(CodeGen *g, AstNode *proto_node) {
- assert(proto_node->type == NodeTypeFnProto);
- AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
-
- ZigFn *fn_entry = create_fn_raw(g, fn_proto->fn_inline);
-
- fn_entry->proto_node = proto_node;
- fn_entry->body_node = (proto_node->data.fn_proto.fn_def_node == nullptr) ? nullptr :
- proto_node->data.fn_proto.fn_def_node->data.fn_def.body;
-
- fn_entry->analyzed_executable.source_node = fn_entry->body_node;
-
- return fn_entry;
-}
-
-ZigType *get_test_fn_type(CodeGen *g) {
- if (g->test_fn_type)
- return g->test_fn_type;
-
- FnTypeId fn_type_id = {0};
- fn_type_id.return_type = get_error_union_type(g, g->builtin_types.entry_global_error_set,
- g->builtin_types.entry_void);
- g->test_fn_type = get_fn_type(g, &fn_type_id);
- return g->test_fn_type;
-}
-
-void add_var_export(CodeGen *g, ZigVar *var, const char *symbol_name, GlobalLinkageId linkage) {
- GlobalExport *global_export = var->export_list.add_one();
- memset(global_export, 0, sizeof(GlobalExport));
- buf_init_from_str(&global_export->name, symbol_name);
- global_export->linkage = linkage;
-}
-
-void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, const char *symbol_name, GlobalLinkageId linkage, CallingConvention cc) {
- if (cc == CallingConventionC && strcmp(symbol_name, "main") == 0 && g->link_libc) {
- g->have_c_main = true;
- } else if (cc == CallingConventionStdcall && g->zig_target->os == OsWindows) {
- if (strcmp(symbol_name, "WinMain") == 0) {
- g->have_winmain = true;
- } else if (strcmp(symbol_name, "wWinMain") == 0) {
- g->have_wwinmain = true;
- } else if (strcmp(symbol_name, "WinMainCRTStartup") == 0) {
- g->have_winmain_crt_startup = true;
- } else if (strcmp(symbol_name, "wWinMainCRTStartup") == 0) {
- g->have_wwinmain_crt_startup = true;
- } else if (strcmp(symbol_name, "DllMainCRTStartup") == 0) {
- g->have_dllmain_crt_startup = true;
- }
- }
-
- GlobalExport *fn_export = fn_table_entry->export_list.add_one();
- memset(fn_export, 0, sizeof(GlobalExport));
- buf_init_from_str(&fn_export->name, symbol_name);
- fn_export->linkage = linkage;
-}
-
-static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
- AstNode *source_node = tld_fn->base.source_node;
- if (source_node->type == NodeTypeFnProto) {
- AstNodeFnProto *fn_proto = &source_node->data.fn_proto;
-
- AstNode *fn_def_node = fn_proto->fn_def_node;
-
- ZigFn *fn_table_entry = create_fn(g, source_node);
- tld_fn->fn_entry = fn_table_entry;
-
- bool is_extern = (fn_table_entry->body_node == nullptr);
- if (fn_proto->is_export || is_extern) {
- buf_init_from_buf(&fn_table_entry->symbol_name, tld_fn->base.name);
- } else {
- get_fully_qualified_decl_name(g, &fn_table_entry->symbol_name, &tld_fn->base, false);
- }
-
- if (!is_extern) {
- fn_table_entry->fndef_scope = create_fndef_scope(g,
- fn_table_entry->body_node, tld_fn->base.parent_scope, fn_table_entry);
-
- for (size_t i = 0; i < fn_proto->params.length; i += 1) {
- AstNode *param_node = fn_proto->params.at(i);
- assert(param_node->type == NodeTypeParamDecl);
- if (param_node->data.param_decl.name == nullptr) {
- add_node_error(g, param_node, buf_sprintf("missing parameter name"));
- }
- }
- } else {
- fn_table_entry->inferred_async_node = inferred_async_none;
- g->external_symbol_names.put_unique(tld_fn->base.name, &tld_fn->base);
- }
-
- Scope *child_scope = fn_table_entry->fndef_scope ? &fn_table_entry->fndef_scope->base : tld_fn->base.parent_scope;
-
- CallingConvention cc;
- if (fn_proto->callconv_expr != nullptr) {
- ZigType *cc_enum_value = get_builtin_type(g, "CallingConvention");
-
- ZigValue *result_val = analyze_const_value(g, child_scope, fn_proto->callconv_expr,
- cc_enum_value, nullptr, UndefBad);
- if (type_is_invalid(result_val->type)) {
- fn_table_entry->type_entry = g->builtin_types.entry_invalid;
- tld_fn->base.resolution = TldResolutionInvalid;
- return;
- }
-
- cc = (CallingConvention)bigint_as_u32(&result_val->data.x_enum_tag);
- } else {
- cc = cc_from_fn_proto(fn_proto);
- }
-
- if (fn_proto->section_expr != nullptr) {
- if (!analyze_const_string(g, child_scope, fn_proto->section_expr, &fn_table_entry->section_name)) {
- fn_table_entry->type_entry = g->builtin_types.entry_invalid;
- tld_fn->base.resolution = TldResolutionInvalid;
- return;
- }
- }
-
- fn_table_entry->type_entry = analyze_fn_type(g, source_node, child_scope, fn_table_entry, cc);
-
- if (type_is_invalid(fn_table_entry->type_entry)) {
- tld_fn->base.resolution = TldResolutionInvalid;
- return;
- }
-
- const CallingConvention fn_cc = fn_table_entry->type_entry->data.fn.fn_type_id.cc;
-
- if (fn_proto->is_export) {
- switch (fn_cc) {
- case CallingConventionAsync:
- add_node_error(g, fn_def_node,
- buf_sprintf("exported function cannot be async"));
- fn_table_entry->type_entry = g->builtin_types.entry_invalid;
- tld_fn->base.resolution = TldResolutionInvalid;
- return;
- case CallingConventionC:
- case CallingConventionCold:
- case CallingConventionNaked:
- case CallingConventionInterrupt:
- case CallingConventionSignal:
- case CallingConventionStdcall:
- case CallingConventionFastcall:
- case CallingConventionVectorcall:
- case CallingConventionThiscall:
- case CallingConventionAPCS:
- case CallingConventionAAPCS:
- case CallingConventionAAPCSVFP:
- add_fn_export(g, fn_table_entry, buf_ptr(&fn_table_entry->symbol_name),
- GlobalLinkageIdStrong, fn_cc);
- break;
- case CallingConventionUnspecified:
- // An exported function without a specific calling
- // convention defaults to C
- add_fn_export(g, fn_table_entry, buf_ptr(&fn_table_entry->symbol_name),
- GlobalLinkageIdStrong, CallingConventionC);
- break;
- }
- }
-
- if (!fn_table_entry->type_entry->data.fn.is_generic) {
- if (fn_def_node)
- g->fn_defs.append(fn_table_entry);
- }
-
- // if the calling convention implies that it cannot be async, we save that for later
- // and leave the value to be nullptr to indicate that we have not emitted possible
- // compile errors for improperly calling async functions.
- if (fn_cc == CallingConventionAsync) {
- fn_table_entry->inferred_async_node = fn_table_entry->proto_node;
- }
- } else if (source_node->type == NodeTypeTestDecl) {
- ZigFn *fn_table_entry = create_fn_raw(g, FnInlineAuto);
-
- get_fully_qualified_decl_name(g, &fn_table_entry->symbol_name, &tld_fn->base, true);
-
- tld_fn->fn_entry = fn_table_entry;
-
- fn_table_entry->proto_node = source_node;
- fn_table_entry->fndef_scope = create_fndef_scope(g, source_node, tld_fn->base.parent_scope, fn_table_entry);
- fn_table_entry->type_entry = get_test_fn_type(g);
- fn_table_entry->body_node = source_node->data.test_decl.body;
- fn_table_entry->is_test = true;
-
- g->fn_defs.append(fn_table_entry);
- g->test_fns.append(fn_table_entry);
-
- } else {
- zig_unreachable();
- }
-}
-
-static void resolve_decl_comptime(CodeGen *g, TldCompTime *tld_comptime) {
- assert(tld_comptime->base.source_node->type == NodeTypeCompTime);
- AstNode *expr_node = tld_comptime->base.source_node->data.comptime_expr.expr;
- analyze_const_value(g, tld_comptime->base.parent_scope, expr_node, g->builtin_types.entry_void,
- nullptr, UndefBad);
-}
-
-static void add_top_level_decl(CodeGen *g, ScopeDecls *decls_scope, Tld *tld) {
- bool is_export = false;
- if (tld->id == TldIdVar) {
- assert(tld->source_node->type == NodeTypeVariableDeclaration);
- is_export = tld->source_node->data.variable_declaration.is_export;
- } else if (tld->id == TldIdFn) {
- assert(tld->source_node->type == NodeTypeFnProto);
- is_export = tld->source_node->data.fn_proto.is_export;
-
- if (!tld->source_node->data.fn_proto.is_extern &&
- tld->source_node->data.fn_proto.fn_def_node == nullptr)
- {
- add_node_error(g, tld->source_node, buf_sprintf("non-extern function has no body"));
- return;
- }
- if (!tld->source_node->data.fn_proto.is_extern &&
- tld->source_node->data.fn_proto.is_var_args)
- {
- add_node_error(g, tld->source_node, buf_sprintf("non-extern function is variadic"));
- return;
- }
- } else if (tld->id == TldIdUsingNamespace) {
- g->resolve_queue.append(tld);
- }
- if (is_export) {
- g->resolve_queue.append(tld);
-
- auto entry = g->exported_symbol_names.put_unique(tld->name, tld);
- if (entry) {
- AstNode *other_source_node = entry->value->source_node;
- ErrorMsg *msg = add_node_error(g, tld->source_node,
- buf_sprintf("exported symbol collision: '%s'", buf_ptr(tld->name)));
- add_error_note(g, msg, other_source_node, buf_sprintf("other symbol here"));
- }
- }
-
- if (tld->name != nullptr) {
- auto entry = decls_scope->decl_table.put_unique(tld->name, tld);
- if (entry) {
- Tld *other_tld = entry->value;
- if (other_tld->id == TldIdVar) {
- ZigVar *var = reinterpret_cast(other_tld)->var;
- if (var != nullptr && var->var_type != nullptr && type_is_invalid(var->var_type)) {
- return; // already reported compile error
- }
- }
- ErrorMsg *msg = add_node_error(g, tld->source_node, buf_sprintf("redefinition of '%s'", buf_ptr(tld->name)));
- add_error_note(g, msg, other_tld->source_node, buf_sprintf("previous definition is here"));
- return;
- }
-
- ZigType *type;
- if (get_primitive_type(g, tld->name, &type) != ErrorPrimitiveTypeNotFound) {
- add_node_error(g, tld->source_node,
- buf_sprintf("declaration shadows primitive type '%s'", buf_ptr(tld->name)));
- }
- }
-}
-
-static void preview_test_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_scope) {
- assert(node->type == NodeTypeTestDecl);
-
- if (!g->is_test_build)
- return;
-
- ZigType *import = get_scope_import(&decls_scope->base);
- if (import->data.structure.root_struct->package != g->main_pkg)
- return;
-
- Buf *decl_name_buf = node->data.test_decl.name;
-
- Buf *test_name = g->test_name_prefix ?
- buf_sprintf("%s%s", buf_ptr(g->test_name_prefix), buf_ptr(decl_name_buf)) : decl_name_buf;
-
- if (g->test_filter != nullptr && strstr(buf_ptr(test_name), buf_ptr(g->test_filter)) == nullptr) {
- return;
- }
-
- TldFn *tld_fn = heap::c_allocator.create();
- init_tld(&tld_fn->base, TldIdFn, test_name, VisibModPrivate, node, &decls_scope->base);
- g->resolve_queue.append(&tld_fn->base);
-}
-
-static void preview_comptime_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_scope) {
- assert(node->type == NodeTypeCompTime);
-
- TldCompTime *tld_comptime = heap::c_allocator.create();
- init_tld(&tld_comptime->base, TldIdCompTime, nullptr, VisibModPrivate, node, &decls_scope->base);
- g->resolve_queue.append(&tld_comptime->base);
-}
-
-void init_tld(Tld *tld, TldId id, Buf *name, VisibMod visib_mod, AstNode *source_node,
- Scope *parent_scope)
-{
- tld->id = id;
- tld->name = name;
- tld->visib_mod = visib_mod;
- tld->source_node = source_node;
- tld->import = source_node ? source_node->owner : nullptr;
- tld->parent_scope = parent_scope;
-}
-
-void update_compile_var(CodeGen *g, Buf *name, ZigValue *value) {
- ScopeDecls *builtin_scope = get_container_scope(g->compile_var_import);
- Tld *tld = find_container_decl(g, builtin_scope, name);
- assert(tld != nullptr);
- resolve_top_level_decl(g, tld, tld->source_node, false);
- assert(tld->id == TldIdVar && tld->resolution == TldResolutionOk);
- TldVar *tld_var = (TldVar *)tld;
- copy_const_val(g, tld_var->var->const_value, value);
- tld_var->var->var_type = value->type;
- tld_var->var->align_bytes = get_abi_alignment(g, value->type);
-}
-
-void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
- switch (node->type) {
- case NodeTypeContainerDecl:
- for (size_t i = 0; i < node->data.container_decl.decls.length; i += 1) {
- AstNode *child = node->data.container_decl.decls.at(i);
- scan_decls(g, decls_scope, child);
- }
- break;
- case NodeTypeFnDef:
- scan_decls(g, decls_scope, node->data.fn_def.fn_proto);
- break;
- case NodeTypeVariableDeclaration:
- {
- Buf *name = node->data.variable_declaration.symbol;
- VisibMod visib_mod = node->data.variable_declaration.visib_mod;
- TldVar *tld_var = heap::c_allocator.create();
- init_tld(&tld_var->base, TldIdVar, name, visib_mod, node, &decls_scope->base);
- tld_var->extern_lib_name = node->data.variable_declaration.lib_name;
- add_top_level_decl(g, decls_scope, &tld_var->base);
- break;
- }
- case NodeTypeFnProto:
- {
- // if the name is missing, we immediately announce an error
- Buf *fn_name = node->data.fn_proto.name;
- if (fn_name == nullptr) {
- add_node_error(g, node, buf_sprintf("missing function name"));
- break;
- }
-
- VisibMod visib_mod = node->data.fn_proto.visib_mod;
- TldFn *tld_fn = heap::c_allocator.create();
- init_tld(&tld_fn->base, TldIdFn, fn_name, visib_mod, node, &decls_scope->base);
- tld_fn->extern_lib_name = node->data.fn_proto.lib_name;
- add_top_level_decl(g, decls_scope, &tld_fn->base);
-
- break;
- }
- case NodeTypeUsingNamespace: {
- VisibMod visib_mod = node->data.using_namespace.visib_mod;
- TldUsingNamespace *tld_using_namespace = heap::c_allocator.create();
- init_tld(&tld_using_namespace->base, TldIdUsingNamespace, nullptr, visib_mod, node, &decls_scope->base);
- add_top_level_decl(g, decls_scope, &tld_using_namespace->base);
- decls_scope->use_decls.append(tld_using_namespace);
- break;
- }
- case NodeTypeTestDecl:
- preview_test_decl(g, node, decls_scope);
- break;
- case NodeTypeCompTime:
- preview_comptime_decl(g, node, decls_scope);
- break;
- case NodeTypeNoSuspend:
- case NodeTypeParamDecl:
- case NodeTypeReturnExpr:
- case NodeTypeDefer:
- case NodeTypeBlock:
- case NodeTypeGroupedExpr:
- case NodeTypeBinOpExpr:
- case NodeTypeCatchExpr:
- case NodeTypeFnCallExpr:
- case NodeTypeArrayAccessExpr:
- case NodeTypeSliceExpr:
- case NodeTypeFloatLiteral:
- case NodeTypeIntLiteral:
- case NodeTypeStringLiteral:
- case NodeTypeCharLiteral:
- case NodeTypeBoolLiteral:
- case NodeTypeNullLiteral:
- case NodeTypeUndefinedLiteral:
- case NodeTypeSymbol:
- case NodeTypePrefixOpExpr:
- case NodeTypePointerType:
- case NodeTypeIfBoolExpr:
- case NodeTypeWhileExpr:
- case NodeTypeForExpr:
- case NodeTypeSwitchExpr:
- case NodeTypeSwitchProng:
- case NodeTypeSwitchRange:
- case NodeTypeBreak:
- case NodeTypeContinue:
- case NodeTypeUnreachable:
- case NodeTypeAsmExpr:
- case NodeTypeFieldAccessExpr:
- case NodeTypePtrDeref:
- case NodeTypeUnwrapOptional:
- case NodeTypeStructField:
- case NodeTypeContainerInitExpr:
- case NodeTypeStructValueField:
- case NodeTypeArrayType:
- case NodeTypeInferredArrayType:
- case NodeTypeErrorType:
- case NodeTypeIfErrorExpr:
- case NodeTypeIfOptional:
- case NodeTypeErrorSetDecl:
- case NodeTypeResume:
- case NodeTypeAwaitExpr:
- case NodeTypeSuspend:
- case NodeTypeEnumLiteral:
- case NodeTypeAnyFrameType:
- case NodeTypeErrorSetField:
- case NodeTypeAnyTypeField:
- zig_unreachable();
- }
-}
-
-static Error resolve_decl_container(CodeGen *g, TldContainer *tld_container) {
- ZigType *type_entry = tld_container->type_entry;
- assert(type_entry);
-
- switch (type_entry->id) {
- case ZigTypeIdStruct:
- return resolve_struct_type(g, tld_container->type_entry);
- case ZigTypeIdEnum:
- return resolve_enum_zero_bits(g, tld_container->type_entry);
- case ZigTypeIdUnion:
- return resolve_union_type(g, tld_container->type_entry);
- default:
- zig_unreachable();
- }
-}
-
-ZigType *validate_var_type(CodeGen *g, AstNodeVariableDeclaration *source_node, ZigType *type_entry) {
- switch (type_entry->id) {
- case ZigTypeIdInvalid:
- return g->builtin_types.entry_invalid;
- case ZigTypeIdOpaque:
- if (source_node->is_extern)
- return type_entry;
- ZIG_FALLTHROUGH;
- case ZigTypeIdUnreachable:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- add_node_error(g, source_node->type, buf_sprintf("variable of type '%s' not allowed",
- buf_ptr(&type_entry->name)));
- return g->builtin_types.entry_invalid;
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdEnumLiteral:
- case ZigTypeIdMetaType:
- case ZigTypeIdVoid:
- case ZigTypeIdBool:
- case ZigTypeIdInt:
- case ZigTypeIdFloat:
- case ZigTypeIdPointer:
- case ZigTypeIdArray:
- case ZigTypeIdStruct:
- case ZigTypeIdOptional:
- case ZigTypeIdErrorUnion:
- case ZigTypeIdErrorSet:
- case ZigTypeIdEnum:
- case ZigTypeIdUnion:
- case ZigTypeIdFn:
- case ZigTypeIdBoundFn:
- case ZigTypeIdVector:
- case ZigTypeIdFnFrame:
- case ZigTypeIdAnyFrame:
- return type_entry;
- }
- zig_unreachable();
-}
-
-// Set name to nullptr to make the variable anonymous (not visible to programmer).
-// TODO merge with definition of add_local_var in ir.cpp
-ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf *name,
- bool is_const, ZigValue *const_value, Tld *src_tld, ZigType *var_type)
-{
- Error err;
- assert(const_value != nullptr);
- assert(var_type != nullptr);
-
- ZigVar *variable_entry = heap::c_allocator.create();
- variable_entry->const_value = const_value;
- variable_entry->var_type = var_type;
- variable_entry->parent_scope = parent_scope;
- variable_entry->shadowable = false;
- variable_entry->src_arg_index = SIZE_MAX;
-
- assert(name);
- variable_entry->name = strdup(buf_ptr(name));
-
- if ((err = type_resolve(g, var_type, ResolveStatusAlignmentKnown))) {
- variable_entry->var_type = g->builtin_types.entry_invalid;
- } else {
- variable_entry->align_bytes = get_abi_alignment(g, var_type);
-
- ZigVar *existing_var = find_variable(g, parent_scope, name, nullptr);
- if (existing_var && !existing_var->shadowable) {
- if (existing_var->var_type == nullptr || !type_is_invalid(existing_var->var_type)) {
- ErrorMsg *msg = add_node_error(g, source_node,
- buf_sprintf("redeclaration of variable '%s'", buf_ptr(name)));
- add_error_note(g, msg, existing_var->decl_node, buf_sprintf("previous declaration is here"));
- }
- variable_entry->var_type = g->builtin_types.entry_invalid;
- } else {
- ZigType *type;
- if (get_primitive_type(g, name, &type) != ErrorPrimitiveTypeNotFound) {
- add_node_error(g, source_node,
- buf_sprintf("variable shadows primitive type '%s'", buf_ptr(name)));
- variable_entry->var_type = g->builtin_types.entry_invalid;
- } else {
- Scope *search_scope = nullptr;
- if (src_tld == nullptr) {
- search_scope = parent_scope;
- } else if (src_tld->parent_scope != nullptr && src_tld->parent_scope->parent != nullptr) {
- search_scope = src_tld->parent_scope->parent;
- }
- if (search_scope != nullptr) {
- Tld *tld = find_decl(g, search_scope, name);
- if (tld != nullptr && tld != src_tld) {
- bool want_err_msg = true;
- if (tld->id == TldIdVar) {
- ZigVar *var = reinterpret_cast(tld)->var;
- if (var != nullptr && var->var_type != nullptr && type_is_invalid(var->var_type)) {
- want_err_msg = false;
- }
- }
- if (want_err_msg) {
- ErrorMsg *msg = add_node_error(g, source_node,
- buf_sprintf("redefinition of '%s'", buf_ptr(name)));
- add_error_note(g, msg, tld->source_node, buf_sprintf("previous definition is here"));
- }
- variable_entry->var_type = g->builtin_types.entry_invalid;
- }
- }
- }
- }
- }
-
- Scope *child_scope;
- if (source_node && source_node->type == NodeTypeParamDecl) {
- child_scope = create_var_scope(g, source_node, parent_scope, variable_entry);
- } else {
- // it's already in the decls table
- child_scope = parent_scope;
- }
-
-
- variable_entry->src_is_const = is_const;
- variable_entry->gen_is_const = is_const;
- variable_entry->decl_node = source_node;
- variable_entry->child_scope = child_scope;
-
-
- 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;
-
- bool is_const = var_decl->is_const;
- bool is_extern = var_decl->is_extern;
- bool is_export = var_decl->is_export;
- bool is_thread_local = var_decl->threadlocal_tok != nullptr;
-
- ZigType *explicit_type = nullptr;
- if (var_decl->type) {
- if (tld_var->analyzing_type) {
- add_node_error(g, var_decl->type,
- buf_sprintf("type of '%s' depends on itself", buf_ptr(tld_var->base.name)));
- explicit_type = g->builtin_types.entry_invalid;
- } else {
- tld_var->analyzing_type = true;
- ZigType *proposed_type = analyze_type_expr(g, tld_var->base.parent_scope, var_decl->type);
- explicit_type = validate_var_type(g, var_decl, proposed_type);
- }
- }
-
- assert(!is_export || !is_extern);
-
- ZigValue *init_value = nullptr;
-
- // TODO more validation for types that can't be used for export/extern variables
- ZigType *implicit_type = nullptr;
- if (explicit_type != nullptr && type_is_invalid(explicit_type)) {
- implicit_type = explicit_type;
- } else if (var_decl->expr) {
- init_value = analyze_const_value(g, tld_var->base.parent_scope, var_decl->expr, explicit_type,
- var_decl->symbol, allow_lazy ? LazyOk : UndefOk);
- assert(init_value);
- implicit_type = init_value->type;
-
- if (implicit_type->id == ZigTypeIdUnreachable) {
- add_node_error(g, source_node, buf_sprintf("variable initialization is unreachable"));
- implicit_type = g->builtin_types.entry_invalid;
- } else if ((!is_const || is_extern) &&
- (implicit_type->id == ZigTypeIdComptimeFloat ||
- implicit_type->id == ZigTypeIdComptimeInt ||
- implicit_type->id == ZigTypeIdEnumLiteral))
- {
- add_node_error(g, source_node, buf_sprintf("unable to infer variable type"));
- implicit_type = g->builtin_types.entry_invalid;
- } else if (implicit_type->id == ZigTypeIdNull) {
- add_node_error(g, source_node, buf_sprintf("unable to infer variable type"));
- implicit_type = g->builtin_types.entry_invalid;
- } else if (implicit_type->id == ZigTypeIdMetaType && !is_const) {
- add_node_error(g, source_node, buf_sprintf("variable of type 'type' must be constant"));
- implicit_type = g->builtin_types.entry_invalid;
- }
- assert(implicit_type->id == ZigTypeIdInvalid || init_value->special != ConstValSpecialRuntime);
- } else if (!is_extern) {
- add_node_error(g, source_node, buf_sprintf("variables must be initialized"));
- implicit_type = g->builtin_types.entry_invalid;
- } else if (explicit_type == nullptr) {
- // extern variable without explicit type
- add_node_error(g, source_node, buf_sprintf("unable to infer variable type"));
- implicit_type = g->builtin_types.entry_invalid;
- }
-
- ZigType *type = explicit_type ? explicit_type : implicit_type;
- assert(type != nullptr); // should have been caught by the parser
-
- ZigValue *init_val = (init_value != nullptr) ? init_value : create_const_runtime(g, type);
-
- tld_var->var = add_variable(g, source_node, tld_var->base.parent_scope, var_decl->symbol,
- is_const, init_val, &tld_var->base, type);
- tld_var->var->is_thread_local = is_thread_local;
-
- if (implicit_type != nullptr && type_is_invalid(implicit_type)) {
- tld_var->var->var_type = g->builtin_types.entry_invalid;
- }
-
- if (var_decl->align_expr != nullptr) {
- if (!analyze_const_align(g, tld_var->base.parent_scope, var_decl->align_expr, &tld_var->var->align_bytes)) {
- tld_var->var->var_type = g->builtin_types.entry_invalid;
- }
- }
-
- if (var_decl->section_expr != nullptr) {
- if (!analyze_const_string(g, tld_var->base.parent_scope, var_decl->section_expr, &tld_var->var->section_name)) {
- tld_var->var->section_name = nullptr;
- }
- }
-
- if (is_thread_local && is_const) {
- add_node_error(g, source_node, buf_sprintf("threadlocal variable cannot be constant"));
- }
-
- if (is_export) {
- validate_export_var_type(g, type, source_node);
- add_var_export(g, tld_var->var, tld_var->var->name, GlobalLinkageIdStrong);
- }
-
- if (is_extern) {
- g->external_symbol_names.put_unique(tld_var->base.name, &tld_var->base);
- }
-
- g->global_vars.append(tld_var);
-}
-
-static void add_symbols_from_container(CodeGen *g, TldUsingNamespace *src_using_namespace,
- TldUsingNamespace *dst_using_namespace, ScopeDecls* dest_decls_scope)
-{
- if (src_using_namespace->base.resolution == TldResolutionUnresolved ||
- src_using_namespace->base.resolution == TldResolutionResolving)
- {
- assert(src_using_namespace->base.parent_scope->id == ScopeIdDecls);
- ScopeDecls *src_decls_scope = (ScopeDecls *)src_using_namespace->base.parent_scope;
- preview_use_decl(g, src_using_namespace, src_decls_scope);
- if (src_using_namespace != dst_using_namespace) {
- resolve_use_decl(g, src_using_namespace, src_decls_scope);
- }
- }
-
- ZigValue *use_expr = src_using_namespace->using_namespace_value;
- if (type_is_invalid(use_expr->type)) {
- dest_decls_scope->any_imports_failed = true;
- return;
- }
-
- dst_using_namespace->base.resolution = TldResolutionOk;
-
- assert(use_expr->special != ConstValSpecialRuntime);
-
- // The source scope for the imported symbols
- ScopeDecls *src_scope = get_container_scope(use_expr->data.x_type);
- // The top-level container where the symbols are defined, it's used in the
- // loop below in order to exclude the ones coming from an import statement
- ZigType *src_import = get_scope_import(&src_scope->base);
- assert(src_import != nullptr);
-
- if (src_scope->any_imports_failed) {
- dest_decls_scope->any_imports_failed = true;
- }
-
- auto it = src_scope->decl_table.entry_iterator();
- for (;;) {
- auto *entry = it.next();
- if (!entry)
- break;
-
- Buf *target_tld_name = entry->key;
- Tld *target_tld = entry->value;
-
- if (target_tld->visib_mod == VisibModPrivate) {
- continue;
- }
-
- if (target_tld->import != src_import) {
- continue;
- }
-
- auto existing_entry = dest_decls_scope->decl_table.put_unique(target_tld_name, target_tld);
- if (existing_entry) {
- Tld *existing_decl = existing_entry->value;
- if (existing_decl != target_tld) {
- ErrorMsg *msg = add_node_error(g, dst_using_namespace->base.source_node,
- buf_sprintf("import of '%s' overrides existing definition",
- buf_ptr(target_tld_name)));
- add_error_note(g, msg, existing_decl->source_node, buf_sprintf("previous definition here"));
- add_error_note(g, msg, target_tld->source_node, buf_sprintf("imported definition here"));
- }
- }
- }
-
- for (size_t i = 0; i < src_scope->use_decls.length; i += 1) {
- TldUsingNamespace *tld_using_namespace = src_scope->use_decls.at(i);
- if (tld_using_namespace->base.visib_mod != VisibModPrivate)
- add_symbols_from_container(g, tld_using_namespace, dst_using_namespace, dest_decls_scope);
- }
-}
-
-static void resolve_use_decl(CodeGen *g, TldUsingNamespace *tld_using_namespace, ScopeDecls *dest_decls_scope) {
- if (tld_using_namespace->base.resolution == TldResolutionOk ||
- tld_using_namespace->base.resolution == TldResolutionInvalid)
- {
- return;
- }
- add_symbols_from_container(g, tld_using_namespace, tld_using_namespace, dest_decls_scope);
-}
-
-static void preview_use_decl(CodeGen *g, TldUsingNamespace *using_namespace, ScopeDecls *dest_decls_scope) {
- if (using_namespace->base.resolution == TldResolutionOk ||
- using_namespace->base.resolution == TldResolutionInvalid ||
- using_namespace->using_namespace_value != nullptr)
- {
- return;
- }
-
- using_namespace->base.resolution = TldResolutionResolving;
- assert(using_namespace->base.source_node->type == NodeTypeUsingNamespace);
- ZigValue *result = analyze_const_value(g, &dest_decls_scope->base,
- using_namespace->base.source_node->data.using_namespace.expr, g->builtin_types.entry_type,
- nullptr, UndefBad);
- using_namespace->using_namespace_value = result;
-
- if (type_is_invalid(result->type)) {
- dest_decls_scope->any_imports_failed = true;
- using_namespace->base.resolution = TldResolutionInvalid;
- using_namespace->using_namespace_value = g->invalid_inst_gen->value;
- return;
- }
-
- if (!is_container(result->data.x_type)) {
- add_node_error(g, using_namespace->base.source_node,
- buf_sprintf("expected struct, enum, or union; found '%s'", buf_ptr(&result->data.x_type->name)));
- dest_decls_scope->any_imports_failed = true;
- using_namespace->base.resolution = TldResolutionInvalid;
- using_namespace->using_namespace_value = g->invalid_inst_gen->value;
- return;
- }
-}
-
-void resolve_top_level_decl(CodeGen *g, Tld *tld, AstNode *source_node, bool allow_lazy) {
- bool want_resolve_lazy = tld->resolution == TldResolutionOkLazy && !allow_lazy;
- if (tld->resolution != TldResolutionUnresolved && !want_resolve_lazy)
- return;
-
- tld->resolution = TldResolutionResolving;
- update_progress_display(g);
-
- switch (tld->id) {
- case TldIdVar: {
- TldVar *tld_var = (TldVar *)tld;
- if (want_resolve_lazy) {
- ir_resolve_lazy(g, source_node, tld_var->var->const_value);
- } else {
- resolve_decl_var(g, tld_var, allow_lazy);
- }
- tld->resolution = allow_lazy ? TldResolutionOkLazy : TldResolutionOk;
- break;
- }
- case TldIdFn: {
- TldFn *tld_fn = (TldFn *)tld;
- resolve_decl_fn(g, tld_fn);
-
- tld->resolution = TldResolutionOk;
- break;
- }
- case TldIdContainer: {
- TldContainer *tld_container = (TldContainer *)tld;
- resolve_decl_container(g, tld_container);
-
- tld->resolution = TldResolutionOk;
- break;
- }
- case TldIdCompTime: {
- TldCompTime *tld_comptime = (TldCompTime *)tld;
- resolve_decl_comptime(g, tld_comptime);
-
- tld->resolution = TldResolutionOk;
- break;
- }
- case TldIdUsingNamespace: {
- TldUsingNamespace *tld_using_namespace = (TldUsingNamespace *)tld;
- assert(tld_using_namespace->base.parent_scope->id == ScopeIdDecls);
- ScopeDecls *dest_decls_scope = (ScopeDecls *)tld_using_namespace->base.parent_scope;
- preview_use_decl(g, tld_using_namespace, dest_decls_scope);
- resolve_use_decl(g, tld_using_namespace, dest_decls_scope);
-
- tld->resolution = TldResolutionOk;
- break;
- }
- }
-
- if (g->trace_err != nullptr && source_node != nullptr && !source_node->already_traced_this_node) {
- g->trace_err = add_error_note(g, g->trace_err, source_node, buf_create_from_str("referenced here"));
- source_node->already_traced_this_node = true;
- }
-}
-
-Tld *find_container_decl(CodeGen *g, ScopeDecls *decls_scope, Buf *name) {
- // resolve all the using_namespace decls
- for (size_t i = 0; i < decls_scope->use_decls.length; i += 1) {
- TldUsingNamespace *tld_using_namespace = decls_scope->use_decls.at(i);
- if (tld_using_namespace->base.resolution == TldResolutionUnresolved) {
- preview_use_decl(g, tld_using_namespace, decls_scope);
- resolve_use_decl(g, tld_using_namespace, decls_scope);
- }
- }
-
- auto entry = decls_scope->decl_table.maybe_get(name);
- return (entry == nullptr) ? nullptr : entry->value;
-}
-
-Tld *find_decl(CodeGen *g, Scope *scope, Buf *name) {
- while (scope) {
- if (scope->id == ScopeIdDecls) {
- ScopeDecls *decls_scope = (ScopeDecls *)scope;
-
- Tld *result = find_container_decl(g, decls_scope, name);
- if (result != nullptr)
- return result;
- }
- scope = scope->parent;
- }
- return nullptr;
-}
-
-ZigVar *find_variable(CodeGen *g, Scope *scope, Buf *name, ScopeFnDef **crossed_fndef_scope) {
- ScopeFnDef *my_crossed_fndef_scope = nullptr;
- while (scope) {
- if (scope->id == ScopeIdVarDecl) {
- ScopeVarDecl *var_scope = (ScopeVarDecl *)scope;
- if (buf_eql_str(name, var_scope->var->name)) {
- if (crossed_fndef_scope != nullptr)
- *crossed_fndef_scope = my_crossed_fndef_scope;
- return var_scope->var;
- }
- } else if (scope->id == ScopeIdDecls) {
- ScopeDecls *decls_scope = (ScopeDecls *)scope;
- auto entry = decls_scope->decl_table.maybe_get(name);
- if (entry) {
- Tld *tld = entry->value;
- if (tld->id == TldIdVar) {
- TldVar *tld_var = (TldVar *)tld;
- if (tld_var->var) {
- if (crossed_fndef_scope != nullptr)
- *crossed_fndef_scope = nullptr;
- return tld_var->var;
- }
- }
- }
- } else if (scope->id == ScopeIdFnDef) {
- my_crossed_fndef_scope = (ScopeFnDef *)scope;
- }
- scope = scope->parent;
- }
-
- return nullptr;
-}
-
-ZigFn *scope_fn_entry(Scope *scope) {
- while (scope) {
- if (scope->id == ScopeIdFnDef) {
- ScopeFnDef *fn_scope = (ScopeFnDef *)scope;
- return fn_scope->fn_entry;
- }
- scope = scope->parent;
- }
- return nullptr;
-}
-
-ZigPackage *scope_package(Scope *scope) {
- ZigType *import = get_scope_import(scope);
- assert(is_top_level_struct(import));
- return import->data.structure.root_struct->package;
-}
-
-TypeEnumField *find_enum_type_field(ZigType *enum_type, Buf *name) {
- assert(enum_type->id == ZigTypeIdEnum);
- if (enum_type->data.enumeration.src_field_count == 0)
- return nullptr;
- auto entry = enum_type->data.enumeration.fields_by_name.maybe_get(name);
- if (entry == nullptr)
- return nullptr;
- return entry->value;
-}
-
-TypeStructField *find_struct_type_field(ZigType *type_entry, Buf *name) {
- assert(type_entry->id == ZigTypeIdStruct);
- if (type_entry->data.structure.resolve_status == ResolveStatusBeingInferred) {
- for (size_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) {
- TypeStructField *field = type_entry->data.structure.fields[i];
- if (buf_eql_buf(field->name, name))
- return field;
- }
- return nullptr;
- } else {
- assert(type_is_resolved(type_entry, ResolveStatusZeroBitsKnown));
- if (type_entry->data.structure.src_field_count == 0)
- return nullptr;
- auto entry = type_entry->data.structure.fields_by_name.maybe_get(name);
- if (entry == nullptr)
- return nullptr;
- return entry->value;
- }
-}
-
-TypeUnionField *find_union_type_field(ZigType *type_entry, Buf *name) {
- assert(type_entry->id == ZigTypeIdUnion);
- assert(type_is_resolved(type_entry, ResolveStatusZeroBitsKnown));
- if (type_entry->data.unionation.src_field_count == 0)
- return nullptr;
- auto entry = type_entry->data.unionation.fields_by_name.maybe_get(name);
- if (entry == nullptr)
- return nullptr;
- return entry->value;
-}
-
-TypeUnionField *find_union_field_by_tag(ZigType *type_entry, const BigInt *tag) {
- assert(type_entry->id == ZigTypeIdUnion);
- assert(type_is_resolved(type_entry, ResolveStatusZeroBitsKnown));
- for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) {
- TypeUnionField *field = &type_entry->data.unionation.fields[i];
- if (bigint_cmp(&field->enum_field->value, tag) == CmpEQ) {
- return field;
- }
- }
- return nullptr;
-}
-
-TypeEnumField *find_enum_field_by_tag(ZigType *enum_type, const BigInt *tag) {
- assert(type_is_resolved(enum_type, ResolveStatusZeroBitsKnown));
- for (uint32_t i = 0; i < enum_type->data.enumeration.src_field_count; i += 1) {
- TypeEnumField *field = &enum_type->data.enumeration.fields[i];
- if (bigint_cmp(&field->value, tag) == CmpEQ) {
- return field;
- }
- }
- return nullptr;
-}
-
-
-bool is_container(ZigType *type_entry) {
- switch (type_entry->id) {
- case ZigTypeIdInvalid:
- zig_unreachable();
- case ZigTypeIdStruct:
- return type_entry->data.structure.special != StructSpecialSlice;
- case ZigTypeIdEnum:
- case ZigTypeIdUnion:
- return true;
- case ZigTypeIdPointer:
- case ZigTypeIdMetaType:
- case ZigTypeIdVoid:
- case ZigTypeIdBool:
- case ZigTypeIdUnreachable:
- case ZigTypeIdInt:
- case ZigTypeIdFloat:
- case ZigTypeIdArray:
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdEnumLiteral:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdOptional:
- case ZigTypeIdErrorUnion:
- case ZigTypeIdErrorSet:
- case ZigTypeIdFn:
- case ZigTypeIdBoundFn:
- case ZigTypeIdOpaque:
- case ZigTypeIdVector:
- case ZigTypeIdFnFrame:
- case ZigTypeIdAnyFrame:
- return false;
- }
- zig_unreachable();
-}
-
-bool is_ref(ZigType *type_entry) {
- return type_entry->id == ZigTypeIdPointer && type_entry->data.pointer.ptr_len == PtrLenSingle;
-}
-
-bool is_array_ref(ZigType *type_entry) {
- ZigType *array = is_ref(type_entry) ?
- type_entry->data.pointer.child_type : type_entry;
- return array->id == ZigTypeIdArray;
-}
-
-bool is_container_ref(ZigType *parent_ty) {
- ZigType *ty = is_ref(parent_ty) ? parent_ty->data.pointer.child_type : parent_ty;
- return is_slice(ty) || is_container(ty);
-}
-
-ZigType *container_ref_type(ZigType *type_entry) {
- assert(is_container_ref(type_entry));
- return is_ref(type_entry) ?
- type_entry->data.pointer.child_type : type_entry;
-}
-
-ZigType *get_src_ptr_type(ZigType *type) {
- if (type->id == ZigTypeIdPointer) return type;
- if (type->id == ZigTypeIdFn) return type;
- if (type->id == ZigTypeIdAnyFrame) return type;
- if (type->id == ZigTypeIdOptional) {
- if (type->data.maybe.child_type->id == ZigTypeIdPointer) {
- return type->data.maybe.child_type->data.pointer.allow_zero ? nullptr : type->data.maybe.child_type;
- }
- if (type->data.maybe.child_type->id == ZigTypeIdFn) return type->data.maybe.child_type;
- if (type->data.maybe.child_type->id == ZigTypeIdAnyFrame) return type->data.maybe.child_type;
- }
- return nullptr;
-}
-
-Error get_codegen_ptr_type(CodeGen *g, ZigType *type, ZigType **result) {
- Error err;
-
- ZigType *ty = get_src_ptr_type(type);
- if (ty == nullptr) {
- *result = nullptr;
- return ErrorNone;
- }
-
- bool has_bits;
- if ((err = type_has_bits2(g, ty, &has_bits))) return err;
- if (!has_bits) {
- *result = nullptr;
- return ErrorNone;
- }
-
- *result = ty;
- return ErrorNone;
-}
-
-ZigType *get_codegen_ptr_type_bail(CodeGen *g, ZigType *type) {
- Error err;
- ZigType *result;
- if ((err = get_codegen_ptr_type(g, type, &result))) {
- codegen_report_errors_and_exit(g);
- }
- return result;
-}
-
-bool type_is_nonnull_ptr(CodeGen *g, ZigType *type) {
- Error err;
- bool result;
- if ((err = type_is_nonnull_ptr2(g, type, &result))) {
- codegen_report_errors_and_exit(g);
- }
- return result;
-}
-
-Error type_is_nonnull_ptr2(CodeGen *g, ZigType *type, bool *result) {
- Error err;
- ZigType *ptr_type;
- if ((err = get_codegen_ptr_type(g, type, &ptr_type))) return err;
- *result = ptr_type == type && !ptr_allows_addr_zero(type);
- return ErrorNone;
-}
-
-static uint32_t get_async_frame_align_bytes(CodeGen *g) {
- uint32_t a = g->pointer_size_bytes * 2;
- // promises have at least alignment 8 so that we can have 3 extra bits when doing atomicrmw
- if (a < 8) a = 8;
- return a;
-}
-
-uint32_t get_ptr_align(CodeGen *g, ZigType *type) {
- ZigType *ptr_type;
- if (type->id == ZigTypeIdStruct) {
- assert(type->data.structure.special == StructSpecialSlice);
- TypeStructField *ptr_field = type->data.structure.fields[slice_ptr_index];
- ptr_type = resolve_struct_field_type(g, ptr_field);
- } else {
- ptr_type = get_src_ptr_type(type);
- }
- if (ptr_type->id == ZigTypeIdPointer) {
- return (ptr_type->data.pointer.explicit_alignment == 0) ?
- get_abi_alignment(g, ptr_type->data.pointer.child_type) : ptr_type->data.pointer.explicit_alignment;
- } else if (ptr_type->id == ZigTypeIdFn) {
- // I tried making this use LLVMABIAlignmentOfType but it trips this assertion in LLVM:
- // "Cannot getTypeInfo() on a type that is unsized!"
- // when getting the alignment of `?fn() callconv(.C) void`.
- // See http://lists.llvm.org/pipermail/llvm-dev/2018-September/126142.html
- return (ptr_type->data.fn.fn_type_id.alignment == 0) ? 1 : ptr_type->data.fn.fn_type_id.alignment;
- } else if (ptr_type->id == ZigTypeIdAnyFrame) {
- return get_async_frame_align_bytes(g);
- } else {
- zig_unreachable();
- }
-}
-
-bool get_ptr_const(CodeGen *g, ZigType *type) {
- ZigType *ptr_type;
- if (type->id == ZigTypeIdStruct) {
- assert(type->data.structure.special == StructSpecialSlice);
- TypeStructField *ptr_field = type->data.structure.fields[slice_ptr_index];
- ptr_type = resolve_struct_field_type(g, ptr_field);
- } else {
- ptr_type = get_src_ptr_type(type);
- }
- if (ptr_type->id == ZigTypeIdPointer) {
- return ptr_type->data.pointer.is_const;
- } else if (ptr_type->id == ZigTypeIdFn) {
- return true;
- } else if (ptr_type->id == ZigTypeIdAnyFrame) {
- return true;
- } else {
- zig_unreachable();
- }
-}
-
-AstNode *get_param_decl_node(ZigFn *fn_entry, size_t index) {
- if (fn_entry->param_source_nodes)
- return fn_entry->param_source_nodes[index];
- else if (fn_entry->proto_node)
- return fn_entry->proto_node->data.fn_proto.params.at(index);
- else
- return nullptr;
-}
-
-static Error define_local_param_variables(CodeGen *g, ZigFn *fn_table_entry) {
- Error err;
- ZigType *fn_type = fn_table_entry->type_entry;
- assert(!fn_type->data.fn.is_generic);
- FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
- for (size_t i = 0; i < fn_type_id->param_count; i += 1) {
- FnTypeParamInfo *param_info = &fn_type_id->param_info[i];
- AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i);
- Buf *param_name;
- bool is_var_args = param_decl_node && param_decl_node->data.param_decl.is_var_args;
- if (param_decl_node && !is_var_args) {
- param_name = param_decl_node->data.param_decl.name;
- } else {
- param_name = buf_sprintf("arg%" ZIG_PRI_usize "", i);
- }
- if (param_name == nullptr) {
- continue;
- }
-
- ZigType *param_type = param_info->type;
- if ((err = type_resolve(g, param_type, ResolveStatusSizeKnown))) {
- return err;
- }
-
- bool is_noalias = param_info->is_noalias;
- if (is_noalias) {
- ZigType *ptr_type;
- if ((err = get_codegen_ptr_type(g, param_type, &ptr_type))) return err;
- if (ptr_type == nullptr) {
- add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter"));
- }
- }
-
- ZigVar *var = add_variable(g, param_decl_node, fn_table_entry->child_scope,
- param_name, true, create_const_runtime(g, param_type), nullptr, param_type);
- var->src_arg_index = i;
- fn_table_entry->child_scope = var->child_scope;
- var->shadowable = var->shadowable || is_var_args;
-
- if (type_has_bits(g, param_type)) {
- fn_table_entry->variable_list.append(var);
- }
- }
-
- return ErrorNone;
-}
-
-bool resolve_inferred_error_set(CodeGen *g, ZigType *err_set_type, AstNode *source_node) {
- assert(err_set_type->id == ZigTypeIdErrorSet);
- ZigFn *infer_fn = err_set_type->data.error_set.infer_fn;
- if (infer_fn != nullptr && err_set_type->data.error_set.incomplete) {
- if (infer_fn->anal_state == FnAnalStateInvalid) {
- return false;
- } else if (infer_fn->anal_state == FnAnalStateReady) {
- analyze_fn_body(g, infer_fn);
- if (infer_fn->anal_state == FnAnalStateInvalid ||
- err_set_type->data.error_set.incomplete)
- {
- assert(g->errors.length != 0);
- return false;
- }
- } else {
- add_node_error(g, source_node,
- buf_sprintf("cannot resolve inferred error set '%s': function '%s' not fully analyzed yet",
- buf_ptr(&err_set_type->name), buf_ptr(&err_set_type->data.error_set.infer_fn->symbol_name)));
- return false;
- }
- }
- return true;
-}
-
-static void resolve_async_fn_frame(CodeGen *g, ZigFn *fn) {
- ZigType *frame_type = get_fn_frame_type(g, fn);
- Error err;
- if ((err = type_resolve(g, frame_type, ResolveStatusSizeKnown))) {
- if (g->trace_err != nullptr && frame_type->data.frame.resolve_loop_src_node != nullptr &&
- !frame_type->data.frame.reported_loop_err)
- {
- frame_type->data.frame.reported_loop_err = true;
- g->trace_err = add_error_note(g, g->trace_err, frame_type->data.frame.resolve_loop_src_node,
- buf_sprintf("when analyzing type '%s' here", buf_ptr(&frame_type->name)));
- }
- fn->anal_state = FnAnalStateInvalid;
- return;
- }
-}
-
-bool fn_is_async(ZigFn *fn) {
- assert(fn->inferred_async_node != nullptr);
- assert(fn->inferred_async_node != inferred_async_checking);
- return fn->inferred_async_node != inferred_async_none;
-}
-
-void add_async_error_notes(CodeGen *g, ErrorMsg *msg, ZigFn *fn) {
- assert(fn->inferred_async_node != nullptr);
- assert(fn->inferred_async_node != inferred_async_checking);
- assert(fn->inferred_async_node != inferred_async_none);
- if (fn->inferred_async_fn != nullptr) {
- ErrorMsg *new_msg;
- if (fn->inferred_async_node->type == NodeTypeAwaitExpr) {
- new_msg = add_error_note(g, msg, fn->inferred_async_node,
- buf_create_from_str("await here is a suspend point"));
- } else {
- new_msg = add_error_note(g, msg, fn->inferred_async_node,
- buf_sprintf("async function call here"));
- }
- return add_async_error_notes(g, new_msg, fn->inferred_async_fn);
- } else if (fn->inferred_async_node->type == NodeTypeFnProto) {
- add_error_note(g, msg, fn->inferred_async_node,
- buf_sprintf("async calling convention here"));
- } else if (fn->inferred_async_node->type == NodeTypeSuspend) {
- add_error_note(g, msg, fn->inferred_async_node,
- buf_sprintf("suspends here"));
- } else if (fn->inferred_async_node->type == NodeTypeAwaitExpr) {
- add_error_note(g, msg, fn->inferred_async_node,
- buf_sprintf("await here is a suspend point"));
- } else if (fn->inferred_async_node->type == NodeTypeFnCallExpr &&
- fn->inferred_async_node->data.fn_call_expr.modifier == CallModifierBuiltin)
- {
- add_error_note(g, msg, fn->inferred_async_node,
- buf_sprintf("@frame() causes function to be async"));
- } else {
- add_error_note(g, msg, fn->inferred_async_node,
- buf_sprintf("suspends here"));
- }
-}
-
-// ErrorNone - not async
-// ErrorIsAsync - yes async
-// ErrorSemanticAnalyzeFail - compile error emitted result is invalid
-static Error analyze_callee_async(CodeGen *g, ZigFn *fn, ZigFn *callee, AstNode *call_node,
- bool must_not_be_async, CallModifier modifier)
-{
- if (modifier == CallModifierNoSuspend)
- return ErrorNone;
- bool callee_is_async = false;
- switch (callee->type_entry->data.fn.fn_type_id.cc) {
- case CallingConventionUnspecified:
- break;
- case CallingConventionAsync:
- callee_is_async = true;
- break;
- default:
- return ErrorNone;
- }
- if (!callee_is_async) {
- if (callee->anal_state == FnAnalStateReady) {
- analyze_fn_body(g, callee);
- if (callee->anal_state == FnAnalStateInvalid) {
- return ErrorSemanticAnalyzeFail;
- }
- }
- if (callee->anal_state == FnAnalStateComplete) {
- analyze_fn_async(g, callee, true);
- if (callee->anal_state == FnAnalStateInvalid) {
- if (g->trace_err != nullptr) {
- g->trace_err = add_error_note(g, g->trace_err, call_node,
- buf_sprintf("while checking if '%s' is async", buf_ptr(&fn->symbol_name)));
- }
- return ErrorSemanticAnalyzeFail;
- }
- callee_is_async = fn_is_async(callee);
- } else {
- // If it's already been determined, use that value. Otherwise
- // assume non-async, emit an error later if it turned out to be async.
- if (callee->inferred_async_node == nullptr ||
- callee->inferred_async_node == inferred_async_checking)
- {
- callee->assumed_non_async = call_node;
- callee_is_async = false;
- } else {
- callee_is_async = callee->inferred_async_node != inferred_async_none;
- }
- }
- }
- if (callee_is_async) {
- bool bad_recursion = (fn->inferred_async_node == inferred_async_none);
- fn->inferred_async_node = call_node;
- fn->inferred_async_fn = callee;
- if (must_not_be_async) {
- ErrorMsg *msg = add_node_error(g, fn->proto_node,
- buf_sprintf("function with calling convention '%s' cannot be async",
- calling_convention_name(fn->type_entry->data.fn.fn_type_id.cc)));
- add_async_error_notes(g, msg, fn);
- return ErrorSemanticAnalyzeFail;
- }
- if (bad_recursion) {
- ErrorMsg *msg = add_node_error(g, fn->proto_node,
- buf_sprintf("recursive function cannot be async"));
- add_async_error_notes(g, msg, fn);
- return ErrorSemanticAnalyzeFail;
- }
- if (fn->assumed_non_async != nullptr) {
- ErrorMsg *msg = add_node_error(g, fn->proto_node,
- buf_sprintf("unable to infer whether '%s' should be async",
- buf_ptr(&fn->symbol_name)));
- add_error_note(g, msg, fn->assumed_non_async,
- buf_sprintf("assumed to be non-async here"));
- add_async_error_notes(g, msg, fn);
- fn->anal_state = FnAnalStateInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- return ErrorIsAsync;
- }
- return ErrorNone;
-}
-
-// This function resolves functions being inferred async.
-static void analyze_fn_async(CodeGen *g, ZigFn *fn, bool resolve_frame) {
- if (fn->inferred_async_node == inferred_async_checking) {
- // TODO call graph cycle detected, disallow the recursion
- fn->inferred_async_node = inferred_async_none;
- return;
- }
- if (fn->inferred_async_node == inferred_async_none) {
- return;
- }
- if (fn->inferred_async_node != nullptr) {
- if (resolve_frame) {
- resolve_async_fn_frame(g, fn);
- }
- return;
- }
- fn->inferred_async_node = inferred_async_checking;
-
- bool must_not_be_async = false;
- if (fn->type_entry->data.fn.fn_type_id.cc != CallingConventionUnspecified) {
- must_not_be_async = true;
- fn->inferred_async_node = inferred_async_none;
- }
-
- for (size_t i = 0; i < fn->call_list.length; i += 1) {
- IrInstGenCall *call = fn->call_list.at(i);
- if (call->fn_entry == nullptr) {
- // TODO function pointer call here, could be anything
- continue;
- }
- switch (analyze_callee_async(g, fn, call->fn_entry, call->base.base.source_node, must_not_be_async,
- call->modifier))
- {
- case ErrorSemanticAnalyzeFail:
- fn->anal_state = FnAnalStateInvalid;
- return;
- case ErrorNone:
- continue;
- case ErrorIsAsync:
- if (resolve_frame) {
- resolve_async_fn_frame(g, fn);
- }
- return;
- default:
- zig_unreachable();
- }
- }
- for (size_t i = 0; i < fn->await_list.length; i += 1) {
- IrInstGenAwait *await = fn->await_list.at(i);
- if (await->is_nosuspend) continue;
- switch (analyze_callee_async(g, fn, await->target_fn, await->base.base.source_node, must_not_be_async,
- CallModifierNone))
- {
- case ErrorSemanticAnalyzeFail:
- fn->anal_state = FnAnalStateInvalid;
- return;
- case ErrorNone:
- continue;
- case ErrorIsAsync:
- if (resolve_frame) {
- resolve_async_fn_frame(g, fn);
- }
- return;
- default:
- zig_unreachable();
- }
- }
- fn->inferred_async_node = inferred_async_none;
-}
-
-static void analyze_fn_ir(CodeGen *g, ZigFn *fn, AstNode *return_type_node) {
- ZigType *fn_type = fn->type_entry;
- assert(!fn_type->data.fn.is_generic);
- FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
-
- if (fn->analyzed_executable.begin_scope == nullptr) {
- fn->analyzed_executable.begin_scope = &fn->def_scope->base;
- }
- if (fn->analyzed_executable.source_node == nullptr) {
- fn->analyzed_executable.source_node = fn->body_node;
- }
- ZigType *block_return_type = ir_analyze(g, fn->ir_executable,
- &fn->analyzed_executable, fn_type_id->return_type, return_type_node, nullptr);
- fn->src_implicit_return_type = block_return_type;
-
- if (type_is_invalid(block_return_type) || fn->analyzed_executable.first_err_trace_msg != nullptr) {
- assert(g->errors.length > 0);
- fn->anal_state = FnAnalStateInvalid;
- return;
- }
-
- if (fn_type_id->return_type->id == ZigTypeIdErrorUnion) {
- ZigType *return_err_set_type = fn_type_id->return_type->data.error_union.err_set_type;
- if (return_err_set_type->data.error_set.infer_fn != nullptr &&
- return_err_set_type->data.error_set.incomplete)
- {
- // The inferred error set type is null if the function doesn't
- // return any error
- ZigType *inferred_err_set_type = nullptr;
-
- if (fn->src_implicit_return_type->id == ZigTypeIdErrorSet) {
- inferred_err_set_type = fn->src_implicit_return_type;
- } else if (fn->src_implicit_return_type->id == ZigTypeIdErrorUnion) {
- inferred_err_set_type = fn->src_implicit_return_type->data.error_union.err_set_type;
- }
-
- if (inferred_err_set_type != nullptr) {
- if (inferred_err_set_type->data.error_set.infer_fn != nullptr &&
- inferred_err_set_type->data.error_set.incomplete)
- {
- if (!resolve_inferred_error_set(g, inferred_err_set_type, return_type_node)) {
- fn->anal_state = FnAnalStateInvalid;
- return;
- }
- }
-
- return_err_set_type->data.error_set.incomplete = false;
- if (type_is_global_error_set(inferred_err_set_type)) {
- return_err_set_type->data.error_set.err_count = UINT32_MAX;
- } else {
- return_err_set_type->data.error_set.err_count = inferred_err_set_type->data.error_set.err_count;
- if (inferred_err_set_type->data.error_set.err_count > 0) {
- return_err_set_type->data.error_set.errors = heap::c_allocator.allocate(inferred_err_set_type->data.error_set.err_count);
- for (uint32_t i = 0; i < inferred_err_set_type->data.error_set.err_count; i += 1) {
- return_err_set_type->data.error_set.errors[i] = inferred_err_set_type->data.error_set.errors[i];
- }
- }
- }
- } else {
- return_err_set_type->data.error_set.incomplete = false;
- return_err_set_type->data.error_set.err_count = 0;
- }
- }
- }
-
- CallingConvention cc = fn->type_entry->data.fn.fn_type_id.cc;
- if (cc != CallingConventionUnspecified && cc != CallingConventionAsync &&
- fn->inferred_async_node != nullptr &&
- fn->inferred_async_node != inferred_async_checking &&
- fn->inferred_async_node != inferred_async_none)
- {
- ErrorMsg *msg = add_node_error(g, fn->proto_node,
- buf_sprintf("function with calling convention '%s' cannot be async",
- calling_convention_name(cc)));
- add_async_error_notes(g, msg, fn);
- fn->anal_state = FnAnalStateInvalid;
- }
-
- if (g->verbose_ir) {
- fprintf(stderr, "fn %s() { // (analyzed)\n", buf_ptr(&fn->symbol_name));
- ir_print_gen(g, stderr, &fn->analyzed_executable, 4);
- fprintf(stderr, "}\n");
- }
- fn->anal_state = FnAnalStateComplete;
-}
-
-static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry) {
- assert(fn_table_entry->anal_state != FnAnalStateProbing);
- if (fn_table_entry->anal_state != FnAnalStateReady)
- return;
-
- fn_table_entry->anal_state = FnAnalStateProbing;
- update_progress_display(g);
-
- AstNode *return_type_node = (fn_table_entry->proto_node != nullptr) ?
- fn_table_entry->proto_node->data.fn_proto.return_type : fn_table_entry->fndef_scope->base.source_node;
-
- assert(fn_table_entry->fndef_scope);
- if (!fn_table_entry->child_scope)
- fn_table_entry->child_scope = &fn_table_entry->fndef_scope->base;
-
- if (define_local_param_variables(g, fn_table_entry) != ErrorNone) {
- fn_table_entry->anal_state = FnAnalStateInvalid;
- return;
- }
-
- ZigType *fn_type = fn_table_entry->type_entry;
- assert(!fn_type->data.fn.is_generic);
-
- if (!ir_gen_fn(g, fn_table_entry)) {
- fn_table_entry->anal_state = FnAnalStateInvalid;
- return;
- }
-
- if (fn_table_entry->ir_executable->first_err_trace_msg != nullptr) {
- fn_table_entry->anal_state = FnAnalStateInvalid;
- return;
- }
-
- if (g->verbose_ir) {
- 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_src(g, stderr, fn_table_entry->ir_executable, 4);
- fprintf(stderr, "}\n");
- }
-
- analyze_fn_ir(g, fn_table_entry, return_type_node);
-}
-
-ZigType *add_source_file(CodeGen *g, ZigPackage *package, Buf *resolved_path, Buf *source_code,
- SourceKind source_kind)
-{
- if (g->verbose_tokenize) {
- fprintf(stderr, "\nOriginal Source (%s):\n", buf_ptr(resolved_path));
- fprintf(stderr, "----------------\n");
- fprintf(stderr, "%s\n", buf_ptr(source_code));
-
- fprintf(stderr, "\nTokens:\n");
- fprintf(stderr, "---------\n");
- }
-
- Tokenization tokenization = {0};
- tokenize(source_code, &tokenization);
-
- if (tokenization.err) {
- ErrorMsg *err = err_msg_create_with_line(resolved_path, tokenization.err_line, tokenization.err_column,
- source_code, tokenization.line_offsets, tokenization.err);
-
- print_err_msg(err, g->err_color);
- exit(1);
- }
-
- if (g->verbose_tokenize) {
- print_tokens(source_code, tokenization.tokens);
-
- fprintf(stderr, "\nAST:\n");
- fprintf(stderr, "------\n");
- }
-
- Buf *src_dirname = buf_alloc();
- Buf *src_basename = buf_alloc();
- os_path_split(resolved_path, src_dirname, src_basename);
-
- Buf noextname = BUF_INIT;
- os_path_extname(resolved_path, &noextname, nullptr);
-
- Buf *pkg_root_src_dir = &package->root_src_dir;
- Buf resolved_root_src_dir = os_path_resolve(&pkg_root_src_dir, 1);
-
- Buf *namespace_name = buf_create_from_buf(&package->pkg_path);
- if (source_kind == SourceKindNonRoot) {
- assert(buf_starts_with_buf(resolved_path, &resolved_root_src_dir));
- if (buf_len(namespace_name) != 0) {
- buf_append_char(namespace_name, NAMESPACE_SEP_CHAR);
- }
- // The namespace components are obtained from the relative path to the
- // source directory
- if (buf_len(&noextname) > buf_len(&resolved_root_src_dir)) {
- // Skip the trailing separator
- buf_append_mem(namespace_name,
- buf_ptr(&noextname) + buf_len(&resolved_root_src_dir) + 1,
- buf_len(&noextname) - buf_len(&resolved_root_src_dir) - 1);
- }
- buf_replace(namespace_name, ZIG_OS_SEP_CHAR, NAMESPACE_SEP_CHAR);
- }
- Buf *bare_name = buf_alloc();
- os_path_extname(src_basename, bare_name, nullptr);
-
- RootStruct *root_struct = heap::c_allocator.create();
- root_struct->package = package;
- root_struct->source_code = source_code;
- root_struct->line_offsets = tokenization.line_offsets;
- root_struct->path = resolved_path;
- root_struct->di_file = ZigLLVMCreateFile(g->dbuilder, buf_ptr(src_basename), buf_ptr(src_dirname));
- ZigType *import_entry = get_root_container_type(g, buf_ptr(namespace_name), bare_name, root_struct);
- if (source_kind == SourceKindRoot) {
- assert(g->root_import == nullptr);
- g->root_import = import_entry;
- }
- g->import_table.put(resolved_path, import_entry);
-
- AstNode *root_node = ast_parse(source_code, tokenization.tokens, import_entry, g->err_color);
- assert(root_node != nullptr);
- assert(root_node->type == NodeTypeContainerDecl);
- import_entry->data.structure.decl_node = root_node;
- import_entry->data.structure.decls_scope->base.source_node = root_node;
- if (g->verbose_ast) {
- ast_print(stderr, root_node, 0);
- }
-
- for (size_t decl_i = 0; decl_i < root_node->data.container_decl.decls.length; decl_i += 1) {
- AstNode *top_level_decl = root_node->data.container_decl.decls.at(decl_i);
- scan_decls(g, import_entry->data.structure.decls_scope, top_level_decl);
- }
-
- TldContainer *tld_container = heap::c_allocator.create();
- init_tld(&tld_container->base, TldIdContainer, namespace_name, VisibModPub, root_node, nullptr);
- tld_container->type_entry = import_entry;
- tld_container->decls_scope = import_entry->data.structure.decls_scope;
- g->resolve_queue.append(&tld_container->base);
-
- return import_entry;
-}
-
-void semantic_analyze(CodeGen *g) {
- while (g->resolve_queue_index < g->resolve_queue.length ||
- g->fn_defs_index < g->fn_defs.length)
- {
- for (; g->resolve_queue_index < g->resolve_queue.length; g->resolve_queue_index += 1) {
- Tld *tld = g->resolve_queue.at(g->resolve_queue_index);
- g->trace_err = nullptr;
- AstNode *source_node = nullptr;
- resolve_top_level_decl(g, tld, source_node, false);
- }
-
- for (; g->fn_defs_index < g->fn_defs.length; g->fn_defs_index += 1) {
- ZigFn *fn_entry = g->fn_defs.at(g->fn_defs_index);
- g->trace_err = nullptr;
- analyze_fn_body(g, fn_entry);
- }
- }
-
- if (g->errors.length != 0) {
- return;
- }
-
- // second pass over functions for detecting async
- for (g->fn_defs_index = 0; g->fn_defs_index < g->fn_defs.length; g->fn_defs_index += 1) {
- ZigFn *fn = g->fn_defs.at(g->fn_defs_index);
- g->trace_err = nullptr;
- analyze_fn_async(g, fn, true);
- if (fn->anal_state == FnAnalStateInvalid)
- continue;
- if (fn_is_async(fn) && fn->non_async_node != nullptr) {
- ErrorMsg *msg = add_node_error(g, fn->proto_node,
- buf_sprintf("'%s' cannot be async", buf_ptr(&fn->symbol_name)));
- add_error_note(g, msg, fn->non_async_node,
- buf_sprintf("required to be non-async here"));
- add_async_error_notes(g, msg, fn);
- }
- }
-}
-
-ZigType *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits) {
- assert(size_in_bits <= 65535);
- TypeId type_id = {};
- type_id.id = ZigTypeIdInt;
- type_id.data.integer.is_signed = is_signed;
- type_id.data.integer.bit_count = size_in_bits;
-
- {
- auto entry = g->type_table.maybe_get(type_id);
- if (entry)
- return entry->value;
- }
-
- ZigType *new_entry = make_int_type(g, is_signed, size_in_bits);
- g->type_table.put(type_id, new_entry);
- return new_entry;
-}
-
-Error is_valid_vector_elem_type(CodeGen *g, ZigType *elem_type, bool *result) {
- if (elem_type->id == ZigTypeIdInt ||
- elem_type->id == ZigTypeIdFloat ||
- elem_type->id == ZigTypeIdBool)
- {
- *result = true;
- return ErrorNone;
- }
-
- Error err;
- ZigType *ptr_type;
- if ((err = get_codegen_ptr_type(g, elem_type, &ptr_type))) return err;
- if (ptr_type != nullptr) {
- *result = true;
- return ErrorNone;
- }
-
- *result = false;
- return ErrorNone;
-}
-
-ZigType *get_vector_type(CodeGen *g, uint32_t len, ZigType *elem_type) {
- Error err;
-
- bool valid_vector_elem;
- if ((err = is_valid_vector_elem_type(g, elem_type, &valid_vector_elem))) {
- codegen_report_errors_and_exit(g);
- }
- assert(valid_vector_elem);
-
- TypeId type_id = {};
- type_id.id = ZigTypeIdVector;
- type_id.data.vector.len = len;
- type_id.data.vector.elem_type = elem_type;
-
- {
- auto entry = g->type_table.maybe_get(type_id);
- if (entry)
- return entry->value;
- }
-
- ZigType *entry = new_type_table_entry(ZigTypeIdVector);
- if ((len != 0) && type_has_bits(g, elem_type)) {
- // Vectors can only be ints, floats, bools, or pointers. ints (inc. bools) and floats have trivially resolvable
- // llvm type refs. pointers we will use usize instead.
- LLVMTypeRef example_vector_llvm_type;
- if (elem_type->id == ZigTypeIdPointer) {
- example_vector_llvm_type = LLVMVectorType(g->builtin_types.entry_usize->llvm_type, len);
- } else {
- example_vector_llvm_type = LLVMVectorType(elem_type->llvm_type, len);
- }
- assert(example_vector_llvm_type != nullptr);
- entry->size_in_bits = elem_type->size_in_bits * len;
- entry->abi_size = LLVMABISizeOfType(g->target_data_ref, example_vector_llvm_type);
- entry->abi_align = LLVMABIAlignmentOfType(g->target_data_ref, example_vector_llvm_type);
- }
- entry->data.vector.len = len;
- entry->data.vector.elem_type = elem_type;
- entry->data.vector.padding = 0;
-
- buf_resize(&entry->name, 0);
- buf_appendf(&entry->name, "@Vector(%u, %s)", len, buf_ptr(&elem_type->name));
-
- g->type_table.put(type_id, entry);
- return entry;
-}
-
-ZigType **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type) {
- return &g->builtin_types.entry_c_int[c_int_type];
-}
-
-ZigType *get_c_int_type(CodeGen *g, CIntType c_int_type) {
- return *get_c_int_type_ptr(g, c_int_type);
-}
-
-bool handle_is_ptr(CodeGen *g, ZigType *type_entry) {
- switch (type_entry->id) {
- case ZigTypeIdInvalid:
- case ZigTypeIdMetaType:
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdEnumLiteral:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdBoundFn:
- case ZigTypeIdOpaque:
- zig_unreachable();
- case ZigTypeIdUnreachable:
- case ZigTypeIdVoid:
- case ZigTypeIdBool:
- case ZigTypeIdInt:
- case ZigTypeIdFloat:
- case ZigTypeIdPointer:
- case ZigTypeIdErrorSet:
- case ZigTypeIdFn:
- case ZigTypeIdEnum:
- case ZigTypeIdVector:
- case ZigTypeIdAnyFrame:
- return false;
- case ZigTypeIdArray:
- case ZigTypeIdStruct:
- case ZigTypeIdFnFrame:
- return type_has_bits(g, type_entry);
- case ZigTypeIdErrorUnion:
- return type_has_bits(g, type_entry->data.error_union.payload_type);
- case ZigTypeIdOptional:
- return type_has_bits(g, type_entry->data.maybe.child_type) &&
- !type_is_nonnull_ptr(g, type_entry->data.maybe.child_type) &&
- type_entry->data.maybe.child_type->id != ZigTypeIdErrorSet;
- case ZigTypeIdUnion:
- return type_has_bits(g, type_entry) && type_entry->data.unionation.gen_field_count != 0;
-
- }
- zig_unreachable();
-}
-
-static uint32_t hash_ptr(void *ptr) {
- return (uint32_t)(((uintptr_t)ptr) % UINT32_MAX);
-}
-
-static uint32_t hash_size(size_t x) {
- return (uint32_t)(x % UINT32_MAX);
-}
-
-uint32_t fn_table_entry_hash(ZigFn* value) {
- return ptr_hash(value);
-}
-
-bool fn_table_entry_eql(ZigFn *a, ZigFn *b) {
- return ptr_eq(a, b);
-}
-
-uint32_t fn_type_id_hash(FnTypeId *id) {
- uint32_t result = 0;
- result += ((uint32_t)(id->cc)) * (uint32_t)3349388391;
- result += id->is_var_args ? (uint32_t)1931444534 : 0;
- result += hash_ptr(id->return_type);
- result += id->alignment * 0xd3b3f3e2;
- for (size_t i = 0; i < id->param_count; i += 1) {
- FnTypeParamInfo *info = &id->param_info[i];
- result += info->is_noalias ? (uint32_t)892356923 : 0;
- result += hash_ptr(info->type);
- }
- return result;
-}
-
-bool fn_type_id_eql(FnTypeId *a, FnTypeId *b) {
- if (a->cc != b->cc ||
- a->return_type != b->return_type ||
- a->is_var_args != b->is_var_args ||
- a->param_count != b->param_count ||
- a->alignment != b->alignment)
- {
- return false;
- }
- for (size_t i = 0; i < a->param_count; i += 1) {
- FnTypeParamInfo *a_param_info = &a->param_info[i];
- FnTypeParamInfo *b_param_info = &b->param_info[i];
-
- if (a_param_info->type != b_param_info->type ||
- a_param_info->is_noalias != b_param_info->is_noalias)
- {
- return false;
- }
- }
- return true;
-}
-
-static uint32_t hash_const_val_error_set(ZigValue *const_val) {
- assert(const_val->data.x_err_set != nullptr);
- return const_val->data.x_err_set->value ^ 2630160122;
-}
-
-static uint32_t hash_const_val_ptr(ZigValue *const_val) {
- uint32_t hash_val = 0;
- switch (const_val->data.x_ptr.mut) {
- case ConstPtrMutRuntimeVar:
- hash_val += (uint32_t)3500721036;
- break;
- case ConstPtrMutComptimeConst:
- hash_val += (uint32_t)4214318515;
- break;
- case ConstPtrMutInfer:
- case ConstPtrMutComptimeVar:
- hash_val += (uint32_t)1103195694;
- break;
- }
- switch (const_val->data.x_ptr.special) {
- case ConstPtrSpecialInvalid:
- zig_unreachable();
- case ConstPtrSpecialRef:
- hash_val += (uint32_t)2478261866;
- hash_val += hash_ptr(const_val->data.x_ptr.data.ref.pointee);
- return hash_val;
- case ConstPtrSpecialBaseArray:
- hash_val += (uint32_t)1764906839;
- hash_val += hash_ptr(const_val->data.x_ptr.data.base_array.array_val);
- hash_val += hash_size(const_val->data.x_ptr.data.base_array.elem_index);
- return hash_val;
- case ConstPtrSpecialSubArray:
- hash_val += (uint32_t)2643358777;
- hash_val += hash_ptr(const_val->data.x_ptr.data.base_array.array_val);
- hash_val += hash_size(const_val->data.x_ptr.data.base_array.elem_index);
- return hash_val;
- case ConstPtrSpecialBaseStruct:
- hash_val += (uint32_t)3518317043;
- hash_val += hash_ptr(const_val->data.x_ptr.data.base_struct.struct_val);
- hash_val += hash_size(const_val->data.x_ptr.data.base_struct.field_index);
- return hash_val;
- case ConstPtrSpecialBaseErrorUnionCode:
- hash_val += (uint32_t)2994743799;
- hash_val += hash_ptr(const_val->data.x_ptr.data.base_err_union_code.err_union_val);
- return hash_val;
- case ConstPtrSpecialBaseErrorUnionPayload:
- hash_val += (uint32_t)3456080131;
- hash_val += hash_ptr(const_val->data.x_ptr.data.base_err_union_payload.err_union_val);
- return hash_val;
- case ConstPtrSpecialBaseOptionalPayload:
- hash_val += (uint32_t)3163140517;
- hash_val += hash_ptr(const_val->data.x_ptr.data.base_optional_payload.optional_val);
- return hash_val;
- case ConstPtrSpecialHardCodedAddr:
- hash_val += (uint32_t)4048518294;
- hash_val += hash_size(const_val->data.x_ptr.data.hard_coded_addr.addr);
- return hash_val;
- case ConstPtrSpecialDiscard:
- hash_val += 2010123162;
- return hash_val;
- case ConstPtrSpecialFunction:
- hash_val += (uint32_t)2590901619;
- hash_val += hash_ptr(const_val->data.x_ptr.data.fn.fn_entry);
- return hash_val;
- case ConstPtrSpecialNull:
- hash_val += (uint32_t)1486246455;
- return hash_val;
- }
- zig_unreachable();
-}
-
-static uint32_t hash_const_val(ZigValue *const_val) {
- assert(const_val->special == ConstValSpecialStatic);
- switch (const_val->type->id) {
- case ZigTypeIdOpaque:
- zig_unreachable();
- case ZigTypeIdBool:
- return const_val->data.x_bool ? (uint32_t)127863866 : (uint32_t)215080464;
- case ZigTypeIdMetaType:
- return hash_ptr(const_val->data.x_type);
- case ZigTypeIdVoid:
- return (uint32_t)4149439618;
- case ZigTypeIdInt:
- case ZigTypeIdComptimeInt:
- {
- uint32_t result = 1331471175;
- for (size_t i = 0; i < const_val->data.x_bigint.digit_count; i += 1) {
- uint64_t digit = bigint_ptr(&const_val->data.x_bigint)[i];
- result ^= ((uint32_t)(digit >> 32)) ^ (uint32_t)(result);
- }
- return result;
- }
- case ZigTypeIdEnumLiteral:
- return buf_hash(const_val->data.x_enum_literal) * (uint32_t)2691276464;
- case ZigTypeIdEnum:
- {
- uint32_t result = 31643936;
- for (size_t i = 0; i < const_val->data.x_enum_tag.digit_count; i += 1) {
- uint64_t digit = bigint_ptr(&const_val->data.x_enum_tag)[i];
- result ^= ((uint32_t)(digit >> 32)) ^ (uint32_t)(result);
- }
- return result;
- }
- case ZigTypeIdFloat:
- switch (const_val->type->data.floating.bit_count) {
- case 16:
- {
- uint16_t result;
- static_assert(sizeof(result) == sizeof(const_val->data.x_f16), "");
- memcpy(&result, &const_val->data.x_f16, sizeof(result));
- return result * 65537u;
- }
- case 32:
- {
- uint32_t result;
- memcpy(&result, &const_val->data.x_f32, 4);
- return result ^ 4084870010;
- }
- case 64:
- {
- uint32_t ints[2];
- memcpy(&ints[0], &const_val->data.x_f64, 8);
- return ints[0] ^ ints[1] ^ 0x22ed43c6;
- }
- case 128:
- {
- uint32_t ints[4];
- memcpy(&ints[0], &const_val->data.x_f128, 16);
- return ints[0] ^ ints[1] ^ ints[2] ^ ints[3] ^ 0xb5ffef27;
- }
- default:
- zig_unreachable();
- }
- case ZigTypeIdComptimeFloat:
- {
- float128_t f128 = bigfloat_to_f128(&const_val->data.x_bigfloat);
- uint32_t ints[4];
- memcpy(&ints[0], &f128, 16);
- return ints[0] ^ ints[1] ^ ints[2] ^ ints[3] ^ 0xed8b3dfb;
- }
- case ZigTypeIdFn:
- assert(const_val->data.x_ptr.mut == ConstPtrMutComptimeConst);
- assert(const_val->data.x_ptr.special == ConstPtrSpecialFunction);
- return 3677364617 ^ hash_ptr(const_val->data.x_ptr.data.fn.fn_entry);
- case ZigTypeIdPointer:
- return hash_const_val_ptr(const_val);
- case ZigTypeIdUndefined:
- return 162837799;
- case ZigTypeIdNull:
- return 844854567;
- case ZigTypeIdArray:
- // TODO better hashing algorithm
- return 1166190605;
- case ZigTypeIdStruct:
- // TODO better hashing algorithm
- return 1532530855;
- case ZigTypeIdUnion:
- // TODO better hashing algorithm
- return 2709806591;
- case ZigTypeIdOptional:
- if (get_src_ptr_type(const_val->type) != nullptr) {
- return hash_const_val_ptr(const_val) * (uint32_t)1992916303;
- } else if (const_val->type->data.maybe.child_type->id == ZigTypeIdErrorSet) {
- return hash_const_val_error_set(const_val) * (uint32_t)3147031929;
- } else {
- if (const_val->data.x_optional) {
- return hash_const_val(const_val->data.x_optional) * (uint32_t)1992916303;
- } else {
- return 4016830364;
- }
- }
- case ZigTypeIdErrorUnion:
- // TODO better hashing algorithm
- return 3415065496;
- case ZigTypeIdErrorSet:
- return hash_const_val_error_set(const_val);
- case ZigTypeIdVector:
- // TODO better hashing algorithm
- return 3647867726;
- case ZigTypeIdFnFrame:
- // TODO better hashing algorithm
- return 675741936;
- case ZigTypeIdAnyFrame:
- // TODO better hashing algorithm
- return 3747294894;
- case ZigTypeIdBoundFn: {
- assert(const_val->data.x_bound_fn.fn != nullptr);
- return 3677364617 ^ hash_ptr(const_val->data.x_bound_fn.fn);
- }
- case ZigTypeIdInvalid:
- case ZigTypeIdUnreachable:
- zig_unreachable();
- }
- zig_unreachable();
-}
-
-uint32_t generic_fn_type_id_hash(GenericFnTypeId *id) {
- uint32_t result = 0;
- result += hash_ptr(id->fn_entry);
- for (size_t i = 0; i < id->param_count; i += 1) {
- ZigValue *generic_param = &id->params[i];
- if (generic_param->special != ConstValSpecialRuntime) {
- result += hash_const_val(generic_param);
- result += hash_ptr(generic_param->type);
- }
- }
- return result;
-}
-
-bool generic_fn_type_id_eql(GenericFnTypeId *a, GenericFnTypeId *b) {
- assert(a->fn_entry);
- if (a->fn_entry != b->fn_entry) return false;
- if (a->param_count != b->param_count) return false;
- for (size_t i = 0; i < a->param_count; i += 1) {
- ZigValue *a_val = &a->params[i];
- ZigValue *b_val = &b->params[i];
- if (a_val->type != b_val->type) return false;
- if (a_val->special != ConstValSpecialRuntime && b_val->special != ConstValSpecialRuntime) {
- assert(a_val->special == ConstValSpecialStatic);
- assert(b_val->special == ConstValSpecialStatic);
- if (!const_values_equal(a->codegen, a_val, b_val)) {
- return false;
- }
- } else {
- assert(a_val->special == ConstValSpecialRuntime && b_val->special == ConstValSpecialRuntime);
- }
- }
- return true;
-}
-
-static bool can_mutate_comptime_var_state(ZigValue *value) {
- assert(value != nullptr);
- if (value->special == ConstValSpecialUndef)
- return false;
- switch (value->type->id) {
- case ZigTypeIdInvalid:
- zig_unreachable();
- case ZigTypeIdMetaType:
- case ZigTypeIdVoid:
- case ZigTypeIdBool:
- case ZigTypeIdUnreachable:
- case ZigTypeIdInt:
- case ZigTypeIdVector:
- case ZigTypeIdFloat:
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdEnumLiteral:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdBoundFn:
- case ZigTypeIdFn:
- case ZigTypeIdOpaque:
- case ZigTypeIdErrorSet:
- case ZigTypeIdEnum:
- case ZigTypeIdFnFrame:
- case ZigTypeIdAnyFrame:
- return false;
-
- case ZigTypeIdPointer:
- return value->data.x_ptr.mut == ConstPtrMutComptimeVar;
-
- case ZigTypeIdArray:
- if (value->special == ConstValSpecialUndef)
- return false;
- if (value->type->data.array.len == 0)
- return false;
- switch (value->data.x_array.special) {
- case ConstArraySpecialUndef:
- case ConstArraySpecialBuf:
- return false;
- case ConstArraySpecialNone:
- for (uint32_t i = 0; i < value->type->data.array.len; i += 1) {
- if (can_mutate_comptime_var_state(&value->data.x_array.data.s_none.elements[i]))
- return true;
- }
- return false;
- }
- zig_unreachable();
- case ZigTypeIdStruct:
- for (uint32_t i = 0; i < value->type->data.structure.src_field_count; i += 1) {
- if (can_mutate_comptime_var_state(value->data.x_struct.fields[i]))
- return true;
- }
- return false;
-
- case ZigTypeIdOptional:
- if (get_src_ptr_type(value->type) != nullptr)
- return value->data.x_ptr.mut == ConstPtrMutComptimeVar;
- if (value->data.x_optional == nullptr)
- return false;
- return can_mutate_comptime_var_state(value->data.x_optional);
-
- case ZigTypeIdErrorUnion:
- if (value->data.x_err_union.error_set->data.x_err_set != nullptr)
- return false;
- assert(value->data.x_err_union.payload != nullptr);
- return can_mutate_comptime_var_state(value->data.x_err_union.payload);
-
- case ZigTypeIdUnion:
- return can_mutate_comptime_var_state(value->data.x_union.payload);
- }
- zig_unreachable();
-}
-
-static bool return_type_is_cacheable(ZigType *return_type) {
- switch (return_type->id) {
- case ZigTypeIdInvalid:
- zig_unreachable();
- case ZigTypeIdMetaType:
- case ZigTypeIdVoid:
- case ZigTypeIdBool:
- case ZigTypeIdUnreachable:
- case ZigTypeIdInt:
- case ZigTypeIdFloat:
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdEnumLiteral:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdBoundFn:
- case ZigTypeIdFn:
- case ZigTypeIdOpaque:
- case ZigTypeIdErrorSet:
- case ZigTypeIdEnum:
- case ZigTypeIdPointer:
- case ZigTypeIdVector:
- case ZigTypeIdFnFrame:
- case ZigTypeIdAnyFrame:
- return true;
-
- case ZigTypeIdArray:
- case ZigTypeIdStruct:
- case ZigTypeIdUnion:
- return false;
-
- case ZigTypeIdOptional:
- return return_type_is_cacheable(return_type->data.maybe.child_type);
-
- case ZigTypeIdErrorUnion:
- return return_type_is_cacheable(return_type->data.error_union.payload_type);
- }
- zig_unreachable();
-}
-
-bool fn_eval_cacheable(Scope *scope, ZigType *return_type) {
- if (!return_type_is_cacheable(return_type))
- return false;
- while (scope) {
- if (scope->id == ScopeIdVarDecl) {
- ScopeVarDecl *var_scope = (ScopeVarDecl *)scope;
- if (type_is_invalid(var_scope->var->var_type))
- return false;
- if (var_scope->var->const_value->special == ConstValSpecialUndef)
- return false;
- if (can_mutate_comptime_var_state(var_scope->var->const_value))
- return false;
- } else if (scope->id == ScopeIdFnDef) {
- return true;
- } else {
- zig_unreachable();
- }
-
- scope = scope->parent;
- }
- zig_unreachable();
-}
-
-uint32_t fn_eval_hash(Scope* scope) {
- uint32_t result = 0;
- while (scope) {
- if (scope->id == ScopeIdVarDecl) {
- ScopeVarDecl *var_scope = (ScopeVarDecl *)scope;
- result += hash_const_val(var_scope->var->const_value);
- } else if (scope->id == ScopeIdFnDef) {
- ScopeFnDef *fn_scope = (ScopeFnDef *)scope;
- result += hash_ptr(fn_scope->fn_entry);
- return result;
- } else {
- zig_unreachable();
- }
-
- scope = scope->parent;
- }
- zig_unreachable();
-}
-
-bool fn_eval_eql(Scope *a, Scope *b) {
- assert(a->codegen != nullptr);
- assert(b->codegen != nullptr);
- while (a && b) {
- if (a->id != b->id)
- return false;
-
- if (a->id == ScopeIdVarDecl) {
- ScopeVarDecl *a_var_scope = (ScopeVarDecl *)a;
- ScopeVarDecl *b_var_scope = (ScopeVarDecl *)b;
- if (a_var_scope->var->var_type != b_var_scope->var->var_type)
- return false;
- if (a_var_scope->var->var_type == a_var_scope->var->const_value->type &&
- b_var_scope->var->var_type == b_var_scope->var->const_value->type)
- {
- if (!const_values_equal(a->codegen, a_var_scope->var->const_value, b_var_scope->var->const_value))
- return false;
- } else {
- zig_panic("TODO comptime ptr reinterpret for fn_eval_eql");
- }
- } else if (a->id == ScopeIdFnDef) {
- ScopeFnDef *a_fn_scope = (ScopeFnDef *)a;
- ScopeFnDef *b_fn_scope = (ScopeFnDef *)b;
- if (a_fn_scope->fn_entry != b_fn_scope->fn_entry)
- return false;
-
- return true;
- } else {
- zig_unreachable();
- }
-
- a = a->parent;
- b = b->parent;
- }
- return false;
-}
-
-// Deprecated. Use type_has_bits2.
-bool type_has_bits(CodeGen *g, ZigType *type_entry) {
- Error err;
- bool result;
- if ((err = type_has_bits2(g, type_entry, &result))) {
- codegen_report_errors_and_exit(g);
- }
- return result;
-}
-
-// Whether the type has bits at runtime.
-Error type_has_bits2(CodeGen *g, ZigType *type_entry, bool *result) {
- Error err;
-
- if (type_is_invalid(type_entry))
- return ErrorSemanticAnalyzeFail;
-
- if (type_entry->id == ZigTypeIdStruct &&
- type_entry->data.structure.resolve_status == ResolveStatusBeingInferred)
- {
- *result = true;
- return ErrorNone;
- }
-
- if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown)))
- return err;
-
- *result = type_entry->abi_size != 0;
- return ErrorNone;
-}
-
-// Whether you can infer the value based solely on the type.
-OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry) {
- assert(type_entry != nullptr);
-
- if (type_entry->one_possible_value != OnePossibleValueInvalid)
- return type_entry->one_possible_value;
-
- if (type_entry->id == ZigTypeIdStruct &&
- type_entry->data.structure.resolve_status == ResolveStatusBeingInferred)
- {
- return OnePossibleValueNo;
- }
-
- Error err;
- if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown)))
- return OnePossibleValueInvalid;
- switch (type_entry->id) {
- case ZigTypeIdInvalid:
- zig_unreachable();
- case ZigTypeIdOpaque:
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdEnumLiteral:
- case ZigTypeIdMetaType:
- case ZigTypeIdBoundFn:
- case ZigTypeIdOptional:
- case ZigTypeIdFn:
- case ZigTypeIdBool:
- case ZigTypeIdFloat:
- case ZigTypeIdErrorUnion:
- case ZigTypeIdFnFrame:
- case ZigTypeIdAnyFrame:
- return OnePossibleValueNo;
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdVoid:
- case ZigTypeIdUnreachable:
- return OnePossibleValueYes;
- case ZigTypeIdArray:
- if (type_entry->data.array.len == 0)
- return OnePossibleValueYes;
- return type_has_one_possible_value(g, type_entry->data.array.child_type);
- case ZigTypeIdStruct:
- // If the recursive function call asks, then we are not one possible value.
- type_entry->one_possible_value = OnePossibleValueNo;
- for (size_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) {
- TypeStructField *field = type_entry->data.structure.fields[i];
- if (field->is_comptime) {
- // If this field is comptime then the field can only be one possible value
- continue;
- }
- OnePossibleValue opv = (field->type_entry != nullptr) ?
- type_has_one_possible_value(g, field->type_entry) :
- type_val_resolve_has_one_possible_value(g, field->type_val);
- switch (opv) {
- case OnePossibleValueInvalid:
- type_entry->one_possible_value = OnePossibleValueInvalid;
- return OnePossibleValueInvalid;
- case OnePossibleValueNo:
- return OnePossibleValueNo;
- case OnePossibleValueYes:
- continue;
- }
- }
- type_entry->one_possible_value = OnePossibleValueYes;
- return OnePossibleValueYes;
- case ZigTypeIdErrorSet:
- case ZigTypeIdEnum:
- case ZigTypeIdInt:
- case ZigTypeIdVector:
- return type_has_bits(g, type_entry) ? OnePossibleValueNo : OnePossibleValueYes;
- case ZigTypeIdPointer: {
- ZigType *elem_type = type_entry->data.pointer.child_type;
- // If the recursive function call asks, then we are not one possible value.
- type_entry->one_possible_value = OnePossibleValueNo;
- // Now update it to be the value of the recursive call.
- type_entry->one_possible_value = type_has_one_possible_value(g, elem_type);
- return type_entry->one_possible_value;
- }
- case ZigTypeIdUnion:
- if (type_entry->data.unionation.src_field_count > 1)
- return OnePossibleValueNo;
- TypeUnionField *only_field = &type_entry->data.unionation.fields[0];
- if (only_field->type_entry != nullptr) {
- return type_has_one_possible_value(g, only_field->type_entry);
- }
- return type_val_resolve_has_one_possible_value(g, only_field->type_val);
- }
- zig_unreachable();
-}
-
-ZigValue *get_the_one_possible_value(CodeGen *g, ZigType *type_entry) {
- auto entry = g->one_possible_values.maybe_get(type_entry);
- if (entry != nullptr) {
- return entry->value;
- }
- ZigValue *result = g->pass1_arena->create();
- result->type = type_entry;
- result->special = ConstValSpecialStatic;
-
- if (result->type->id == ZigTypeIdStruct) {
- // The fields array cannot be left unpopulated
- const ZigType *struct_type = result->type;
- const size_t field_count = struct_type->data.structure.src_field_count;
- result->data.x_struct.fields = alloc_const_vals_ptrs(g, field_count);
- for (size_t i = 0; i < field_count; i += 1) {
- TypeStructField *field = struct_type->data.structure.fields[i];
- if (field->is_comptime) {
- copy_const_val(g, result->data.x_struct.fields[i], field->init_val);
- continue;
- }
- ZigType *field_type = resolve_struct_field_type(g, field);
- assert(field_type != nullptr);
- result->data.x_struct.fields[i] = get_the_one_possible_value(g, field_type);
- }
- } else if (result->type->id == ZigTypeIdArray) {
- // The elements array cannot be left unpopulated
- ZigType *array_type = result->type;
- ZigType *elem_type = array_type->data.array.child_type;
- const size_t elem_count = array_type->data.array.len;
-
- result->data.x_array.data.s_none.elements = g->pass1_arena->allocate(elem_count);
- for (size_t i = 0; i < elem_count; i += 1) {
- ZigValue *elem_val = &result->data.x_array.data.s_none.elements[i];
- copy_const_val(g, elem_val, get_the_one_possible_value(g, elem_type));
- }
- } else if (result->type->id == ZigTypeIdPointer) {
- result->data.x_ptr.special = ConstPtrSpecialRef;
- result->data.x_ptr.data.ref.pointee = get_the_one_possible_value(g, result->type->data.pointer.child_type);
- }
- g->one_possible_values.put(type_entry, result);
- return result;
-}
-
-ReqCompTime type_requires_comptime(CodeGen *g, ZigType *ty) {
- Error err;
- if (ty == g->builtin_types.entry_anytype) {
- return ReqCompTimeYes;
- }
- switch (ty->id) {
- case ZigTypeIdInvalid:
- zig_unreachable();
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdEnumLiteral:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdMetaType:
- case ZigTypeIdBoundFn:
- return ReqCompTimeYes;
- case ZigTypeIdArray:
- return type_requires_comptime(g, ty->data.array.child_type);
- case ZigTypeIdStruct:
- if (ty->data.structure.resolve_loop_flag_zero_bits) {
- // Does a struct which contains a pointer field to itself require comptime? No.
- return ReqCompTimeNo;
- }
- if ((err = type_resolve(g, ty, ResolveStatusZeroBitsKnown)))
- return ReqCompTimeInvalid;
- return ty->data.structure.requires_comptime ? ReqCompTimeYes : ReqCompTimeNo;
- case ZigTypeIdUnion:
- if (ty->data.unionation.resolve_loop_flag_zero_bits) {
- // Does a union which contains a pointer field to itself require comptime? No.
- return ReqCompTimeNo;
- }
- if ((err = type_resolve(g, ty, ResolveStatusZeroBitsKnown)))
- return ReqCompTimeInvalid;
- return ty->data.unionation.requires_comptime ? ReqCompTimeYes : ReqCompTimeNo;
- case ZigTypeIdOptional:
- return type_requires_comptime(g, ty->data.maybe.child_type);
- case ZigTypeIdErrorUnion:
- return type_requires_comptime(g, ty->data.error_union.payload_type);
- case ZigTypeIdPointer:
- if (ty->data.pointer.child_type->id == ZigTypeIdOpaque) {
- return ReqCompTimeNo;
- } else {
- return type_requires_comptime(g, ty->data.pointer.child_type);
- }
- case ZigTypeIdFn:
- return ty->data.fn.is_generic ? ReqCompTimeYes : ReqCompTimeNo;
- case ZigTypeIdOpaque:
- case ZigTypeIdEnum:
- case ZigTypeIdErrorSet:
- case ZigTypeIdBool:
- case ZigTypeIdInt:
- case ZigTypeIdVector:
- case ZigTypeIdFloat:
- case ZigTypeIdVoid:
- case ZigTypeIdUnreachable:
- case ZigTypeIdFnFrame:
- case ZigTypeIdAnyFrame:
- return ReqCompTimeNo;
- }
- zig_unreachable();
-}
-
-void init_const_str_lit(CodeGen *g, ZigValue *const_val, Buf *str) {
- auto entry = g->string_literals_table.maybe_get(str);
- if (entry != nullptr) {
- memcpy(const_val, entry->value, sizeof(ZigValue));
- return;
- }
-
- // first we build the underlying array
- ZigValue *array_val = g->pass1_arena->create();
- array_val->special = ConstValSpecialStatic;
- array_val->type = get_array_type(g, g->builtin_types.entry_u8, buf_len(str), g->intern.for_zero_byte());
- array_val->data.x_array.special = ConstArraySpecialBuf;
- array_val->data.x_array.data.s_buf = str;
-
- // then make the pointer point to it
- const_val->special = ConstValSpecialStatic;
- const_val->type = get_pointer_to_type_extra2(g, array_val->type, true, false,
- PtrLenSingle, 0, 0, 0, false, VECTOR_INDEX_NONE, nullptr, nullptr);
- const_val->data.x_ptr.special = ConstPtrSpecialRef;
- const_val->data.x_ptr.data.ref.pointee = array_val;
-
- g->string_literals_table.put(str, const_val);
-}
-
-ZigValue *create_const_str_lit(CodeGen *g, Buf *str) {
- ZigValue *const_val = g->pass1_arena->create();
- init_const_str_lit(g, const_val, str);
- return const_val;
-}
-
-void init_const_bigint(ZigValue *const_val, ZigType *type, const BigInt *bigint) {
- const_val->special = ConstValSpecialStatic;
- const_val->type = type;
- bigint_init_bigint(&const_val->data.x_bigint, bigint);
-}
-
-ZigValue *create_const_bigint(CodeGen *g, ZigType *type, const BigInt *bigint) {
- ZigValue *const_val = g->pass1_arena->create();
- init_const_bigint(const_val, type, bigint);
- return const_val;
-}
-
-
-void init_const_unsigned_negative(ZigValue *const_val, ZigType *type, uint64_t x, bool negative) {
- const_val->special = ConstValSpecialStatic;
- const_val->type = type;
- bigint_init_unsigned(&const_val->data.x_bigint, x);
- const_val->data.x_bigint.is_negative = negative;
-}
-
-ZigValue *create_const_unsigned_negative(CodeGen *g, ZigType *type, uint64_t x, bool negative) {
- ZigValue *const_val = g->pass1_arena->create();
- init_const_unsigned_negative(const_val, type, x, negative);
- return const_val;
-}
-
-void init_const_usize(CodeGen *g, ZigValue *const_val, uint64_t x) {
- return init_const_unsigned_negative(const_val, g->builtin_types.entry_usize, x, false);
-}
-
-ZigValue *create_const_usize(CodeGen *g, uint64_t x) {
- return create_const_unsigned_negative(g, g->builtin_types.entry_usize, x, false);
-}
-
-void init_const_signed(ZigValue *const_val, ZigType *type, int64_t x) {
- const_val->special = ConstValSpecialStatic;
- const_val->type = type;
- bigint_init_signed(&const_val->data.x_bigint, x);
-}
-
-ZigValue *create_const_signed(CodeGen *g, ZigType *type, int64_t x) {
- ZigValue *const_val = g->pass1_arena->create();
- init_const_signed(const_val, type, x);
- return const_val;
-}
-
-void init_const_null(ZigValue *const_val, ZigType *type) {
- const_val->special = ConstValSpecialStatic;
- const_val->type = type;
- const_val->data.x_optional = nullptr;
-}
-
-ZigValue *create_const_null(CodeGen *g, ZigType *type) {
- ZigValue *const_val = g->pass1_arena->create();
- init_const_null(const_val, type);
- return const_val;
-}
-
-void init_const_fn(ZigValue *const_val, ZigFn *fn) {
- const_val->special = ConstValSpecialStatic;
- const_val->type = fn->type_entry;
- const_val->data.x_ptr.special = ConstPtrSpecialFunction;
- const_val->data.x_ptr.data.fn.fn_entry = fn;
-}
-
-ZigValue *create_const_fn(CodeGen *g, ZigFn *fn) {
- ZigValue *const_val = g->pass1_arena->create();
- init_const_fn(const_val, fn);
- return const_val;
-}
-
-void init_const_float(ZigValue *const_val, ZigType *type, double value) {
- const_val->special = ConstValSpecialStatic;
- const_val->type = type;
- if (type->id == ZigTypeIdComptimeFloat) {
- bigfloat_init_64(&const_val->data.x_bigfloat, value);
- } else if (type->id == ZigTypeIdFloat) {
- switch (type->data.floating.bit_count) {
- case 16:
- const_val->data.x_f16 = zig_double_to_f16(value);
- break;
- case 32:
- const_val->data.x_f32 = value;
- break;
- case 64:
- const_val->data.x_f64 = value;
- break;
- case 128:
- // if we need this, we should add a function that accepts a float128_t param
- zig_unreachable();
- default:
- zig_unreachable();
- }
- } else {
- zig_unreachable();
- }
-}
-
-ZigValue *create_const_float(CodeGen *g, ZigType *type, double value) {
- ZigValue *const_val = g->pass1_arena->create();
- init_const_float(const_val, type, value);
- return const_val;
-}
-
-void init_const_enum(ZigValue *const_val, ZigType *type, const BigInt *tag) {
- const_val->special = ConstValSpecialStatic;
- const_val->type = type;
- bigint_init_bigint(&const_val->data.x_enum_tag, tag);
-}
-
-ZigValue *create_const_enum(CodeGen *g, ZigType *type, const BigInt *tag) {
- ZigValue *const_val = g->pass1_arena->create();
- init_const_enum(const_val, type, tag);
- return const_val;
-}
-
-
-void init_const_bool(CodeGen *g, ZigValue *const_val, bool value) {
- const_val->special = ConstValSpecialStatic;
- const_val->type = g->builtin_types.entry_bool;
- const_val->data.x_bool = value;
-}
-
-ZigValue *create_const_bool(CodeGen *g, bool value) {
- ZigValue *const_val = g->pass1_arena->create();
- init_const_bool(g, const_val, value);
- return const_val;
-}
-
-void init_const_runtime(ZigValue *const_val, ZigType *type) {
- const_val->special = ConstValSpecialRuntime;
- const_val->type = type;
-}
-
-ZigValue *create_const_runtime(CodeGen *g, ZigType *type) {
- ZigValue *const_val = g->pass1_arena->create();
- init_const_runtime(const_val, type);
- return const_val;
-}
-
-void init_const_type(CodeGen *g, ZigValue *const_val, ZigType *type_value) {
- const_val->special = ConstValSpecialStatic;
- const_val->type = g->builtin_types.entry_type;
- const_val->data.x_type = type_value;
-}
-
-ZigValue *create_const_type(CodeGen *g, ZigType *type_value) {
- ZigValue *const_val = g->pass1_arena->create();
- init_const_type(g, const_val, type_value);
- return const_val;
-}
-
-void init_const_slice(CodeGen *g, ZigValue *const_val, ZigValue *array_val,
- size_t start, size_t len, bool is_const)
-{
- assert(array_val->type->id == ZigTypeIdArray);
-
- ZigType *ptr_type = get_pointer_to_type_extra(g, array_val->type->data.array.child_type,
- is_const, false, PtrLenUnknown, 0, 0, 0, false);
-
- const_val->special = ConstValSpecialStatic;
- const_val->type = get_slice_type(g, ptr_type);
- const_val->data.x_struct.fields = alloc_const_vals_ptrs(g, 2);
-
- init_const_ptr_array(g, const_val->data.x_struct.fields[slice_ptr_index], array_val, start, is_const,
- PtrLenUnknown);
- init_const_usize(g, const_val->data.x_struct.fields[slice_len_index], len);
-}
-
-ZigValue *create_const_slice(CodeGen *g, ZigValue *array_val, size_t start, size_t len, bool is_const) {
- ZigValue *const_val = g->pass1_arena->create();
- init_const_slice(g, const_val, array_val, start, len, is_const);
- return const_val;
-}
-
-void init_const_ptr_array(CodeGen *g, ZigValue *const_val, ZigValue *array_val,
- size_t elem_index, bool is_const, PtrLen ptr_len)
-{
- assert(array_val->type->id == ZigTypeIdArray);
- ZigType *child_type = array_val->type->data.array.child_type;
-
- const_val->special = ConstValSpecialStatic;
- const_val->type = get_pointer_to_type_extra(g, child_type, is_const, false,
- ptr_len, 0, 0, 0, false);
- const_val->data.x_ptr.special = ConstPtrSpecialBaseArray;
- const_val->data.x_ptr.data.base_array.array_val = array_val;
- const_val->data.x_ptr.data.base_array.elem_index = elem_index;
-}
-
-ZigValue *create_const_ptr_array(CodeGen *g, ZigValue *array_val, size_t elem_index, bool is_const,
- PtrLen ptr_len)
-{
- ZigValue *const_val = g->pass1_arena->create();
- init_const_ptr_array(g, const_val, array_val, elem_index, is_const, ptr_len);
- return const_val;
-}
-
-void init_const_ptr_ref(CodeGen *g, ZigValue *const_val, ZigValue *pointee_val, bool is_const) {
- const_val->special = ConstValSpecialStatic;
- const_val->type = get_pointer_to_type(g, pointee_val->type, is_const);
- const_val->data.x_ptr.special = ConstPtrSpecialRef;
- const_val->data.x_ptr.data.ref.pointee = pointee_val;
-}
-
-ZigValue *create_const_ptr_ref(CodeGen *g, ZigValue *pointee_val, bool is_const) {
- ZigValue *const_val = g->pass1_arena->create();
- init_const_ptr_ref(g, const_val, pointee_val, is_const);
- return const_val;
-}
-
-void init_const_ptr_hard_coded_addr(CodeGen *g, ZigValue *const_val, ZigType *pointee_type,
- size_t addr, bool is_const)
-{
- const_val->special = ConstValSpecialStatic;
- const_val->type = get_pointer_to_type(g, pointee_type, is_const);
- const_val->data.x_ptr.special = ConstPtrSpecialHardCodedAddr;
- const_val->data.x_ptr.data.hard_coded_addr.addr = addr;
-}
-
-ZigValue *create_const_ptr_hard_coded_addr(CodeGen *g, ZigType *pointee_type,
- size_t addr, bool is_const)
-{
- ZigValue *const_val = g->pass1_arena->create();
- init_const_ptr_hard_coded_addr(g, const_val, pointee_type, addr, is_const);
- return const_val;
-}
-
-ZigValue **alloc_const_vals_ptrs(CodeGen *g, size_t count) {
- return realloc_const_vals_ptrs(g, nullptr, 0, count);
-}
-
-ZigValue **realloc_const_vals_ptrs(CodeGen *g, ZigValue **ptr, size_t old_count, size_t new_count) {
- assert(new_count >= old_count);
-
- size_t new_item_count = new_count - old_count;
- ZigValue **result = heap::c_allocator.reallocate(ptr, old_count, new_count);
- ZigValue *vals = g->pass1_arena->allocate(new_item_count);
- for (size_t i = old_count; i < new_count; i += 1) {
- result[i] = &vals[i - old_count];
- }
- return result;
-}
-
-TypeStructField **alloc_type_struct_fields(size_t count) {
- return realloc_type_struct_fields(nullptr, 0, count);
-}
-
-TypeStructField **realloc_type_struct_fields(TypeStructField **ptr, size_t old_count, size_t new_count) {
- assert(new_count >= old_count);
-
- size_t new_item_count = new_count - old_count;
- TypeStructField **result = heap::c_allocator.reallocate(ptr, old_count, new_count);
- TypeStructField *vals = heap::c_allocator.allocate(new_item_count);
- for (size_t i = old_count; i < new_count; i += 1) {
- result[i] = &vals[i - old_count];
- }
- return result;
-}
-
-static ZigType *get_async_fn_type(CodeGen *g, ZigType *orig_fn_type) {
- if (orig_fn_type->data.fn.fn_type_id.cc == CallingConventionAsync)
- return orig_fn_type;
-
- ZigType *fn_type = heap::c_allocator.allocate_nonzero(1);
- *fn_type = *orig_fn_type;
- fn_type->data.fn.fn_type_id.cc = CallingConventionAsync;
- fn_type->llvm_type = nullptr;
- fn_type->llvm_di_type = nullptr;
-
- return fn_type;
-}
-
-// Traverse up to the very top ExprScope, which has children.
-// We have just arrived at the top from a child. That child,
-// and its next siblings, do not need to be marked. But the previous
-// siblings do.
-// x + (await y)
-// vs
-// (await y) + x
-static void mark_suspension_point(Scope *scope) {
- ScopeExpr *child_expr_scope = (scope->id == ScopeIdExpr) ? reinterpret_cast(scope) : nullptr;
- bool looking_for_exprs = true;
- for (;;) {
- scope = scope->parent;
- switch (scope->id) {
- case ScopeIdDeferExpr:
- case ScopeIdDecls:
- case ScopeIdFnDef:
- case ScopeIdCompTime:
- case ScopeIdNoSuspend:
- case ScopeIdCImport:
- case ScopeIdSuspend:
- case ScopeIdTypeOf:
- return;
- case ScopeIdVarDecl:
- case ScopeIdDefer:
- case ScopeIdBlock:
- looking_for_exprs = false;
- continue;
- case ScopeIdRuntime:
- continue;
- case ScopeIdLoop: {
- ScopeLoop *loop_scope = reinterpret_cast(scope);
- if (loop_scope->spill_scope != nullptr) {
- loop_scope->spill_scope->need_spill = MemoizedBoolTrue;
- }
- looking_for_exprs = false;
- continue;
- }
- case ScopeIdExpr: {
- ScopeExpr *parent_expr_scope = reinterpret_cast(scope);
- if (!looking_for_exprs) {
- if (parent_expr_scope->spill_harder) {
- parent_expr_scope->need_spill = MemoizedBoolTrue;
- }
- // Now we're only looking for a block, to see if it's in a loop (see the case ScopeIdBlock)
- continue;
- }
- if (child_expr_scope != nullptr) {
- for (size_t i = 0; parent_expr_scope->children_ptr[i] != child_expr_scope; i += 1) {
- assert(i < parent_expr_scope->children_len);
- parent_expr_scope->children_ptr[i]->need_spill = MemoizedBoolTrue;
- }
- }
- parent_expr_scope->need_spill = MemoizedBoolTrue;
- child_expr_scope = parent_expr_scope;
- continue;
- }
- }
- }
-}
-
-static bool scope_needs_spill(Scope *scope) {
- ScopeExpr *scope_expr = find_expr_scope(scope);
- if (scope_expr == nullptr) return false;
-
- switch (scope_expr->need_spill) {
- case MemoizedBoolUnknown:
- if (scope_needs_spill(scope_expr->base.parent)) {
- scope_expr->need_spill = MemoizedBoolTrue;
- return true;
- } else {
- scope_expr->need_spill = MemoizedBoolFalse;
- return false;
- }
- case MemoizedBoolFalse:
- return false;
- case MemoizedBoolTrue:
- return true;
- }
- zig_unreachable();
-}
-
-static ZigType *resolve_type_isf(ZigType *ty) {
- if (ty->id != ZigTypeIdPointer) return ty;
- InferredStructField *isf = ty->data.pointer.inferred_struct_field;
- if (isf == nullptr) return ty;
- TypeStructField *field = find_struct_type_field(isf->inferred_struct_type, isf->field_name);
- assert(field != nullptr);
- return field->type_entry;
-}
-
-static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) {
- Error err;
-
- if (frame_type->data.frame.locals_struct != nullptr)
- return ErrorNone;
-
- ZigFn *fn = frame_type->data.frame.fn;
- assert(!fn->type_entry->data.fn.is_generic);
-
- if (frame_type->data.frame.resolve_loop_type != nullptr) {
- if (!frame_type->data.frame.reported_loop_err) {
- add_node_error(g, fn->proto_node,
- buf_sprintf("'%s' depends on itself", buf_ptr(&frame_type->name)));
- }
- return ErrorSemanticAnalyzeFail;
- }
-
- switch (fn->anal_state) {
- case FnAnalStateInvalid:
- return ErrorSemanticAnalyzeFail;
- case FnAnalStateComplete:
- break;
- case FnAnalStateReady:
- analyze_fn_body(g, fn);
- if (fn->anal_state == FnAnalStateInvalid)
- return ErrorSemanticAnalyzeFail;
- break;
- case FnAnalStateProbing: {
- add_node_error(g, fn->proto_node,
- buf_sprintf("cannot resolve '%s': function not fully analyzed yet",
- buf_ptr(&frame_type->name)));
- return ErrorSemanticAnalyzeFail;
- }
- }
- analyze_fn_async(g, fn, false);
- if (fn->anal_state == FnAnalStateInvalid)
- return ErrorSemanticAnalyzeFail;
-
- if (!fn_is_async(fn)) {
- ZigType *fn_type = fn->type_entry;
- FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
- ZigType *ptr_return_type = get_pointer_to_type(g, fn_type_id->return_type, false);
-
- // label (grep this): [fn_frame_struct_layout]
- ZigList fields = {};
-
- fields.append({"@fn_ptr", g->builtin_types.entry_usize, 0});
- fields.append({"@resume_index", g->builtin_types.entry_usize, 0});
- fields.append({"@awaiter", g->builtin_types.entry_usize, 0});
-
- fields.append({"@result_ptr_callee", ptr_return_type, 0});
- fields.append({"@result_ptr_awaiter", ptr_return_type, 0});
- fields.append({"@result", fn_type_id->return_type, 0});
-
- if (codegen_fn_has_err_ret_tracing_arg(g, fn_type_id->return_type)) {
- ZigType *ptr_to_stack_trace_type = get_pointer_to_type(g, get_stack_trace_type(g), false);
- fields.append({"@ptr_stack_trace_callee", ptr_to_stack_trace_type, 0});
- fields.append({"@ptr_stack_trace_awaiter", ptr_to_stack_trace_type, 0});
-
- fields.append({"@stack_trace", get_stack_trace_type(g), 0});
- fields.append({"@instruction_addresses",
- get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count, nullptr), 0});
- }
-
- frame_type->data.frame.locals_struct = get_struct_type(g, buf_ptr(&frame_type->name),
- fields.items, fields.length, target_fn_align(g->zig_target));
- frame_type->abi_size = frame_type->data.frame.locals_struct->abi_size;
- frame_type->abi_align = frame_type->data.frame.locals_struct->abi_align;
- frame_type->size_in_bits = frame_type->data.frame.locals_struct->size_in_bits;
-
- return ErrorNone;
- }
-
- ZigType *fn_type = get_async_fn_type(g, fn->type_entry);
-
- if (fn->analyzed_executable.need_err_code_spill) {
- IrInstGenAlloca *alloca_gen = heap::c_allocator.create();
- alloca_gen->base.id = IrInstGenIdAlloca;
- alloca_gen->base.base.source_node = fn->proto_node;
- alloca_gen->base.base.scope = fn->child_scope;
- alloca_gen->base.value = g->pass1_arena->create();
- alloca_gen->base.value->type = get_pointer_to_type(g, g->builtin_types.entry_global_error_set, false);
- alloca_gen->base.base.ref_count = 1;
- alloca_gen->name_hint = "";
- fn->alloca_gen_list.append(alloca_gen);
- fn->err_code_spill = &alloca_gen->base;
- }
-
- ZigType *largest_call_frame_type = nullptr;
- // Later we'll change this to be largest_call_frame_type instead of void.
- IrInstGen *all_calls_alloca = ir_create_alloca(g, &fn->fndef_scope->base, fn->body_node,
- fn, g->builtin_types.entry_void, "@async_call_frame");
-
- for (size_t i = 0; i < fn->call_list.length; i += 1) {
- IrInstGenCall *call = fn->call_list.at(i);
- if (call->new_stack != nullptr) {
- // don't need to allocate a frame for this
- continue;
- }
- ZigFn *callee = call->fn_entry;
- if (callee == nullptr) {
- if (call->fn_ref->value->type->data.fn.fn_type_id.cc != CallingConventionAsync) {
- continue;
- }
- add_node_error(g, call->base.base.source_node,
- buf_sprintf("function is not comptime-known; @asyncCall required"));
- return ErrorSemanticAnalyzeFail;
- }
- if (callee->body_node == nullptr) {
- continue;
- }
- if (callee->anal_state == FnAnalStateProbing) {
- ErrorMsg *msg = add_node_error(g, fn->proto_node,
- buf_sprintf("unable to determine async function frame of '%s'", buf_ptr(&fn->symbol_name)));
- g->trace_err = add_error_note(g, msg, call->base.base.source_node,
- buf_sprintf("analysis of function '%s' depends on the frame", buf_ptr(&callee->symbol_name)));
- return ErrorSemanticAnalyzeFail;
- }
-
- ZigType *callee_frame_type = get_fn_frame_type(g, callee);
- frame_type->data.frame.resolve_loop_type = callee_frame_type;
- frame_type->data.frame.resolve_loop_src_node = call->base.base.source_node;
-
- analyze_fn_body(g, callee);
- if (callee->anal_state == FnAnalStateInvalid) {
- frame_type->data.frame.locals_struct = g->builtin_types.entry_invalid;
- return ErrorSemanticAnalyzeFail;
- }
- analyze_fn_async(g, callee, true);
- if (callee->inferred_async_node == inferred_async_checking) {
- assert(g->errors.length != 0);
- frame_type->data.frame.locals_struct = g->builtin_types.entry_invalid;
- return ErrorSemanticAnalyzeFail;
- }
- if (!fn_is_async(callee))
- continue;
-
- mark_suspension_point(call->base.base.scope);
-
- if ((err = type_resolve(g, callee_frame_type, ResolveStatusSizeKnown))) {
- return err;
- }
- if (largest_call_frame_type == nullptr ||
- callee_frame_type->abi_size > largest_call_frame_type->abi_size)
- {
- largest_call_frame_type = callee_frame_type;
- }
-
- call->frame_result_loc = all_calls_alloca;
- }
- if (largest_call_frame_type != nullptr) {
- all_calls_alloca->value->type = get_pointer_to_type(g, largest_call_frame_type, false);
- }
-
- // Since this frame is async, an await might represent a suspend point, and
- // therefore need to spill. It also needs to mark expr scopes as having to spill.
- // For example: foo() + await z
- // The funtion call result of foo() must be spilled.
- for (size_t i = 0; i < fn->await_list.length; i += 1) {
- IrInstGenAwait *await = fn->await_list.at(i);
- if (await->is_nosuspend) {
- continue;
- }
- if (await->base.value->special != ConstValSpecialRuntime) {
- // Known at comptime. No spill, no suspend.
- continue;
- }
- if (await->target_fn != nullptr) {
- // we might not need to suspend
- analyze_fn_async(g, await->target_fn, false);
- if (await->target_fn->anal_state == FnAnalStateInvalid) {
- frame_type->data.frame.locals_struct = g->builtin_types.entry_invalid;
- return ErrorSemanticAnalyzeFail;
- }
- if (!fn_is_async(await->target_fn)) {
- // This await does not represent a suspend point. No spill needed,
- // and no need to mark ExprScope.
- continue;
- }
- }
- // This await is a suspend point, but it might not need a spill.
- // We do need to mark the ExprScope as having a suspend point in it.
- mark_suspension_point(await->base.base.scope);
-
- if (await->result_loc != nullptr) {
- // If there's a result location, that is the spill
- continue;
- }
- if (await->base.base.ref_count == 0)
- continue;
- if (!type_has_bits(g, await->base.value->type))
- continue;
- await->result_loc = ir_create_alloca(g, await->base.base.scope, await->base.base.source_node, fn,
- await->base.value->type, "");
- }
- for (size_t block_i = 0; block_i < fn->analyzed_executable.basic_block_list.length; block_i += 1) {
- IrBasicBlockGen *block = fn->analyzed_executable.basic_block_list.at(block_i);
- for (size_t instr_i = 0; instr_i < block->instruction_list.length; instr_i += 1) {
- IrInstGen *instruction = block->instruction_list.at(instr_i);
- if (instruction->id == IrInstGenIdSuspendFinish) {
- mark_suspension_point(instruction->base.scope);
- }
- }
- }
- // Now that we've marked all the expr scopes that have to spill, we go over the instructions
- // and spill the relevant ones.
- for (size_t block_i = 0; block_i < fn->analyzed_executable.basic_block_list.length; block_i += 1) {
- IrBasicBlockGen *block = fn->analyzed_executable.basic_block_list.at(block_i);
- for (size_t instr_i = 0; instr_i < block->instruction_list.length; instr_i += 1) {
- IrInstGen *instruction = block->instruction_list.at(instr_i);
- if (instruction->id == IrInstGenIdAwait ||
- instruction->id == IrInstGenIdVarPtr ||
- instruction->id == IrInstGenIdAlloca ||
- instruction->id == IrInstGenIdSpillBegin ||
- instruction->id == IrInstGenIdSpillEnd)
- {
- // This instruction does its own spilling specially, or otherwise doesn't need it.
- continue;
- }
- if (instruction->id == IrInstGenIdCast &&
- reinterpret_cast(instruction)->cast_op == CastOpNoop)
- {
- // The IR instruction exists only to change the type according to Zig. No spill needed.
- continue;
- }
- if (instruction->value->special != ConstValSpecialRuntime)
- continue;
- if (instruction->base.ref_count == 0)
- continue;
- if ((err = type_resolve(g, instruction->value->type, ResolveStatusZeroBitsKnown)))
- return ErrorSemanticAnalyzeFail;
- if (!type_has_bits(g, instruction->value->type))
- continue;
- if (scope_needs_spill(instruction->base.scope)) {
- instruction->spill = ir_create_alloca(g, instruction->base.scope, instruction->base.source_node,
- fn, instruction->value->type, "");
- }
- }
- }
-
- FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
- ZigType *ptr_return_type = get_pointer_to_type(g, fn_type_id->return_type, false);
-
- // label (grep this): [fn_frame_struct_layout]
- ZigList fields = {};
-
- fields.append({"@fn_ptr", fn_type, 0});
- fields.append({"@resume_index", g->builtin_types.entry_usize, 0});
- fields.append({"@awaiter", g->builtin_types.entry_usize, 0});
-
- fields.append({"@result_ptr_callee", ptr_return_type, 0});
- fields.append({"@result_ptr_awaiter", ptr_return_type, 0});
- fields.append({"@result", fn_type_id->return_type, 0});
-
- if (codegen_fn_has_err_ret_tracing_arg(g, fn_type_id->return_type)) {
- ZigType *ptr_stack_trace_type = get_pointer_to_type(g, get_stack_trace_type(g), false);
- fields.append({"@ptr_stack_trace_callee", ptr_stack_trace_type, 0});
- fields.append({"@ptr_stack_trace_awaiter", ptr_stack_trace_type, 0});
- }
-
- for (size_t arg_i = 0; arg_i < fn_type_id->param_count; arg_i += 1) {
- FnTypeParamInfo *param_info = &fn_type_id->param_info[arg_i];
- AstNode *param_decl_node = get_param_decl_node(fn, arg_i);
- Buf *param_name;
- bool is_var_args = param_decl_node && param_decl_node->data.param_decl.is_var_args;
- if (param_decl_node && !is_var_args) {
- param_name = param_decl_node->data.param_decl.name;
- } else {
- param_name = buf_sprintf("@arg%" ZIG_PRI_usize, arg_i);
- }
- ZigType *param_type = resolve_type_isf(param_info->type);
- if ((err = type_resolve(g, param_type, ResolveStatusSizeKnown))) {
- return err;
- }
-
- fields.append({buf_ptr(param_name), param_type, 0});
- }
-
- if (codegen_fn_has_err_ret_tracing_stack(g, fn, true)) {
- fields.append({"@stack_trace", get_stack_trace_type(g), 0});
- fields.append({"@instruction_addresses",
- get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count, nullptr), 0});
- }
-
- for (size_t alloca_i = 0; alloca_i < fn->alloca_gen_list.length; alloca_i += 1) {
- IrInstGenAlloca *instruction = fn->alloca_gen_list.at(alloca_i);
- instruction->field_index = SIZE_MAX;
- ZigType *ptr_type = instruction->base.value->type;
- assert(ptr_type->id == ZigTypeIdPointer);
- ZigType *child_type = resolve_type_isf(ptr_type->data.pointer.child_type);
- if (!type_has_bits(g, child_type))
- continue;
- if (instruction->base.base.ref_count == 0)
- continue;
- if (instruction->base.value->special != ConstValSpecialRuntime) {
- if (const_ptr_pointee(nullptr, g, instruction->base.value, nullptr)->special !=
- ConstValSpecialRuntime)
- {
- continue;
- }
- }
-
- frame_type->data.frame.resolve_loop_type = child_type;
- frame_type->data.frame.resolve_loop_src_node = instruction->base.base.source_node;
- if ((err = type_resolve(g, child_type, ResolveStatusSizeKnown))) {
- return err;
- }
-
- const char *name;
- if (*instruction->name_hint == 0) {
- name = buf_ptr(buf_sprintf("@local%" ZIG_PRI_usize, alloca_i));
- } else {
- name = buf_ptr(buf_sprintf("%s.%" ZIG_PRI_usize, instruction->name_hint, alloca_i));
- }
- instruction->field_index = fields.length;
-
- fields.append({name, child_type, instruction->align});
- }
-
-
- frame_type->data.frame.locals_struct = get_struct_type(g, buf_ptr(&frame_type->name),
- fields.items, fields.length, target_fn_align(g->zig_target));
- frame_type->abi_size = frame_type->data.frame.locals_struct->abi_size;
- frame_type->abi_align = frame_type->data.frame.locals_struct->abi_align;
- frame_type->size_in_bits = frame_type->data.frame.locals_struct->size_in_bits;
-
- if (g->largest_frame_fn == nullptr || frame_type->abi_size > g->largest_frame_fn->frame_type->abi_size) {
- g->largest_frame_fn = fn;
- }
-
- return ErrorNone;
-}
-
-static Error resolve_pointer_zero_bits(CodeGen *g, ZigType *ty) {
- Error err;
-
- if (ty->abi_size != SIZE_MAX)
- return ErrorNone;
-
- if (ty->data.pointer.resolve_loop_flag_zero_bits) {
- ty->abi_size = g->builtin_types.entry_usize->abi_size;
- ty->size_in_bits = g->builtin_types.entry_usize->size_in_bits;
- ty->abi_align = g->builtin_types.entry_usize->abi_align;
- return ErrorNone;
- }
- ty->data.pointer.resolve_loop_flag_zero_bits = true;
-
- ZigType *elem_type;
- InferredStructField *isf = ty->data.pointer.inferred_struct_field;
- if (isf != nullptr) {
- TypeStructField *field = find_struct_type_field(isf->inferred_struct_type, isf->field_name);
- assert(field != nullptr);
- if (field->is_comptime) {
- ty->abi_size = 0;
- ty->size_in_bits = 0;
- ty->abi_align = 0;
- return ErrorNone;
- }
- elem_type = field->type_entry;
- } else {
- elem_type = ty->data.pointer.child_type;
- }
-
- bool has_bits;
- if ((err = type_has_bits2(g, elem_type, &has_bits)))
- return err;
-
- if (has_bits) {
- ty->abi_size = g->builtin_types.entry_usize->abi_size;
- ty->size_in_bits = g->builtin_types.entry_usize->size_in_bits;
- ty->abi_align = g->builtin_types.entry_usize->abi_align;
- } else {
- ty->abi_size = 0;
- ty->size_in_bits = 0;
- ty->abi_align = 0;
- }
- return ErrorNone;
-}
-
-Error type_resolve(CodeGen *g, ZigType *ty, ResolveStatus status) {
- if (type_is_invalid(ty))
- return ErrorSemanticAnalyzeFail;
- switch (status) {
- case ResolveStatusUnstarted:
- return ErrorNone;
- case ResolveStatusBeingInferred:
- zig_unreachable();
- case ResolveStatusInvalid:
- zig_unreachable();
- case ResolveStatusZeroBitsKnown:
- switch (ty->id) {
- case ZigTypeIdStruct:
- return resolve_struct_zero_bits(g, ty);
- case ZigTypeIdEnum:
- return resolve_enum_zero_bits(g, ty);
- case ZigTypeIdUnion:
- return resolve_union_zero_bits(g, ty);
- case ZigTypeIdPointer:
- return resolve_pointer_zero_bits(g, ty);
- default:
- return ErrorNone;
- }
- case ResolveStatusAlignmentKnown:
- switch (ty->id) {
- case ZigTypeIdStruct:
- return resolve_struct_alignment(g, ty);
- case ZigTypeIdEnum:
- return resolve_enum_zero_bits(g, ty);
- case ZigTypeIdUnion:
- return resolve_union_alignment(g, ty);
- case ZigTypeIdFnFrame:
- return resolve_async_frame(g, ty);
- case ZigTypeIdPointer:
- return resolve_pointer_zero_bits(g, ty);
- default:
- return ErrorNone;
- }
- case ResolveStatusSizeKnown:
- switch (ty->id) {
- case ZigTypeIdStruct:
- return resolve_struct_type(g, ty);
- case ZigTypeIdEnum:
- return resolve_enum_zero_bits(g, ty);
- case ZigTypeIdUnion:
- return resolve_union_type(g, ty);
- case ZigTypeIdFnFrame:
- return resolve_async_frame(g, ty);
- case ZigTypeIdPointer:
- return resolve_pointer_zero_bits(g, ty);
- default:
- return ErrorNone;
- }
- case ResolveStatusLLVMFwdDecl:
- case ResolveStatusLLVMFull:
- resolve_llvm_types(g, ty, status);
- return ErrorNone;
- }
- zig_unreachable();
-}
-
-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) {
- 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);
- 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->id == IrInstSrcIdConst);
- IrInstSrcConst *const_inst = reinterpret_cast(var->is_comptime);
- assert(const_inst->value->type->id == ZigTypeIdBool);
- var->is_comptime_memoized_value = const_inst->value->data.x_bool;
- var->is_comptime = nullptr;
- return var->is_comptime_memoized_value;
-}
-
-bool const_values_equal_ptr(ZigValue *a, ZigValue *b) {
- if (a->data.x_ptr.special != b->data.x_ptr.special)
- return false;
- switch (a->data.x_ptr.special) {
- case ConstPtrSpecialInvalid:
- zig_unreachable();
- case ConstPtrSpecialRef:
- if (a->data.x_ptr.data.ref.pointee != b->data.x_ptr.data.ref.pointee)
- return false;
- return true;
- case ConstPtrSpecialBaseArray:
- case ConstPtrSpecialSubArray:
- if (a->data.x_ptr.data.base_array.array_val != b->data.x_ptr.data.base_array.array_val) {
- return false;
- }
- if (a->data.x_ptr.data.base_array.elem_index != b->data.x_ptr.data.base_array.elem_index)
- return false;
- return true;
- case ConstPtrSpecialBaseStruct:
- if (a->data.x_ptr.data.base_struct.struct_val != b->data.x_ptr.data.base_struct.struct_val) {
- return false;
- }
- if (a->data.x_ptr.data.base_struct.field_index != b->data.x_ptr.data.base_struct.field_index)
- return false;
- return true;
- case ConstPtrSpecialBaseErrorUnionCode:
- if (a->data.x_ptr.data.base_err_union_code.err_union_val !=
- b->data.x_ptr.data.base_err_union_code.err_union_val)
- {
- return false;
- }
- return true;
- case ConstPtrSpecialBaseErrorUnionPayload:
- if (a->data.x_ptr.data.base_err_union_payload.err_union_val !=
- b->data.x_ptr.data.base_err_union_payload.err_union_val)
- {
- return false;
- }
- return true;
- case ConstPtrSpecialBaseOptionalPayload:
- if (a->data.x_ptr.data.base_optional_payload.optional_val !=
- b->data.x_ptr.data.base_optional_payload.optional_val)
- {
- return false;
- }
- return true;
- case ConstPtrSpecialHardCodedAddr:
- if (a->data.x_ptr.data.hard_coded_addr.addr != b->data.x_ptr.data.hard_coded_addr.addr)
- return false;
- return true;
- case ConstPtrSpecialDiscard:
- return true;
- case ConstPtrSpecialFunction:
- return a->data.x_ptr.data.fn.fn_entry == b->data.x_ptr.data.fn.fn_entry;
- case ConstPtrSpecialNull:
- return true;
- }
- zig_unreachable();
-}
-
-static bool const_values_equal_array(CodeGen *g, ZigValue *a, ZigValue *b, size_t len) {
- if (a->data.x_array.special == ConstArraySpecialUndef &&
- b->data.x_array.special == ConstArraySpecialUndef)
- {
- return true;
- }
- if (a->data.x_array.special == ConstArraySpecialUndef ||
- b->data.x_array.special == ConstArraySpecialUndef)
- {
- return false;
- }
- if (a->data.x_array.special == ConstArraySpecialBuf &&
- b->data.x_array.special == ConstArraySpecialBuf)
- {
- return buf_eql_buf(a->data.x_array.data.s_buf, b->data.x_array.data.s_buf);
- }
- expand_undef_array(g, a);
- expand_undef_array(g, b);
-
- ZigValue *a_elems = a->data.x_array.data.s_none.elements;
- ZigValue *b_elems = b->data.x_array.data.s_none.elements;
-
- for (size_t i = 0; i < len; i += 1) {
- if (!const_values_equal(g, &a_elems[i], &b_elems[i]))
- return false;
- }
-
- return true;
-}
-
-bool const_values_equal(CodeGen *g, ZigValue *a, ZigValue *b) {
- if (a->type->id != b->type->id) return false;
- if (a->type == b->type) {
- switch (type_has_one_possible_value(g, a->type)) {
- case OnePossibleValueInvalid:
- zig_unreachable();
- case OnePossibleValueNo:
- break;
- case OnePossibleValueYes:
- return true;
- }
- }
- if (a->special == ConstValSpecialUndef || b->special == ConstValSpecialUndef) {
- return a->special == b->special;
- }
- assert(a->special == ConstValSpecialStatic);
- assert(b->special == ConstValSpecialStatic);
- switch (a->type->id) {
- case ZigTypeIdOpaque:
- zig_unreachable();
- case ZigTypeIdEnum:
- return bigint_cmp(&a->data.x_enum_tag, &b->data.x_enum_tag) == CmpEQ;
- case ZigTypeIdUnion: {
- ConstUnionValue *union1 = &a->data.x_union;
- ConstUnionValue *union2 = &b->data.x_union;
-
- if (bigint_cmp(&union1->tag, &union2->tag) == CmpEQ) {
- TypeUnionField *field = find_union_field_by_tag(a->type, &union1->tag);
- assert(field != nullptr);
- if (!type_has_bits(g, field->type_entry))
- return true;
- assert(find_union_field_by_tag(a->type, &union2->tag) != nullptr);
- return const_values_equal(g, union1->payload, union2->payload);
- }
- return false;
- }
- case ZigTypeIdMetaType:
- return a->data.x_type == b->data.x_type;
- case ZigTypeIdVoid:
- return true;
- case ZigTypeIdErrorSet:
- return a->data.x_err_set->value == b->data.x_err_set->value;
- case ZigTypeIdBool:
- return a->data.x_bool == b->data.x_bool;
- case ZigTypeIdFloat:
- assert(a->type->data.floating.bit_count == b->type->data.floating.bit_count);
- switch (a->type->data.floating.bit_count) {
- case 16:
- return f16_eq(a->data.x_f16, b->data.x_f16);
- case 32:
- return a->data.x_f32 == b->data.x_f32;
- case 64:
- return a->data.x_f64 == b->data.x_f64;
- case 128:
- return f128M_eq(&a->data.x_f128, &b->data.x_f128);
- default:
- zig_unreachable();
- }
- case ZigTypeIdComptimeFloat:
- return bigfloat_cmp(&a->data.x_bigfloat, &b->data.x_bigfloat) == CmpEQ;
- case ZigTypeIdInt:
- case ZigTypeIdComptimeInt:
- return bigint_cmp(&a->data.x_bigint, &b->data.x_bigint) == CmpEQ;
- case ZigTypeIdEnumLiteral:
- return buf_eql_buf(a->data.x_enum_literal, b->data.x_enum_literal);
- case ZigTypeIdPointer:
- case ZigTypeIdFn:
- return const_values_equal_ptr(a, b);
- case ZigTypeIdVector:
- assert(a->type->data.vector.len == b->type->data.vector.len);
- return const_values_equal_array(g, a, b, a->type->data.vector.len);
- case ZigTypeIdArray: {
- assert(a->type->data.array.len == b->type->data.array.len);
- return const_values_equal_array(g, a, b, a->type->data.array.len);
- }
- case ZigTypeIdStruct:
- for (size_t i = 0; i < a->type->data.structure.src_field_count; i += 1) {
- ZigValue *field_a = a->data.x_struct.fields[i];
- ZigValue *field_b = b->data.x_struct.fields[i];
- if (!const_values_equal(g, field_a, field_b))
- return false;
- }
- return true;
- case ZigTypeIdFnFrame:
- zig_panic("TODO");
- case ZigTypeIdAnyFrame:
- zig_panic("TODO");
- case ZigTypeIdUndefined:
- zig_panic("TODO");
- case ZigTypeIdNull:
- zig_panic("TODO");
- case ZigTypeIdOptional:
- if (get_src_ptr_type(a->type) != nullptr)
- return const_values_equal_ptr(a, b);
- if (a->data.x_optional == nullptr || b->data.x_optional == nullptr) {
- return (a->data.x_optional == nullptr && b->data.x_optional == nullptr);
- } else {
- return const_values_equal(g, a->data.x_optional, b->data.x_optional);
- }
- case ZigTypeIdErrorUnion:
- zig_panic("TODO");
- case ZigTypeIdBoundFn:
- case ZigTypeIdInvalid:
- case ZigTypeIdUnreachable:
- zig_unreachable();
- }
- zig_unreachable();
-}
-
-void eval_min_max_value_int(CodeGen *g, ZigType *int_type, BigInt *bigint, bool is_max) {
- assert(int_type->id == ZigTypeIdInt);
- if (int_type->data.integral.bit_count == 0) {
- bigint_init_unsigned(bigint, 0);
- return;
- }
- if (is_max) {
- // is_signed=true (1 << (bit_count - 1)) - 1
- // is_signed=false (1 << (bit_count - 0)) - 1
- BigInt one = {0};
- bigint_init_unsigned(&one, 1);
-
- size_t shift_amt = int_type->data.integral.bit_count - (int_type->data.integral.is_signed ? 1 : 0);
- BigInt bit_count_bi = {0};
- bigint_init_unsigned(&bit_count_bi, shift_amt);
-
- BigInt shifted_bi = {0};
- bigint_shl(&shifted_bi, &one, &bit_count_bi);
-
- bigint_sub(bigint, &shifted_bi, &one);
- } else if (int_type->data.integral.is_signed) {
- // - (1 << (bit_count - 1))
- BigInt one = {0};
- bigint_init_unsigned(&one, 1);
-
- BigInt bit_count_bi = {0};
- bigint_init_unsigned(&bit_count_bi, int_type->data.integral.bit_count - 1);
-
- BigInt shifted_bi = {0};
- bigint_shl(&shifted_bi, &one, &bit_count_bi);
-
- bigint_negate(bigint, &shifted_bi);
- } else {
- bigint_init_unsigned(bigint, 0);
- }
-}
-
-void eval_min_max_value(CodeGen *g, ZigType *type_entry, ZigValue *const_val, bool is_max) {
- if (type_entry->id == ZigTypeIdInt) {
- const_val->special = ConstValSpecialStatic;
- eval_min_max_value_int(g, type_entry, &const_val->data.x_bigint, is_max);
- } else if (type_entry->id == ZigTypeIdBool) {
- const_val->special = ConstValSpecialStatic;
- const_val->data.x_bool = is_max;
- } else if (type_entry->id == ZigTypeIdVoid) {
- // nothing to do
- } else {
- zig_unreachable();
- }
-}
-
-static void render_const_val_ptr(CodeGen *g, Buf *buf, ZigValue *const_val, ZigType *type_entry) {
- if (type_entry->id == ZigTypeIdPointer && type_entry->data.pointer.child_type->id == ZigTypeIdOpaque) {
- buf_append_buf(buf, &type_entry->name);
- return;
- }
-
- switch (const_val->data.x_ptr.special) {
- case ConstPtrSpecialInvalid:
- zig_unreachable();
- case ConstPtrSpecialRef:
- case ConstPtrSpecialBaseStruct:
- case ConstPtrSpecialBaseErrorUnionCode:
- case ConstPtrSpecialBaseErrorUnionPayload:
- case ConstPtrSpecialBaseOptionalPayload:
- buf_appendf(buf, "*");
- // TODO we need a source node for const_ptr_pointee because it can generate compile errors
- render_const_value(g, buf, const_ptr_pointee(nullptr, g, const_val, nullptr));
- return;
- case ConstPtrSpecialBaseArray:
- case ConstPtrSpecialSubArray:
- buf_appendf(buf, "*");
- // TODO we need a source node for const_ptr_pointee because it can generate compile errors
- render_const_value(g, buf, const_ptr_pointee(nullptr, g, const_val, nullptr));
- return;
- case ConstPtrSpecialHardCodedAddr:
- buf_appendf(buf, "(%s)(%" ZIG_PRI_x64 ")", buf_ptr(&type_entry->name),
- const_val->data.x_ptr.data.hard_coded_addr.addr);
- return;
- case ConstPtrSpecialDiscard:
- buf_append_str(buf, "*_");
- return;
- case ConstPtrSpecialFunction:
- {
- ZigFn *fn_entry = const_val->data.x_ptr.data.fn.fn_entry;
- buf_appendf(buf, "@ptrCast(%s, %s)", buf_ptr(&const_val->type->name), buf_ptr(&fn_entry->symbol_name));
- return;
- }
- case ConstPtrSpecialNull:
- buf_append_str(buf, "null");
- return;
- }
- zig_unreachable();
-}
-
-static void render_const_val_err_set(CodeGen *g, Buf *buf, ZigValue *const_val, ZigType *type_entry) {
- if (const_val->data.x_err_set == nullptr) {
- buf_append_str(buf, "null");
- } else {
- buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->name), buf_ptr(&const_val->data.x_err_set->name));
- }
-}
-
-static void render_const_val_array(CodeGen *g, Buf *buf, Buf *type_name, ZigValue *const_val, uint64_t start, uint64_t len) {
- ConstArrayValue *array = &const_val->data.x_array;
- switch (array->special) {
- case ConstArraySpecialUndef:
- buf_append_str(buf, "undefined");
- return;
- case ConstArraySpecialBuf: {
- Buf *array_buf = array->data.s_buf;
- const char *base = &buf_ptr(array_buf)[start];
- assert(start + len <= buf_len(array_buf));
-
- buf_append_char(buf, '"');
- for (size_t i = 0; i < len; i += 1) {
- uint8_t c = base[i];
- if (c == '"') {
- buf_append_str(buf, "\\\"");
- } else {
- buf_append_char(buf, c);
- }
- }
- buf_append_char(buf, '"');
- return;
- }
- case ConstArraySpecialNone: {
- assert(start + len <= const_val->type->data.array.len);
- ZigValue *base = &array->data.s_none.elements[start];
- assert(len == 0 || base != nullptr);
-
- buf_appendf(buf, "%s{", buf_ptr(type_name));
- for (uint64_t i = 0; i < len; i += 1) {
- if (i != 0) buf_appendf(buf, ",");
- render_const_value(g, buf, &base[i]);
- }
- buf_appendf(buf, "}");
- return;
- }
- }
- zig_unreachable();
-}
-
-void render_const_value(CodeGen *g, Buf *buf, ZigValue *const_val) {
- if (const_val == nullptr) {
- buf_appendf(buf, "(invalid nullptr value)");
- return;
- }
- switch (const_val->special) {
- case ConstValSpecialRuntime:
- buf_appendf(buf, "(runtime value)");
- return;
- case ConstValSpecialLazy:
- buf_appendf(buf, "(lazy value)");
- return;
- case ConstValSpecialUndef:
- buf_appendf(buf, "undefined");
- return;
- case ConstValSpecialStatic:
- break;
- }
- assert(const_val->type);
-
- ZigType *type_entry = const_val->type;
- switch (type_entry->id) {
- case ZigTypeIdOpaque:
- zig_unreachable();
- case ZigTypeIdInvalid:
- buf_appendf(buf, "(invalid)");
- return;
- case ZigTypeIdVoid:
- buf_appendf(buf, "{}");
- return;
- case ZigTypeIdComptimeFloat:
- bigfloat_append_buf(buf, &const_val->data.x_bigfloat);
- return;
- case ZigTypeIdFloat:
- switch (type_entry->data.floating.bit_count) {
- case 16:
- buf_appendf(buf, "%f", zig_f16_to_double(const_val->data.x_f16));
- return;
- case 32:
- buf_appendf(buf, "%f", const_val->data.x_f32);
- return;
- case 64:
- buf_appendf(buf, "%f", const_val->data.x_f64);
- return;
- case 128:
- {
- const size_t extra_len = 100;
- size_t old_len = buf_len(buf);
- buf_resize(buf, old_len + extra_len);
- float64_t f64_value = f128M_to_f64(&const_val->data.x_f128);
- double double_value;
- memcpy(&double_value, &f64_value, sizeof(double));
- // TODO actual f128 printing to decimal
- int len = snprintf(buf_ptr(buf) + old_len, extra_len, "%f", double_value);
- assert(len > 0);
- buf_resize(buf, old_len + len);
- return;
- }
- default:
- zig_unreachable();
- }
- case ZigTypeIdComptimeInt:
- case ZigTypeIdInt:
- bigint_append_buf(buf, &const_val->data.x_bigint, 10);
- return;
- case ZigTypeIdEnumLiteral:
- buf_append_buf(buf, const_val->data.x_enum_literal);
- return;
- case ZigTypeIdMetaType:
- buf_appendf(buf, "%s", buf_ptr(&const_val->data.x_type->name));
- return;
- case ZigTypeIdUnreachable:
- buf_appendf(buf, "unreachable");
- return;
- case ZigTypeIdBool:
- {
- const char *value = const_val->data.x_bool ? "true" : "false";
- buf_appendf(buf, "%s", value);
- return;
- }
- case ZigTypeIdFn:
- {
- assert(const_val->data.x_ptr.mut == ConstPtrMutComptimeConst);
- assert(const_val->data.x_ptr.special == ConstPtrSpecialFunction);
- ZigFn *fn_entry = const_val->data.x_ptr.data.fn.fn_entry;
- buf_appendf(buf, "%s", buf_ptr(&fn_entry->symbol_name));
- return;
- }
- case ZigTypeIdPointer:
- return render_const_val_ptr(g, buf, const_val, type_entry);
- case ZigTypeIdArray: {
- uint64_t len = type_entry->data.array.len;
- render_const_val_array(g, buf, &type_entry->name, const_val, 0, len);
- return;
- }
- case ZigTypeIdVector: {
- uint32_t len = type_entry->data.vector.len;
- render_const_val_array(g, buf, &type_entry->name, const_val, 0, len);
- return;
- }
- case ZigTypeIdNull:
- {
- buf_appendf(buf, "null");
- return;
- }
- case ZigTypeIdUndefined:
- {
- buf_appendf(buf, "undefined");
- return;
- }
- case ZigTypeIdOptional:
- {
- if (get_src_ptr_type(const_val->type) != nullptr)
- return render_const_val_ptr(g, buf, const_val, type_entry->data.maybe.child_type);
- if (type_entry->data.maybe.child_type->id == ZigTypeIdErrorSet)
- return render_const_val_err_set(g, buf, const_val, type_entry->data.maybe.child_type);
- if (const_val->data.x_optional) {
- render_const_value(g, buf, const_val->data.x_optional);
- } else {
- buf_appendf(buf, "null");
- }
- return;
- }
- case ZigTypeIdBoundFn:
- {
- ZigFn *fn_entry = const_val->data.x_bound_fn.fn;
- buf_appendf(buf, "(bound fn %s)", buf_ptr(&fn_entry->symbol_name));
- return;
- }
- case ZigTypeIdStruct:
- {
- if (is_slice(type_entry)) {
- ZigValue *len_val = const_val->data.x_struct.fields[slice_len_index];
- size_t len = bigint_as_usize(&len_val->data.x_bigint);
-
- ZigValue *ptr_val = const_val->data.x_struct.fields[slice_ptr_index];
- if (ptr_val->special == ConstValSpecialUndef) {
- assert(len == 0);
- buf_appendf(buf, "((%s)(undefined))[0..0]", buf_ptr(&type_entry->name));
- return;
- }
- assert(ptr_val->data.x_ptr.special == ConstPtrSpecialBaseArray);
- ZigValue *array = ptr_val->data.x_ptr.data.base_array.array_val;
- size_t start = ptr_val->data.x_ptr.data.base_array.elem_index;
-
- render_const_val_array(g, buf, &type_entry->name, array, start, len);
- } else {
- buf_appendf(buf, "(struct %s constant)", buf_ptr(&type_entry->name));
- }
- return;
- }
- case ZigTypeIdEnum:
- {
- TypeEnumField *field = find_enum_field_by_tag(type_entry, &const_val->data.x_enum_tag);
- if(field != nullptr){
- buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->name), buf_ptr(field->name));
- } else {
- // untagged value in a non-exhaustive enum
- buf_appendf(buf, "%s.(", buf_ptr(&type_entry->name));
- bigint_append_buf(buf, &const_val->data.x_enum_tag, 10);
- buf_appendf(buf, ")");
- }
- return;
- }
- case ZigTypeIdErrorUnion:
- {
- buf_appendf(buf, "%s(", buf_ptr(&type_entry->name));
- ErrorTableEntry *err_set = const_val->data.x_err_union.error_set->data.x_err_set;
- if (err_set == nullptr) {
- render_const_value(g, buf, const_val->data.x_err_union.payload);
- } else {
- buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->data.error_union.err_set_type->name),
- buf_ptr(&err_set->name));
- }
- buf_appendf(buf, ")");
- return;
- }
- case ZigTypeIdUnion:
- {
- const BigInt *tag = &const_val->data.x_union.tag;
- TypeUnionField *field = find_union_field_by_tag(type_entry, tag);
- buf_appendf(buf, "%s { .%s = ", buf_ptr(&type_entry->name), buf_ptr(field->name));
- render_const_value(g, buf, const_val->data.x_union.payload);
- buf_append_str(buf, "}");
- return;
- }
- case ZigTypeIdErrorSet:
- return render_const_val_err_set(g, buf, const_val, type_entry);
- case ZigTypeIdFnFrame:
- buf_appendf(buf, "(TODO: async function frame value)");
- return;
-
- case ZigTypeIdAnyFrame:
- buf_appendf(buf, "(TODO: anyframe value)");
- return;
-
- }
- zig_unreachable();
-}
-
-ZigType *make_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits) {
- assert(size_in_bits <= 65535);
- ZigType *entry = new_type_table_entry(ZigTypeIdInt);
-
- entry->size_in_bits = size_in_bits;
- if (size_in_bits != 0) {
- entry->llvm_type = LLVMIntType(size_in_bits);
- entry->abi_size = LLVMABISizeOfType(g->target_data_ref, entry->llvm_type);
- entry->abi_align = LLVMABIAlignmentOfType(g->target_data_ref, entry->llvm_type);
-
- if (size_in_bits >= 128 && entry->abi_align < 16) {
- // Override the incorrect alignment reported by LLVM. Clang does this as well.
- // On x86_64 there are some instructions like CMPXCHG16B which require this.
- // On all targets, integers 128 bits and above have ABI alignment of 16.
- // However for some targets, LLVM incorrectly reports this as 8.
- // See: https://github.com/ziglang/zig/issues/2987
- entry->abi_align = 16;
- }
- }
-
- const char u_or_i = is_signed ? 'i' : 'u';
- buf_resize(&entry->name, 0);
- buf_appendf(&entry->name, "%c%" PRIu32, u_or_i, size_in_bits);
-
- entry->data.integral.is_signed = is_signed;
- entry->data.integral.bit_count = size_in_bits;
- return entry;
-}
-
-uint32_t type_id_hash(TypeId x) {
- switch (x.id) {
- case ZigTypeIdInvalid:
- case ZigTypeIdOpaque:
- case ZigTypeIdMetaType:
- case ZigTypeIdVoid:
- case ZigTypeIdBool:
- case ZigTypeIdUnreachable:
- case ZigTypeIdFloat:
- case ZigTypeIdStruct:
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdEnumLiteral:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdOptional:
- case ZigTypeIdErrorSet:
- case ZigTypeIdEnum:
- case ZigTypeIdUnion:
- case ZigTypeIdFn:
- case ZigTypeIdBoundFn:
- case ZigTypeIdFnFrame:
- case ZigTypeIdAnyFrame:
- zig_unreachable();
- case ZigTypeIdErrorUnion:
- return hash_ptr(x.data.error_union.err_set_type) ^ hash_ptr(x.data.error_union.payload_type);
- case ZigTypeIdPointer:
- return hash_ptr(x.data.pointer.child_type) +
- (uint32_t)x.data.pointer.ptr_len * 1120226602u +
- (x.data.pointer.is_const ? (uint32_t)2749109194 : (uint32_t)4047371087) +
- (x.data.pointer.is_volatile ? (uint32_t)536730450 : (uint32_t)1685612214) +
- (x.data.pointer.allow_zero ? (uint32_t)3324284834 : (uint32_t)3584904923) +
- (((uint32_t)x.data.pointer.alignment) ^ (uint32_t)0x777fbe0e) +
- (((uint32_t)x.data.pointer.bit_offset_in_host) ^ (uint32_t)2639019452) +
- (((uint32_t)x.data.pointer.vector_index) ^ (uint32_t)0x19199716) +
- (((uint32_t)x.data.pointer.host_int_bytes) ^ (uint32_t)529908881) *
- (x.data.pointer.sentinel ? hash_const_val(x.data.pointer.sentinel) : (uint32_t)2955491856);
- case ZigTypeIdArray:
- return hash_ptr(x.data.array.child_type) *
- ((uint32_t)x.data.array.size ^ (uint32_t)2122979968) *
- (x.data.array.sentinel ? hash_const_val(x.data.array.sentinel) : (uint32_t)1927201585);
- case ZigTypeIdInt:
- return (x.data.integer.is_signed ? (uint32_t)2652528194 : (uint32_t)163929201) +
- (((uint32_t)x.data.integer.bit_count) ^ (uint32_t)2998081557);
- case ZigTypeIdVector:
- return hash_ptr(x.data.vector.elem_type) * (x.data.vector.len * 526582681);
- }
- zig_unreachable();
-}
-
-bool type_id_eql(TypeId a, TypeId b) {
- if (a.id != b.id)
- return false;
- switch (a.id) {
- case ZigTypeIdInvalid:
- case ZigTypeIdMetaType:
- case ZigTypeIdVoid:
- case ZigTypeIdBool:
- case ZigTypeIdUnreachable:
- case ZigTypeIdFloat:
- case ZigTypeIdStruct:
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdEnumLiteral:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdOptional:
- case ZigTypeIdErrorSet:
- case ZigTypeIdEnum:
- case ZigTypeIdUnion:
- case ZigTypeIdFn:
- case ZigTypeIdBoundFn:
- case ZigTypeIdOpaque:
- case ZigTypeIdFnFrame:
- case ZigTypeIdAnyFrame:
- zig_unreachable();
- case ZigTypeIdErrorUnion:
- return a.data.error_union.err_set_type == b.data.error_union.err_set_type &&
- a.data.error_union.payload_type == b.data.error_union.payload_type;
-
- case ZigTypeIdPointer:
- return a.data.pointer.child_type == b.data.pointer.child_type &&
- a.data.pointer.ptr_len == b.data.pointer.ptr_len &&
- a.data.pointer.is_const == b.data.pointer.is_const &&
- a.data.pointer.is_volatile == b.data.pointer.is_volatile &&
- a.data.pointer.allow_zero == b.data.pointer.allow_zero &&
- a.data.pointer.alignment == b.data.pointer.alignment &&
- a.data.pointer.bit_offset_in_host == b.data.pointer.bit_offset_in_host &&
- a.data.pointer.vector_index == b.data.pointer.vector_index &&
- a.data.pointer.host_int_bytes == b.data.pointer.host_int_bytes &&
- (
- a.data.pointer.sentinel == b.data.pointer.sentinel ||
- (a.data.pointer.sentinel != nullptr && b.data.pointer.sentinel != nullptr &&
- const_values_equal(a.data.pointer.codegen, a.data.pointer.sentinel, b.data.pointer.sentinel))
- ) &&
- (
- a.data.pointer.inferred_struct_field == b.data.pointer.inferred_struct_field ||
- (a.data.pointer.inferred_struct_field != nullptr &&
- b.data.pointer.inferred_struct_field != nullptr &&
- a.data.pointer.inferred_struct_field->inferred_struct_type ==
- b.data.pointer.inferred_struct_field->inferred_struct_type &&
- buf_eql_buf(a.data.pointer.inferred_struct_field->field_name,
- b.data.pointer.inferred_struct_field->field_name))
- );
- case ZigTypeIdArray:
- return a.data.array.child_type == b.data.array.child_type &&
- a.data.array.size == b.data.array.size &&
- (
- a.data.array.sentinel == b.data.array.sentinel ||
- (a.data.array.sentinel != nullptr && b.data.array.sentinel != nullptr &&
- const_values_equal(a.data.array.codegen, a.data.array.sentinel, b.data.array.sentinel))
- );
- case ZigTypeIdInt:
- return a.data.integer.is_signed == b.data.integer.is_signed &&
- a.data.integer.bit_count == b.data.integer.bit_count;
- case ZigTypeIdVector:
- return a.data.vector.elem_type == b.data.vector.elem_type &&
- a.data.vector.len == b.data.vector.len;
- }
- zig_unreachable();
-}
-
-uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) {
- switch (x.id) {
- case ZigLLVMFnIdCtz:
- return (uint32_t)(x.data.ctz.bit_count) * (uint32_t)810453934;
- case ZigLLVMFnIdClz:
- return (uint32_t)(x.data.clz.bit_count) * (uint32_t)2428952817;
- case ZigLLVMFnIdPopCount:
- return (uint32_t)(x.data.clz.bit_count) * (uint32_t)101195049;
- case ZigLLVMFnIdFloatOp:
- return (uint32_t)(x.data.floating.bit_count) * ((uint32_t)x.id + 1025) +
- (uint32_t)(x.data.floating.vector_len) * (((uint32_t)x.id << 5) + 1025) +
- (uint32_t)(x.data.floating.op) * (uint32_t)43789879;
- case ZigLLVMFnIdFMA:
- return (uint32_t)(x.data.floating.bit_count) * ((uint32_t)x.id + 1025) +
- (uint32_t)(x.data.floating.vector_len) * (((uint32_t)x.id << 5) + 1025);
- case ZigLLVMFnIdBswap:
- return (uint32_t)(x.data.bswap.bit_count) * ((uint32_t)3661994335) +
- (uint32_t)(x.data.bswap.vector_len) * (((uint32_t)x.id << 5) + 1025);
- case ZigLLVMFnIdBitReverse:
- return (uint32_t)(x.data.bit_reverse.bit_count) * (uint32_t)2621398431;
- case ZigLLVMFnIdOverflowArithmetic:
- return ((uint32_t)(x.data.overflow_arithmetic.bit_count) * 87135777) +
- ((uint32_t)(x.data.overflow_arithmetic.add_sub_mul) * 31640542) +
- ((uint32_t)(x.data.overflow_arithmetic.is_signed) ? 1062315172 : 314955820) +
- x.data.overflow_arithmetic.vector_len * 1435156945;
- }
- zig_unreachable();
-}
-
-bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {
- if (a.id != b.id)
- return false;
- switch (a.id) {
- case ZigLLVMFnIdCtz:
- return a.data.ctz.bit_count == b.data.ctz.bit_count;
- case ZigLLVMFnIdClz:
- return a.data.clz.bit_count == b.data.clz.bit_count;
- case ZigLLVMFnIdPopCount:
- return a.data.pop_count.bit_count == b.data.pop_count.bit_count;
- case ZigLLVMFnIdBswap:
- return a.data.bswap.bit_count == b.data.bswap.bit_count &&
- a.data.bswap.vector_len == b.data.bswap.vector_len;
- case ZigLLVMFnIdBitReverse:
- return a.data.bit_reverse.bit_count == b.data.bit_reverse.bit_count;
- case ZigLLVMFnIdFloatOp:
- return a.data.floating.bit_count == b.data.floating.bit_count &&
- a.data.floating.vector_len == b.data.floating.vector_len &&
- a.data.floating.op == b.data.floating.op;
- case ZigLLVMFnIdFMA:
- return a.data.floating.bit_count == b.data.floating.bit_count &&
- a.data.floating.vector_len == b.data.floating.vector_len;
- case ZigLLVMFnIdOverflowArithmetic:
- return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) &&
- (a.data.overflow_arithmetic.add_sub_mul == b.data.overflow_arithmetic.add_sub_mul) &&
- (a.data.overflow_arithmetic.is_signed == b.data.overflow_arithmetic.is_signed) &&
- (a.data.overflow_arithmetic.vector_len == b.data.overflow_arithmetic.vector_len);
- }
- zig_unreachable();
-}
-
-static void init_const_undefined(CodeGen *g, ZigValue *const_val) {
- Error err;
- ZigType *wanted_type = const_val->type;
- if (wanted_type->id == ZigTypeIdArray) {
- const_val->special = ConstValSpecialStatic;
- const_val->data.x_array.special = ConstArraySpecialUndef;
- } else if (wanted_type->id == ZigTypeIdStruct) {
- if ((err = type_resolve(g, wanted_type, ResolveStatusZeroBitsKnown))) {
- return;
- }
-
- const_val->special = ConstValSpecialStatic;
- size_t field_count = wanted_type->data.structure.src_field_count;
- const_val->data.x_struct.fields = alloc_const_vals_ptrs(g, field_count);
- for (size_t i = 0; i < field_count; i += 1) {
- ZigValue *field_val = const_val->data.x_struct.fields[i];
- field_val->type = resolve_struct_field_type(g, wanted_type->data.structure.fields[i]);
- assert(field_val->type);
- init_const_undefined(g, field_val);
- field_val->parent.id = ConstParentIdStruct;
- field_val->parent.data.p_struct.struct_val = const_val;
- field_val->parent.data.p_struct.field_index = i;
- }
- } else {
- const_val->special = ConstValSpecialUndef;
- }
-}
-
-void expand_undef_struct(CodeGen *g, ZigValue *const_val) {
- if (const_val->special == ConstValSpecialUndef) {
- init_const_undefined(g, const_val);
- }
-}
-
-// Canonicalize the array value as ConstArraySpecialNone
-void expand_undef_array(CodeGen *g, ZigValue *const_val) {
- size_t elem_count;
- ZigType *elem_type;
- if (const_val->type->id == ZigTypeIdArray) {
- elem_count = const_val->type->data.array.len;
- elem_type = const_val->type->data.array.child_type;
- } else if (const_val->type->id == ZigTypeIdVector) {
- elem_count = const_val->type->data.vector.len;
- elem_type = const_val->type->data.vector.elem_type;
- } else {
- zig_unreachable();
- }
- if (const_val->special == ConstValSpecialUndef) {
- const_val->special = ConstValSpecialStatic;
- const_val->data.x_array.special = ConstArraySpecialUndef;
- }
- switch (const_val->data.x_array.special) {
- case ConstArraySpecialNone:
- return;
- case ConstArraySpecialUndef: {
- const_val->data.x_array.special = ConstArraySpecialNone;
- const_val->data.x_array.data.s_none.elements = g->pass1_arena->allocate(elem_count);
- for (size_t i = 0; i < elem_count; i += 1) {
- ZigValue *element_val = &const_val->data.x_array.data.s_none.elements[i];
- element_val->type = elem_type;
- init_const_undefined(g, element_val);
- element_val->parent.id = ConstParentIdArray;
- element_val->parent.data.p_array.array_val = const_val;
- element_val->parent.data.p_array.elem_index = i;
- }
- return;
- }
- case ConstArraySpecialBuf: {
- Buf *buf = const_val->data.x_array.data.s_buf;
- // If we're doing this it means that we are potentially modifying the data,
- // so we can't have it be in the string literals table
- g->string_literals_table.maybe_remove(buf);
-
- const_val->data.x_array.special = ConstArraySpecialNone;
- assert(elem_count == buf_len(buf));
- const_val->data.x_array.data.s_none.elements = g->pass1_arena->allocate(elem_count);
- for (size_t i = 0; i < elem_count; i += 1) {
- ZigValue *this_char = &const_val->data.x_array.data.s_none.elements[i];
- this_char->special = ConstValSpecialStatic;
- this_char->type = g->builtin_types.entry_u8;
- bigint_init_unsigned(&this_char->data.x_bigint, (uint8_t)buf_ptr(buf)[i]);
- this_char->parent.id = ConstParentIdArray;
- this_char->parent.data.p_array.array_val = const_val;
- this_char->parent.data.p_array.elem_index = i;
- }
- return;
- }
- }
- zig_unreachable();
-}
-
-static const ZigTypeId all_type_ids[] = {
- ZigTypeIdMetaType,
- ZigTypeIdVoid,
- ZigTypeIdBool,
- ZigTypeIdUnreachable,
- ZigTypeIdInt,
- ZigTypeIdFloat,
- ZigTypeIdPointer,
- ZigTypeIdArray,
- ZigTypeIdStruct,
- ZigTypeIdComptimeFloat,
- ZigTypeIdComptimeInt,
- ZigTypeIdUndefined,
- ZigTypeIdNull,
- ZigTypeIdOptional,
- ZigTypeIdErrorUnion,
- ZigTypeIdErrorSet,
- ZigTypeIdEnum,
- ZigTypeIdUnion,
- ZigTypeIdFn,
- ZigTypeIdBoundFn,
- ZigTypeIdOpaque,
- ZigTypeIdFnFrame,
- ZigTypeIdAnyFrame,
- ZigTypeIdVector,
- ZigTypeIdEnumLiteral,
-};
-
-ZigTypeId type_id_at_index(size_t index) {
- assert(index < array_length(all_type_ids));
- return all_type_ids[index];
-}
-
-size_t type_id_len() {
- return array_length(all_type_ids);
-}
-
-size_t type_id_index(ZigType *entry) {
- switch (entry->id) {
- case ZigTypeIdInvalid:
- zig_unreachable();
- case ZigTypeIdMetaType:
- return 0;
- case ZigTypeIdVoid:
- return 1;
- case ZigTypeIdBool:
- return 2;
- case ZigTypeIdUnreachable:
- return 3;
- case ZigTypeIdInt:
- return 4;
- case ZigTypeIdFloat:
- return 5;
- case ZigTypeIdPointer:
- return 6;
- case ZigTypeIdArray:
- return 7;
- case ZigTypeIdStruct:
- if (entry->data.structure.special == StructSpecialSlice)
- return 6;
- return 8;
- case ZigTypeIdComptimeFloat:
- return 9;
- case ZigTypeIdComptimeInt:
- return 10;
- case ZigTypeIdUndefined:
- return 11;
- case ZigTypeIdNull:
- return 12;
- case ZigTypeIdOptional:
- return 13;
- case ZigTypeIdErrorUnion:
- return 14;
- case ZigTypeIdErrorSet:
- return 15;
- case ZigTypeIdEnum:
- return 16;
- case ZigTypeIdUnion:
- return 17;
- case ZigTypeIdFn:
- return 18;
- case ZigTypeIdBoundFn:
- return 19;
- case ZigTypeIdOpaque:
- return 20;
- case ZigTypeIdFnFrame:
- return 21;
- case ZigTypeIdAnyFrame:
- return 22;
- case ZigTypeIdVector:
- return 23;
- case ZigTypeIdEnumLiteral:
- return 24;
- }
- zig_unreachable();
-}
-
-const char *type_id_name(ZigTypeId id) {
- switch (id) {
- case ZigTypeIdInvalid:
- zig_unreachable();
- case ZigTypeIdMetaType:
- return "Type";
- case ZigTypeIdVoid:
- return "Void";
- case ZigTypeIdBool:
- return "Bool";
- case ZigTypeIdUnreachable:
- return "NoReturn";
- case ZigTypeIdInt:
- return "Int";
- case ZigTypeIdFloat:
- return "Float";
- case ZigTypeIdPointer:
- return "Pointer";
- case ZigTypeIdArray:
- return "Array";
- case ZigTypeIdStruct:
- return "Struct";
- case ZigTypeIdComptimeFloat:
- return "ComptimeFloat";
- case ZigTypeIdComptimeInt:
- return "ComptimeInt";
- case ZigTypeIdEnumLiteral:
- return "EnumLiteral";
- case ZigTypeIdUndefined:
- return "Undefined";
- case ZigTypeIdNull:
- return "Null";
- case ZigTypeIdOptional:
- return "Optional";
- case ZigTypeIdErrorUnion:
- return "ErrorUnion";
- case ZigTypeIdErrorSet:
- return "ErrorSet";
- case ZigTypeIdEnum:
- return "Enum";
- case ZigTypeIdUnion:
- return "Union";
- case ZigTypeIdFn:
- return "Fn";
- case ZigTypeIdBoundFn:
- return "BoundFn";
- case ZigTypeIdOpaque:
- return "Opaque";
- case ZigTypeIdVector:
- return "Vector";
- case ZigTypeIdFnFrame:
- return "Frame";
- case ZigTypeIdAnyFrame:
- return "AnyFrame";
- }
- zig_unreachable();
-}
-
-ZigType *get_align_amt_type(CodeGen *g) {
- if (g->align_amt_type == nullptr) {
- // according to LLVM the maximum alignment is 1 << 29.
- g->align_amt_type = get_int_type(g, false, 29);
- }
- return g->align_amt_type;
-}
-
-uint32_t type_ptr_hash(const ZigType *ptr) {
- return hash_ptr((void*)ptr);
-}
-
-bool type_ptr_eql(const ZigType *a, const ZigType *b) {
- return a == b;
-}
-
-uint32_t pkg_ptr_hash(const ZigPackage *ptr) {
- return hash_ptr((void*)ptr);
-}
-
-bool pkg_ptr_eql(const ZigPackage *a, const ZigPackage *b) {
- return a == b;
-}
-
-uint32_t tld_ptr_hash(const Tld *ptr) {
- return hash_ptr((void*)ptr);
-}
-
-bool tld_ptr_eql(const Tld *a, const Tld *b) {
- return a == b;
-}
-
-uint32_t node_ptr_hash(const AstNode *ptr) {
- return hash_ptr((void*)ptr);
-}
-
-bool node_ptr_eql(const AstNode *a, const AstNode *b) {
- return a == b;
-}
-
-uint32_t fn_ptr_hash(const ZigFn *ptr) {
- return hash_ptr((void*)ptr);
-}
-
-bool fn_ptr_eql(const ZigFn *a, const ZigFn *b) {
- return a == b;
-}
-
-uint32_t err_ptr_hash(const ErrorTableEntry *ptr) {
- return hash_ptr((void*)ptr);
-}
-
-bool err_ptr_eql(const ErrorTableEntry *a, const ErrorTableEntry *b) {
- return a == b;
-}
-
-ZigValue *get_builtin_value(CodeGen *codegen, const char *name) {
- ScopeDecls *builtin_scope = get_container_scope(codegen->compile_var_import);
- Tld *tld = find_container_decl(codegen, builtin_scope, buf_create_from_str(name));
- assert(tld != nullptr);
- resolve_top_level_decl(codegen, tld, nullptr, false);
- assert(tld->id == TldIdVar && tld->resolution == TldResolutionOk);
- TldVar *tld_var = (TldVar *)tld;
- ZigValue *var_value = tld_var->var->const_value;
- assert(var_value != nullptr);
- return var_value;
-}
-
-ZigType *get_builtin_type(CodeGen *codegen, const char *name) {
- ZigValue *type_val = get_builtin_value(codegen, name);
- assert(type_val->type->id == ZigTypeIdMetaType);
- return type_val->data.x_type;
-}
-
-bool type_is_global_error_set(ZigType *err_set_type) {
- assert(err_set_type->id == ZigTypeIdErrorSet);
- assert(!err_set_type->data.error_set.incomplete);
- return err_set_type->data.error_set.err_count == UINT32_MAX;
-}
-
-bool type_can_fail(ZigType *type_entry) {
- return type_entry->id == ZigTypeIdErrorUnion || type_entry->id == ZigTypeIdErrorSet;
-}
-
-bool fn_type_can_fail(FnTypeId *fn_type_id) {
- return type_can_fail(fn_type_id->return_type);
-}
-
-// ErrorNone - result pointer has the type
-// ErrorOverflow - an integer primitive type has too large a bit width
-// ErrorPrimitiveTypeNotFound - result pointer unchanged
-Error get_primitive_type(CodeGen *g, Buf *name, ZigType **result) {
- if (buf_len(name) >= 2) {
- uint8_t first_c = buf_ptr(name)[0];
- if (first_c == 'i' || first_c == 'u') {
- for (size_t i = 1; i < buf_len(name); i += 1) {
- uint8_t c = buf_ptr(name)[i];
- if (c < '0' || c > '9') {
- goto not_integer;
- }
- }
- bool is_signed = (first_c == 'i');
- unsigned long int bit_count = strtoul(buf_ptr(name) + 1, nullptr, 10);
- // strtoul returns ULONG_MAX on errors, so this comparison catches that as well.
- if (bit_count >= 65536) return ErrorOverflow;
- *result = get_int_type(g, is_signed, bit_count);
- return ErrorNone;
- }
- }
-
-not_integer:
-
- auto primitive_table_entry = g->primitive_type_table.maybe_get(name);
- if (primitive_table_entry == nullptr)
- return ErrorPrimitiveTypeNotFound;
-
- *result = primitive_table_entry->value;
- return ErrorNone;
-}
-
-Error file_fetch(CodeGen *g, Buf *resolved_path, Buf *contents_buf) {
- size_t len;
- const char *contents = stage2_fetch_file(&g->stage1, buf_ptr(resolved_path), buf_len(resolved_path), &len);
- if (contents == nullptr)
- return ErrorFileNotFound;
- buf_init_from_mem(contents_buf, contents, len);
- return ErrorNone;
-}
-
-static X64CABIClass type_windows_abi_x86_64_class(CodeGen *g, ZigType *ty, size_t ty_size) {
- // https://docs.microsoft.com/en-gb/cpp/build/x64-calling-convention?view=vs-2017
- switch (ty->id) {
- case ZigTypeIdEnum:
- case ZigTypeIdInt:
- case ZigTypeIdBool:
- return X64CABIClass_INTEGER;
- case ZigTypeIdFloat:
- case ZigTypeIdVector:
- return X64CABIClass_SSE;
- case ZigTypeIdStruct:
- case ZigTypeIdUnion: {
- if (ty_size <= 8)
- return X64CABIClass_INTEGER;
- return X64CABIClass_MEMORY;
- }
- default:
- return X64CABIClass_Unknown;
- }
-}
-
-static X64CABIClass type_system_V_abi_x86_64_class(CodeGen *g, ZigType *ty, size_t ty_size) {
- switch (ty->id) {
- case ZigTypeIdEnum:
- case ZigTypeIdInt:
- case ZigTypeIdBool:
- return X64CABIClass_INTEGER;
- case ZigTypeIdFloat:
- case ZigTypeIdVector:
- return X64CABIClass_SSE;
- case ZigTypeIdStruct: {
- // "If the size of an object is larger than four eightbytes, or it contains unaligned
- // fields, it has class MEMORY"
- if (ty_size > 32)
- return X64CABIClass_MEMORY;
- if (ty->data.structure.layout != ContainerLayoutExtern) {
- // TODO determine whether packed structs have any unaligned fields
- return X64CABIClass_Unknown;
- }
- // "If the size of the aggregate exceeds two eightbytes and the first eight-
- // byte isn’t SSE or any other eightbyte isn’t SSEUP, the whole argument
- // is passed in memory."
- if (ty_size > 16) {
- // Zig doesn't support vectors and large fp registers yet, so this will always
- // be memory.
- return X64CABIClass_MEMORY;
- }
- X64CABIClass working_class = X64CABIClass_Unknown;
- for (uint32_t i = 0; i < ty->data.structure.src_field_count; i += 1) {
- X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.structure.fields[0]->type_entry);
- if (field_class == X64CABIClass_Unknown)
- return X64CABIClass_Unknown;
- if (i == 0 || field_class == X64CABIClass_MEMORY || working_class == X64CABIClass_SSE) {
- working_class = field_class;
- }
- }
- return working_class;
- }
- case ZigTypeIdUnion: {
- // "If the size of an object is larger than four eightbytes, or it contains unaligned
- // fields, it has class MEMORY"
- if (ty_size > 32)
- return X64CABIClass_MEMORY;
- if (ty->data.unionation.layout != ContainerLayoutExtern)
- return X64CABIClass_MEMORY;
- // "If the size of the aggregate exceeds two eightbytes and the first eight-
- // byte isn’t SSE or any other eightbyte isn’t SSEUP, the whole argument
- // is passed in memory."
- if (ty_size > 16) {
- // Zig doesn't support vectors and large fp registers yet, so this will always
- // be memory.
- return X64CABIClass_MEMORY;
- }
- X64CABIClass working_class = X64CABIClass_Unknown;
- for (uint32_t i = 0; i < ty->data.unionation.src_field_count; i += 1) {
- X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.unionation.fields->type_entry);
- if (field_class == X64CABIClass_Unknown)
- return X64CABIClass_Unknown;
- if (i == 0 || field_class == X64CABIClass_MEMORY || working_class == X64CABIClass_SSE) {
- working_class = field_class;
- }
- }
- return working_class;
- }
- default:
- return X64CABIClass_Unknown;
- }
-}
-
-X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty) {
- Error err;
-
- const size_t ty_size = type_size(g, ty);
- ZigType *ptr_type;
- if ((err = get_codegen_ptr_type(g, ty, &ptr_type))) return X64CABIClass_Unknown;
- if (ptr_type != nullptr)
- return X64CABIClass_INTEGER;
-
- if (g->zig_target->os == OsWindows || g->zig_target->os == OsUefi) {
- return type_windows_abi_x86_64_class(g, ty, ty_size);
- } else if (g->zig_target->arch == ZigLLVM_aarch64 ||
- g->zig_target->arch == ZigLLVM_aarch64_be)
- {
- X64CABIClass result = type_system_V_abi_x86_64_class(g, ty, ty_size);
- return (result == X64CABIClass_MEMORY) ? X64CABIClass_MEMORY_nobyval : result;
- } else {
- return type_system_V_abi_x86_64_class(g, ty, ty_size);
- }
-}
-
-// NOTE this does not depend on x86_64
-Error type_is_c_abi_int(CodeGen *g, ZigType *ty, bool *result) {
- if (ty->id == ZigTypeIdInt ||
- ty->id == ZigTypeIdFloat ||
- ty->id == ZigTypeIdBool ||
- ty->id == ZigTypeIdEnum ||
- ty->id == ZigTypeIdVoid ||
- ty->id == ZigTypeIdUnreachable)
- {
- *result = true;
- return ErrorNone;
- }
-
- Error err;
- ZigType *ptr_type;
- if ((err = get_codegen_ptr_type(g, ty, &ptr_type))) return err;
- *result = ptr_type != nullptr;
- return ErrorNone;
-}
-
-bool type_is_c_abi_int_bail(CodeGen *g, ZigType *ty) {
- Error err;
- bool result;
- if ((err = type_is_c_abi_int(g, ty, &result)))
- codegen_report_errors_and_exit(g);
-
- return result;
-}
-
-uint32_t get_host_int_bytes(CodeGen *g, ZigType *struct_type, TypeStructField *field) {
- assert(struct_type->id == ZigTypeIdStruct);
- if (struct_type->data.structure.layout != ContainerLayoutAuto) {
- assert(type_is_resolved(struct_type, ResolveStatusSizeKnown));
- }
- if (struct_type->data.structure.host_int_bytes == nullptr)
- return 0;
- return struct_type->data.structure.host_int_bytes[field->gen_index];
-}
-
-Error ensure_const_val_repr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node,
- ZigValue *const_val, ZigType *wanted_type)
-{
- ZigValue ptr_val = {};
- ptr_val.special = ConstValSpecialStatic;
- ptr_val.type = get_pointer_to_type(codegen, wanted_type, true);
- ptr_val.data.x_ptr.mut = ConstPtrMutComptimeConst;
- ptr_val.data.x_ptr.special = ConstPtrSpecialRef;
- ptr_val.data.x_ptr.data.ref.pointee = const_val;
- if (const_ptr_pointee(ira, codegen, &ptr_val, source_node) == nullptr)
- return ErrorSemanticAnalyzeFail;
-
- return ErrorNone;
-}
-
-const char *container_string(ContainerKind kind) {
- switch (kind) {
- case ContainerKindEnum: return "enum";
- case ContainerKindStruct: return "struct";
- case ContainerKindUnion: return "union";
- }
- zig_unreachable();
-}
-
-bool ptr_allows_addr_zero(ZigType *ptr_type) {
- if (ptr_type->id == ZigTypeIdPointer) {
- return ptr_type->data.pointer.allow_zero;
- } else if (ptr_type->id == ZigTypeIdOptional) {
- return true;
- }
- return false;
-}
-
-Buf *type_bare_name(ZigType *type_entry) {
- if (is_slice(type_entry)) {
- return &type_entry->name;
- } else if (is_container(type_entry)) {
- return get_container_scope(type_entry)->bare_name;
- } else if (type_entry->id == ZigTypeIdOpaque) {
- return type_entry->data.opaque.bare_name;
- } else {
- return &type_entry->name;
- }
-}
-
-// TODO this will have to be more clever, probably using the full name
-// and replacing '.' with '_' or something like that
-Buf *type_h_name(ZigType *t) {
- return type_bare_name(t);
-}
-
-static void resolve_llvm_types_slice(CodeGen *g, ZigType *type, ResolveStatus wanted_resolve_status) {
- if (type->data.structure.resolve_status >= wanted_resolve_status) return;
-
- ZigType *ptr_type = type->data.structure.fields[slice_ptr_index]->type_entry;
- ZigType *child_type = ptr_type->data.pointer.child_type;
- ZigType *usize_type = g->builtin_types.entry_usize;
-
- bool done = false;
- if (ptr_type->data.pointer.is_const || ptr_type->data.pointer.is_volatile ||
- ptr_type->data.pointer.explicit_alignment != 0 || ptr_type->data.pointer.allow_zero ||
- ptr_type->data.pointer.sentinel != nullptr)
- {
- ZigType *peer_ptr_type = get_pointer_to_type_extra(g, child_type, false, false,
- PtrLenUnknown, 0, 0, 0, false);
- ZigType *peer_slice_type = get_slice_type(g, peer_ptr_type);
-
- assertNoError(type_resolve(g, peer_slice_type, wanted_resolve_status));
- type->llvm_type = peer_slice_type->llvm_type;
- type->llvm_di_type = peer_slice_type->llvm_di_type;
- type->data.structure.resolve_status = peer_slice_type->data.structure.resolve_status;
- done = true;
- }
-
- // If the child type is []const T then we need to make sure the type ref
- // and debug info is the same as if the child type were []T.
- if (is_slice(child_type)) {
- ZigType *child_ptr_type = child_type->data.structure.fields[slice_ptr_index]->type_entry;
- assert(child_ptr_type->id == ZigTypeIdPointer);
- if (child_ptr_type->data.pointer.is_const || child_ptr_type->data.pointer.is_volatile ||
- child_ptr_type->data.pointer.explicit_alignment != 0 || child_ptr_type->data.pointer.allow_zero ||
- child_ptr_type->data.pointer.sentinel != nullptr)
- {
- ZigType *grand_child_type = child_ptr_type->data.pointer.child_type;
- ZigType *bland_child_ptr_type = get_pointer_to_type_extra(g, grand_child_type, false, false,
- PtrLenUnknown, 0, 0, 0, false);
- ZigType *bland_child_slice = get_slice_type(g, bland_child_ptr_type);
- ZigType *peer_ptr_type = get_pointer_to_type_extra(g, bland_child_slice, false, false,
- PtrLenUnknown, 0, 0, 0, false);
- ZigType *peer_slice_type = get_slice_type(g, peer_ptr_type);
-
- assertNoError(type_resolve(g, peer_slice_type, wanted_resolve_status));
- type->llvm_type = peer_slice_type->llvm_type;
- type->llvm_di_type = peer_slice_type->llvm_di_type;
- type->data.structure.resolve_status = peer_slice_type->data.structure.resolve_status;
- done = true;
- }
- }
-
- if (done) return;
-
- LLVMTypeRef usize_llvm_type = get_llvm_type(g, usize_type);
- ZigLLVMDIType *usize_llvm_di_type = get_llvm_di_type(g, usize_type);
- ZigLLVMDIScope *compile_unit_scope = ZigLLVMCompileUnitToScope(g->compile_unit);
- ZigLLVMDIFile *di_file = nullptr;
- unsigned line = 0;
-
- if (type->data.structure.resolve_status < ResolveStatusLLVMFwdDecl) {
- type->llvm_type = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(&type->name));
-
- type->llvm_di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
- ZigLLVMTag_DW_structure_type(), buf_ptr(&type->name),
- compile_unit_scope, di_file, line);
-
- type->data.structure.resolve_status = ResolveStatusLLVMFwdDecl;
- if (ResolveStatusLLVMFwdDecl >= wanted_resolve_status) return;
- }
-
- if (!type_has_bits(g, child_type)) {
- LLVMTypeRef element_types[] = {
- usize_llvm_type,
- };
- LLVMStructSetBody(type->llvm_type, element_types, 1, false);
-
- uint64_t len_debug_size_in_bits = usize_type->size_in_bits;
- uint64_t len_debug_align_in_bits = 8*usize_type->abi_align;
- uint64_t len_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, 0);
-
- uint64_t debug_size_in_bits = type->size_in_bits;
- uint64_t debug_align_in_bits = 8*type->abi_align;
-
- ZigLLVMDIType *di_element_types[] = {
- ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(type->llvm_di_type),
- "len", di_file, line,
- len_debug_size_in_bits,
- len_debug_align_in_bits,
- len_offset_in_bits,
- ZigLLVM_DIFlags_Zero,
- usize_llvm_di_type),
- };
- ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
- compile_unit_scope,
- buf_ptr(&type->name),
- di_file, line, debug_size_in_bits, debug_align_in_bits,
- ZigLLVM_DIFlags_Zero,
- nullptr, di_element_types, 1, 0, nullptr, "");
-
- ZigLLVMReplaceTemporary(g->dbuilder, type->llvm_di_type, replacement_di_type);
- type->llvm_di_type = replacement_di_type;
- type->data.structure.resolve_status = ResolveStatusLLVMFull;
- return;
- }
-
- LLVMTypeRef element_types[2];
- element_types[slice_ptr_index] = get_llvm_type(g, ptr_type);
- element_types[slice_len_index] = get_llvm_type(g, g->builtin_types.entry_usize);
- if (type->data.structure.resolve_status >= wanted_resolve_status) return;
- LLVMStructSetBody(type->llvm_type, element_types, 2, false);
-
- uint64_t ptr_debug_size_in_bits = ptr_type->size_in_bits;
- uint64_t ptr_debug_align_in_bits = 8*ptr_type->abi_align;
- uint64_t ptr_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, 0);
-
- uint64_t len_debug_size_in_bits = usize_type->size_in_bits;
- uint64_t len_debug_align_in_bits = 8*usize_type->abi_align;
- uint64_t len_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, 1);
-
- uint64_t debug_size_in_bits = type->size_in_bits;
- uint64_t debug_align_in_bits = 8*type->abi_align;
-
- ZigLLVMDIType *di_element_types[] = {
- ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(type->llvm_di_type),
- "ptr", di_file, line,
- ptr_debug_size_in_bits,
- ptr_debug_align_in_bits,
- ptr_offset_in_bits,
- ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, ptr_type)),
- ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(type->llvm_di_type),
- "len", di_file, line,
- len_debug_size_in_bits,
- len_debug_align_in_bits,
- len_offset_in_bits,
- ZigLLVM_DIFlags_Zero, usize_llvm_di_type),
- };
- ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
- compile_unit_scope,
- buf_ptr(&type->name),
- di_file, line, debug_size_in_bits, debug_align_in_bits,
- ZigLLVM_DIFlags_Zero,
- nullptr, di_element_types, 2, 0, nullptr, "");
-
- ZigLLVMReplaceTemporary(g->dbuilder, type->llvm_di_type, replacement_di_type);
- type->llvm_di_type = replacement_di_type;
- type->data.structure.resolve_status = ResolveStatusLLVMFull;
-}
-
-static LLVMTypeRef get_llvm_type_of_n_bytes(unsigned byte_size) {
- return byte_size == 1 ?
- LLVMInt8Type() : LLVMArrayType(LLVMInt8Type(), byte_size);
-}
-
-static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveStatus wanted_resolve_status,
- ZigType *async_frame_type)
-{
- assert(struct_type->id == ZigTypeIdStruct);
- assert(struct_type->data.structure.resolve_status != ResolveStatusInvalid);
- assert(struct_type->data.structure.resolve_status >= ResolveStatusSizeKnown);
- assert(struct_type->data.structure.fields || struct_type->data.structure.src_field_count == 0);
- if (struct_type->data.structure.resolve_status >= wanted_resolve_status) return;
-
- AstNode *decl_node = struct_type->data.structure.decl_node;
- ZigLLVMDIFile *di_file;
- ZigLLVMDIScope *di_scope;
- unsigned line;
- if (decl_node != nullptr) {
- Scope *scope = &struct_type->data.structure.decls_scope->base;
- ZigType *import = get_scope_import(scope);
- di_file = import->data.structure.root_struct->di_file;
- di_scope = ZigLLVMFileToScope(di_file);
- line = decl_node->line + 1;
- } else {
- di_file = nullptr;
- di_scope = ZigLLVMCompileUnitToScope(g->compile_unit);
- line = 0;
- }
-
- if (struct_type->data.structure.resolve_status < ResolveStatusLLVMFwdDecl) {
- struct_type->llvm_type = type_has_bits(g, struct_type) ?
- LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(&struct_type->name)) : LLVMVoidType();
- unsigned dwarf_kind = ZigLLVMTag_DW_structure_type();
- struct_type->llvm_di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
- dwarf_kind, buf_ptr(&struct_type->name),
- di_scope, di_file, line);
-
- struct_type->data.structure.resolve_status = ResolveStatusLLVMFwdDecl;
- if (ResolveStatusLLVMFwdDecl >= wanted_resolve_status) {
- struct_type->data.structure.llvm_full_type_queue_index = g->type_resolve_stack.length;
- g->type_resolve_stack.append(struct_type);
- return;
- } else {
- struct_type->data.structure.llvm_full_type_queue_index = SIZE_MAX;
- }
- }
-
- size_t field_count = struct_type->data.structure.src_field_count;
- // Every field could potentially have a generated padding field after it.
- LLVMTypeRef *element_types = heap::c_allocator.allocate(field_count * 2);
-
- bool packed = (struct_type->data.structure.layout == ContainerLayoutPacked);
- size_t packed_bits_offset = 0;
- size_t first_packed_bits_offset_misalign = SIZE_MAX;
- size_t debug_field_count = 0;
-
- // trigger all the recursive get_llvm_type calls
- for (size_t i = 0; i < field_count; i += 1) {
- TypeStructField *field = struct_type->data.structure.fields[i];
- ZigType *field_type = field->type_entry;
- if (!type_has_bits(g, field_type))
- continue;
- (void)get_llvm_type(g, field_type);
- if (struct_type->data.structure.resolve_status >= wanted_resolve_status) return;
- }
-
- size_t gen_field_index = 0;
-
- // Calculate what LLVM thinks the ABI align of the struct will be. We do this to avoid
- // inserting padding bytes where LLVM would do it automatically.
- size_t llvm_struct_abi_align = 0;
- for (size_t i = 0; i < field_count; i += 1) {
- TypeStructField *field = struct_type->data.structure.fields[i];
- ZigType *field_type = field->type_entry;
- if (field->is_comptime || !type_has_bits(g, field_type))
- continue;
- LLVMTypeRef field_llvm_type = get_llvm_type(g, field_type);
- size_t llvm_field_abi_align = LLVMABIAlignmentOfType(g->target_data_ref, field_llvm_type);
- llvm_struct_abi_align = max(llvm_struct_abi_align, llvm_field_abi_align);
- }
-
- for (size_t i = 0; i < field_count; i += 1) {
- TypeStructField *field = struct_type->data.structure.fields[i];
- ZigType *field_type = field->type_entry;
-
- if (field->is_comptime || !type_has_bits(g, field_type)) {
- field->gen_index = SIZE_MAX;
- continue;
- }
-
- if (packed) {
- size_t field_size_in_bits = type_size_bits(g, field_type);
- size_t next_packed_bits_offset = packed_bits_offset + field_size_in_bits;
-
- if (first_packed_bits_offset_misalign != SIZE_MAX) {
- // this field is not byte-aligned; it is part of the previous field with a bit offset
-
- size_t full_bit_count = next_packed_bits_offset - first_packed_bits_offset_misalign;
- size_t full_abi_size = get_abi_size_bytes(full_bit_count, g->pointer_size_bytes);
- if (full_abi_size * 8 == full_bit_count) {
- // next field recovers ABI alignment
- element_types[gen_field_index] = get_llvm_type_of_n_bytes(full_abi_size);
- gen_field_index += 1;
- first_packed_bits_offset_misalign = SIZE_MAX;
- }
- } else if (get_abi_size_bytes(field_type->size_in_bits, g->pointer_size_bytes) * 8 != field_size_in_bits) {
- first_packed_bits_offset_misalign = packed_bits_offset;
- } else {
- // This is a byte-aligned field (both start and end) in a packed struct.
- element_types[gen_field_index] = get_llvm_type(g, field_type);
- assert(get_abi_size_bytes(field_type->size_in_bits, g->pointer_size_bytes) ==
- LLVMStoreSizeOfType(g->target_data_ref, element_types[gen_field_index]));
- gen_field_index += 1;
- }
- packed_bits_offset = next_packed_bits_offset;
- } else {
- LLVMTypeRef llvm_type;
- if (i == 0 && async_frame_type != nullptr) {
- assert(async_frame_type->id == ZigTypeIdFnFrame);
- assert(field_type->id == ZigTypeIdFn);
- resolve_llvm_types_fn(g, async_frame_type->data.frame.fn);
- llvm_type = LLVMPointerType(async_frame_type->data.frame.fn->raw_type_ref, 0);
- } else {
- llvm_type = get_llvm_type(g, field_type);
- }
- element_types[gen_field_index] = llvm_type;
- field->gen_index = gen_field_index;
- gen_field_index += 1;
-
- // find the next non-zero-byte field for offset calculations
- size_t next_src_field_index = i + 1;
- for (; next_src_field_index < field_count; next_src_field_index += 1) {
- if (type_has_bits(g, struct_type->data.structure.fields[next_src_field_index]->type_entry))
- break;
- }
- size_t next_abi_align;
- if (next_src_field_index == field_count) {
- next_abi_align = struct_type->abi_align;
- } else {
- if (struct_type->data.structure.fields[next_src_field_index]->align == 0) {
- next_abi_align = struct_type->data.structure.fields[next_src_field_index]->type_entry->abi_align;
- } else {
- next_abi_align = struct_type->data.structure.fields[next_src_field_index]->align;
- }
- }
- size_t llvm_next_abi_align = (next_src_field_index == field_count) ?
- llvm_struct_abi_align :
- LLVMABIAlignmentOfType(g->target_data_ref,
- get_llvm_type(g, struct_type->data.structure.fields[next_src_field_index]->type_entry));
-
- size_t next_offset = next_field_offset(field->offset, struct_type->abi_align,
- field_type->abi_size, next_abi_align);
- size_t llvm_next_offset = next_field_offset(field->offset, llvm_struct_abi_align,
- LLVMABISizeOfType(g->target_data_ref, llvm_type), llvm_next_abi_align);
-
- assert(next_offset >= llvm_next_offset);
- if (next_offset > llvm_next_offset) {
- size_t pad_bytes = next_offset - (field->offset + LLVMStoreSizeOfType(g->target_data_ref, llvm_type));
- if (pad_bytes != 0) {
- LLVMTypeRef pad_llvm_type = LLVMArrayType(LLVMInt8Type(), pad_bytes);
- element_types[gen_field_index] = pad_llvm_type;
- gen_field_index += 1;
- }
- }
- }
- debug_field_count += 1;
- }
- if (!packed) {
- struct_type->data.structure.gen_field_count = gen_field_index;
- }
-
- if (first_packed_bits_offset_misalign != SIZE_MAX) {
- size_t full_bit_count = packed_bits_offset - first_packed_bits_offset_misalign;
- size_t full_abi_size = get_abi_size_bytes(full_bit_count, g->pointer_size_bytes);
- element_types[gen_field_index] = get_llvm_type_of_n_bytes(full_abi_size);
- gen_field_index += 1;
- }
-
- if (type_has_bits(g, struct_type)) {
- assert(struct_type->data.structure.gen_field_count == gen_field_index);
- LLVMStructSetBody(struct_type->llvm_type, element_types,
- (unsigned)struct_type->data.structure.gen_field_count, packed);
- }
-
- ZigLLVMDIType **di_element_types = heap::c_allocator.allocate(debug_field_count);
- size_t debug_field_index = 0;
- for (size_t i = 0; i < field_count; i += 1) {
- TypeStructField *field = struct_type->data.structure.fields[i];
- //fprintf(stderr, "%s at gen index %zu\n", buf_ptr(field->name), field->gen_index);
-
- size_t gen_field_index = field->gen_index;
- if (gen_field_index == SIZE_MAX) {
- continue;
- }
-
- ZigType *field_type = field->type_entry;
-
- // if the field is a function, actually the debug info should be a pointer.
- ZigLLVMDIType *field_di_type;
- if (field_type->id == ZigTypeIdFn) {
- ZigType *field_ptr_type = get_pointer_to_type(g, field_type, true);
- uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, get_llvm_type(g, field_ptr_type));
- uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, get_llvm_type(g, field_ptr_type));
- field_di_type = ZigLLVMCreateDebugPointerType(g->dbuilder, get_llvm_di_type(g, field_type),
- debug_size_in_bits, debug_align_in_bits, buf_ptr(&field_ptr_type->name));
- } else {
- field_di_type = get_llvm_di_type(g, field_type);
- }
-
- uint64_t debug_size_in_bits;
- uint64_t debug_align_in_bits;
- uint64_t debug_offset_in_bits;
- if (packed) {
- debug_size_in_bits = field->type_entry->size_in_bits;
- debug_align_in_bits = 8 * field->type_entry->abi_align;
- debug_offset_in_bits = 8 * field->offset + field->bit_offset_in_host;
- } else {
- debug_size_in_bits = 8 * get_store_size_bytes(field_type->size_in_bits);
- debug_align_in_bits = 8 * field_type->abi_align;
- debug_offset_in_bits = 8 * field->offset;
- }
- unsigned line;
- if (decl_node != nullptr) {
- AstNode *field_node = field->decl_node;
- line = field_node->line + 1;
- } else {
- line = 0;
- }
- di_element_types[debug_field_index] = ZigLLVMCreateDebugMemberType(g->dbuilder,
- ZigLLVMTypeToScope(struct_type->llvm_di_type), buf_ptr(field->name),
- di_file, line,
- debug_size_in_bits,
- debug_align_in_bits,
- debug_offset_in_bits,
- ZigLLVM_DIFlags_Zero, field_di_type);
- assert(di_element_types[debug_field_index]);
- debug_field_index += 1;
- }
-
- uint64_t debug_size_in_bits = 8*get_store_size_bytes(struct_type->size_in_bits);
- uint64_t debug_align_in_bits = 8*struct_type->abi_align;
- ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
- di_scope,
- buf_ptr(&struct_type->name),
- di_file, line,
- debug_size_in_bits,
- debug_align_in_bits,
- ZigLLVM_DIFlags_Zero,
- nullptr, di_element_types, (int)debug_field_count, 0, nullptr, "");
-
- ZigLLVMReplaceTemporary(g->dbuilder, struct_type->llvm_di_type, replacement_di_type);
- struct_type->llvm_di_type = replacement_di_type;
- struct_type->data.structure.resolve_status = ResolveStatusLLVMFull;
- if (struct_type->data.structure.llvm_full_type_queue_index != SIZE_MAX) {
- ZigType *last = g->type_resolve_stack.last();
- assert(last->id == ZigTypeIdStruct);
- last->data.structure.llvm_full_type_queue_index = struct_type->data.structure.llvm_full_type_queue_index;
- g->type_resolve_stack.swap_remove(struct_type->data.structure.llvm_full_type_queue_index);
- struct_type->data.structure.llvm_full_type_queue_index = SIZE_MAX;
- }
-}
-
-// This is to be used instead of void for debug info types, to avoid tripping
-// Assertion `!isa(Scope) && "shouldn't make a namespace scope for a type"'
-// when targeting CodeView (Windows).
-static ZigLLVMDIType *make_empty_namespace_llvm_di_type(CodeGen *g, ZigType *import, const char *name,
- AstNode *decl_node)
-{
- uint64_t debug_size_in_bits = 0;
- uint64_t debug_align_in_bits = 0;
- ZigLLVMDIType **di_element_types = nullptr;
- size_t debug_field_count = 0;
- return ZigLLVMCreateDebugStructType(g->dbuilder,
- ZigLLVMFileToScope(import->data.structure.root_struct->di_file),
- name,
- import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1),
- debug_size_in_bits,
- debug_align_in_bits,
- ZigLLVM_DIFlags_Zero,
- nullptr, di_element_types, (int)debug_field_count, 0, nullptr, "");
-}
-
-static void resolve_llvm_types_enum(CodeGen *g, ZigType *enum_type, ResolveStatus wanted_resolve_status) {
- assert(enum_type->data.enumeration.resolve_status >= ResolveStatusSizeKnown);
- if (enum_type->data.enumeration.resolve_status >= wanted_resolve_status) return;
-
- Scope *scope = &enum_type->data.enumeration.decls_scope->base;
- ZigType *import = get_scope_import(scope);
- AstNode *decl_node = enum_type->data.enumeration.decl_node;
-
- if (!type_has_bits(g, enum_type)) {
- enum_type->llvm_type = g->builtin_types.entry_void->llvm_type;
- enum_type->llvm_di_type = make_empty_namespace_llvm_di_type(g, import, buf_ptr(&enum_type->name),
- decl_node);
- enum_type->data.enumeration.resolve_status = ResolveStatusLLVMFull;
- return;
- }
-
- uint32_t field_count = enum_type->data.enumeration.src_field_count;
-
- assert(field_count == 0 || enum_type->data.enumeration.fields != nullptr);
- ZigLLVMDIEnumerator **di_enumerators = heap::c_allocator.allocate(field_count);
-
- for (uint32_t i = 0; i < field_count; i += 1) {
- TypeEnumField *enum_field = &enum_type->data.enumeration.fields[i];
-
- // TODO send patch to LLVM to support APInt in createEnumerator instead of int64_t
- // http://lists.llvm.org/pipermail/llvm-dev/2017-December/119456.html
- di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(enum_field->name),
- bigint_as_signed(&enum_field->value));
- }
-
- ZigType *tag_int_type = enum_type->data.enumeration.tag_int_type;
- enum_type->llvm_type = get_llvm_type(g, tag_int_type);
-
- // create debug type for tag
- uint64_t tag_debug_size_in_bits = 8*tag_int_type->abi_size;
- uint64_t tag_debug_align_in_bits = 8*tag_int_type->abi_align;
- ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder,
- ZigLLVMFileToScope(import->data.structure.root_struct->di_file), buf_ptr(&enum_type->name),
- import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1),
- tag_debug_size_in_bits,
- tag_debug_align_in_bits,
- di_enumerators, field_count,
- get_llvm_di_type(g, tag_int_type), "");
-
- enum_type->llvm_di_type = tag_di_type;
- enum_type->data.enumeration.resolve_status = ResolveStatusLLVMFull;
-}
-
-static void resolve_llvm_types_union(CodeGen *g, ZigType *union_type, ResolveStatus wanted_resolve_status) {
- if (union_type->data.unionation.resolve_status >= wanted_resolve_status) return;
-
- bool packed = (union_type->data.unionation.layout == ContainerLayoutPacked);
- Scope *scope = &union_type->data.unionation.decls_scope->base;
- ZigType *import = get_scope_import(scope);
-
- TypeUnionField *most_aligned_union_member = union_type->data.unionation.most_aligned_union_member;
- ZigType *tag_type = union_type->data.unionation.tag_type;
- uint32_t gen_field_count = union_type->data.unionation.gen_field_count;
- if (gen_field_count == 0) {
- if (tag_type == nullptr) {
- union_type->llvm_type = g->builtin_types.entry_void->llvm_type;
- union_type->llvm_di_type = make_empty_namespace_llvm_di_type(g, import, buf_ptr(&union_type->name),
- union_type->data.unionation.decl_node);
- } else {
- union_type->llvm_type = get_llvm_type(g, tag_type);
- union_type->llvm_di_type = get_llvm_di_type(g, tag_type);
- }
- union_type->data.unionation.resolve_status = ResolveStatusLLVMFull;
- return;
- }
-
- AstNode *decl_node = union_type->data.unionation.decl_node;
-
- if (union_type->data.unionation.resolve_status < ResolveStatusLLVMFwdDecl) {
- union_type->llvm_type = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(&union_type->name));
- size_t line = decl_node ? decl_node->line : 0;
- unsigned dwarf_kind = ZigLLVMTag_DW_structure_type();
- union_type->llvm_di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
- dwarf_kind, buf_ptr(&union_type->name),
- ZigLLVMFileToScope(import->data.structure.root_struct->di_file),
- import->data.structure.root_struct->di_file, (unsigned)(line + 1));
-
- union_type->data.unionation.resolve_status = ResolveStatusLLVMFwdDecl;
- if (ResolveStatusLLVMFwdDecl >= wanted_resolve_status) return;
- }
-
- ZigLLVMDIType **union_inner_di_types = heap::c_allocator.allocate(gen_field_count);
- uint32_t field_count = union_type->data.unionation.src_field_count;
- for (uint32_t i = 0; i < field_count; i += 1) {
- TypeUnionField *union_field = &union_type->data.unionation.fields[i];
- if (!type_has_bits(g, union_field->type_entry))
- continue;
-
- ZigLLVMDIType *field_di_type = get_llvm_di_type(g, union_field->type_entry);
- if (union_type->data.unionation.resolve_status >= wanted_resolve_status) return;
-
- uint64_t store_size_in_bits = union_field->type_entry->size_in_bits;
- uint64_t abi_align_in_bits = 8*union_field->type_entry->abi_align;
- AstNode *field_node = union_field->decl_node;
- union_inner_di_types[union_field->gen_index] = ZigLLVMCreateDebugMemberType(g->dbuilder,
- ZigLLVMTypeToScope(union_type->llvm_di_type), buf_ptr(union_field->enum_field->name),
- import->data.structure.root_struct->di_file, (unsigned)(field_node->line + 1),
- store_size_in_bits,
- abi_align_in_bits,
- 0,
- ZigLLVM_DIFlags_Zero, field_di_type);
-
- }
-
- if (tag_type == nullptr || !type_has_bits(g, tag_type)) {
- assert(most_aligned_union_member != nullptr);
-
- size_t padding_bytes = union_type->data.unionation.union_abi_size - most_aligned_union_member->type_entry->abi_size;
- if (padding_bytes > 0) {
- ZigType *u8_type = get_int_type(g, false, 8);
- ZigType *padding_array = get_array_type(g, u8_type, padding_bytes, nullptr);
- LLVMTypeRef union_element_types[] = {
- most_aligned_union_member->type_entry->llvm_type,
- get_llvm_type(g, padding_array),
- };
- LLVMStructSetBody(union_type->llvm_type, union_element_types, 2, packed);
- } else {
- LLVMStructSetBody(union_type->llvm_type, &most_aligned_union_member->type_entry->llvm_type, 1, packed);
- }
- union_type->data.unionation.union_llvm_type = union_type->llvm_type;
- union_type->data.unionation.gen_tag_index = SIZE_MAX;
- union_type->data.unionation.gen_union_index = SIZE_MAX;
-
- // create debug type for union
- ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder,
- ZigLLVMFileToScope(import->data.structure.root_struct->di_file), buf_ptr(&union_type->name),
- import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1),
- union_type->data.unionation.union_abi_size * 8,
- most_aligned_union_member->align * 8,
- ZigLLVM_DIFlags_Zero, union_inner_di_types,
- gen_field_count, 0, "");
-
- ZigLLVMReplaceTemporary(g->dbuilder, union_type->llvm_di_type, replacement_di_type);
- union_type->llvm_di_type = replacement_di_type;
- union_type->data.unionation.resolve_status = ResolveStatusLLVMFull;
- return;
- }
-
- LLVMTypeRef union_type_ref;
- size_t padding_bytes = union_type->data.unionation.union_abi_size - most_aligned_union_member->type_entry->abi_size;
- if (padding_bytes == 0) {
- union_type_ref = get_llvm_type(g, most_aligned_union_member->type_entry);
- } else {
- ZigType *u8_type = get_int_type(g, false, 8);
- ZigType *padding_array = get_array_type(g, u8_type, padding_bytes, nullptr);
- LLVMTypeRef union_element_types[] = {
- get_llvm_type(g, most_aligned_union_member->type_entry),
- get_llvm_type(g, padding_array),
- };
- union_type_ref = LLVMStructType(union_element_types, 2, false);
- }
- union_type->data.unionation.union_llvm_type = union_type_ref;
-
- LLVMTypeRef root_struct_element_types[2];
- root_struct_element_types[union_type->data.unionation.gen_tag_index] = get_llvm_type(g, tag_type);
- root_struct_element_types[union_type->data.unionation.gen_union_index] = union_type_ref;
- LLVMStructSetBody(union_type->llvm_type, root_struct_element_types, 2, packed);
-
- // create debug type for union
- ZigLLVMDIType *union_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder,
- ZigLLVMTypeToScope(union_type->llvm_di_type), "AnonUnion",
- import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1),
- most_aligned_union_member->type_entry->size_in_bits, 8*most_aligned_union_member->align,
- ZigLLVM_DIFlags_Zero, union_inner_di_types, gen_field_count, 0, "");
-
- uint64_t union_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, union_type->llvm_type,
- union_type->data.unionation.gen_union_index);
- uint64_t tag_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, union_type->llvm_type,
- union_type->data.unionation.gen_tag_index);
-
- ZigLLVMDIType *union_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder,
- ZigLLVMTypeToScope(union_type->llvm_di_type), "payload",
- import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1),
- most_aligned_union_member->type_entry->size_in_bits,
- 8*most_aligned_union_member->align,
- union_offset_in_bits,
- ZigLLVM_DIFlags_Zero, union_di_type);
-
- uint64_t tag_debug_size_in_bits = tag_type->size_in_bits;
- uint64_t tag_debug_align_in_bits = 8*tag_type->abi_align;
-
- ZigLLVMDIType *tag_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder,
- ZigLLVMTypeToScope(union_type->llvm_di_type), "tag",
- import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1),
- tag_debug_size_in_bits,
- tag_debug_align_in_bits,
- tag_offset_in_bits,
- ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, tag_type));
-
- ZigLLVMDIType *di_root_members[2];
- di_root_members[union_type->data.unionation.gen_tag_index] = tag_member_di_type;
- di_root_members[union_type->data.unionation.gen_union_index] = union_member_di_type;
-
- uint64_t debug_size_in_bits = union_type->size_in_bits;
- uint64_t debug_align_in_bits = 8*union_type->abi_align;
- ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
- ZigLLVMFileToScope(import->data.structure.root_struct->di_file),
- buf_ptr(&union_type->name),
- import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1),
- debug_size_in_bits,
- debug_align_in_bits,
- ZigLLVM_DIFlags_Zero, nullptr, di_root_members, 2, 0, nullptr, "");
-
- ZigLLVMReplaceTemporary(g->dbuilder, union_type->llvm_di_type, replacement_di_type);
- union_type->llvm_di_type = replacement_di_type;
- union_type->data.unionation.resolve_status = ResolveStatusLLVMFull;
-}
-
-static void resolve_llvm_types_pointer(CodeGen *g, ZigType *type, ResolveStatus wanted_resolve_status) {
- if (type->llvm_di_type != nullptr) return;
-
- if (resolve_pointer_zero_bits(g, type) != ErrorNone)
- zig_unreachable();
-
- if (!type_has_bits(g, type)) {
- type->llvm_type = g->builtin_types.entry_void->llvm_type;
- type->llvm_di_type = g->builtin_types.entry_void->llvm_di_type;
- return;
- }
-
- ZigType *elem_type = type->data.pointer.child_type;
-
- if (type->data.pointer.is_const || type->data.pointer.is_volatile ||
- type->data.pointer.explicit_alignment != 0 || type->data.pointer.ptr_len != PtrLenSingle ||
- type->data.pointer.bit_offset_in_host != 0 || type->data.pointer.allow_zero ||
- type->data.pointer.vector_index != VECTOR_INDEX_NONE || type->data.pointer.sentinel != nullptr)
- {
- assertNoError(type_resolve(g, elem_type, ResolveStatusLLVMFwdDecl));
- ZigType *peer_type;
- if (type->data.pointer.vector_index == VECTOR_INDEX_NONE) {
- peer_type = get_pointer_to_type_extra2(g, elem_type, false, false,
- PtrLenSingle, 0, 0, type->data.pointer.host_int_bytes, false,
- VECTOR_INDEX_NONE, nullptr, nullptr);
- } else {
- uint32_t host_vec_len = type->data.pointer.host_int_bytes;
- ZigType *host_vec_type = get_vector_type(g, host_vec_len, elem_type);
- peer_type = get_pointer_to_type_extra2(g, host_vec_type, false, false,
- PtrLenSingle, 0, 0, 0, false, VECTOR_INDEX_NONE, nullptr, nullptr);
- }
- type->llvm_type = get_llvm_type(g, peer_type);
- type->llvm_di_type = get_llvm_di_type(g, peer_type);
- assertNoError(type_resolve(g, elem_type, wanted_resolve_status));
- return;
- }
-
- if (type->data.pointer.host_int_bytes == 0) {
- assertNoError(type_resolve(g, elem_type, ResolveStatusLLVMFwdDecl));
- type->llvm_type = LLVMPointerType(elem_type->llvm_type, 0);
- uint64_t debug_size_in_bits = 8*get_store_size_bytes(type->size_in_bits);
- uint64_t debug_align_in_bits = 8*type->abi_align;
- type->llvm_di_type = ZigLLVMCreateDebugPointerType(g->dbuilder, elem_type->llvm_di_type,
- debug_size_in_bits, debug_align_in_bits, buf_ptr(&type->name));
- assertNoError(type_resolve(g, elem_type, wanted_resolve_status));
- } else {
- ZigType *host_int_type = get_int_type(g, false, type->data.pointer.host_int_bytes * 8);
- LLVMTypeRef host_int_llvm_type = get_llvm_type(g, host_int_type);
- type->llvm_type = LLVMPointerType(host_int_llvm_type, 0);
- uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, host_int_llvm_type);
- uint64_t debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, host_int_llvm_type);
- type->llvm_di_type = ZigLLVMCreateDebugPointerType(g->dbuilder, get_llvm_di_type(g, host_int_type),
- debug_size_in_bits, debug_align_in_bits, buf_ptr(&type->name));
- }
-}
-
-static void resolve_llvm_types_integer(CodeGen *g, ZigType *type) {
- if (type->llvm_di_type != nullptr) return;
-
- if (!type_has_bits(g, type)) {
- type->llvm_type = g->builtin_types.entry_void->llvm_type;
- type->llvm_di_type = g->builtin_types.entry_void->llvm_di_type;
- return;
- }
-
- unsigned dwarf_tag;
- if (type->data.integral.is_signed) {
- if (type->size_in_bits == 8) {
- dwarf_tag = ZigLLVMEncoding_DW_ATE_signed_char();
- } else {
- dwarf_tag = ZigLLVMEncoding_DW_ATE_signed();
- }
- } else {
- if (type->size_in_bits == 8) {
- dwarf_tag = ZigLLVMEncoding_DW_ATE_unsigned_char();
- } else {
- dwarf_tag = ZigLLVMEncoding_DW_ATE_unsigned();
- }
- }
-
- type->llvm_di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&type->name),
- type->abi_size * 8, dwarf_tag);
- type->llvm_type = LLVMIntType(type->size_in_bits);
-}
-
-static void resolve_llvm_types_optional(CodeGen *g, ZigType *type, ResolveStatus wanted_resolve_status) {
- assert(type->id == ZigTypeIdOptional);
- assert(type->data.maybe.resolve_status != ResolveStatusInvalid);
- assert(type->data.maybe.resolve_status >= ResolveStatusSizeKnown);
- if (type->data.maybe.resolve_status >= wanted_resolve_status) return;
-
- LLVMTypeRef bool_llvm_type = get_llvm_type(g, g->builtin_types.entry_bool);
- ZigLLVMDIType *bool_llvm_di_type = get_llvm_di_type(g, g->builtin_types.entry_bool);
-
- ZigType *child_type = type->data.maybe.child_type;
- if (!type_has_bits(g, child_type)) {
- type->llvm_type = bool_llvm_type;
- type->llvm_di_type = bool_llvm_di_type;
- type->data.maybe.resolve_status = ResolveStatusLLVMFull;
- return;
- }
-
- if (type_is_nonnull_ptr(g, child_type) || child_type->id == ZigTypeIdErrorSet) {
- type->llvm_type = get_llvm_type(g, child_type);
- type->llvm_di_type = get_llvm_di_type(g, child_type);
- type->data.maybe.resolve_status = ResolveStatusLLVMFull;
- return;
- }
-
- ZigLLVMDIScope *compile_unit_scope = ZigLLVMCompileUnitToScope(g->compile_unit);
- ZigLLVMDIFile *di_file = nullptr;
- unsigned line = 0;
-
- if (type->data.maybe.resolve_status < ResolveStatusLLVMFwdDecl) {
- type->llvm_type = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(&type->name));
- unsigned dwarf_kind = ZigLLVMTag_DW_structure_type();
- type->llvm_di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
- dwarf_kind, buf_ptr(&type->name),
- compile_unit_scope, di_file, line);
-
- type->data.maybe.resolve_status = ResolveStatusLLVMFwdDecl;
- if (ResolveStatusLLVMFwdDecl >= wanted_resolve_status) return;
- }
-
- ZigLLVMDIType *child_llvm_di_type = get_llvm_di_type(g, child_type);
- if (type->data.maybe.resolve_status >= wanted_resolve_status) return;
-
- LLVMTypeRef elem_types[] = {
- get_llvm_type(g, child_type),
- LLVMInt1Type(),
- };
- LLVMStructSetBody(type->llvm_type, elem_types, 2, false);
-
- uint64_t val_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, maybe_child_index);
- uint64_t maybe_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, maybe_null_index);
-
- ZigLLVMDIType *di_element_types[2];
- di_element_types[maybe_child_index] =
- ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(type->llvm_di_type),
- "val", di_file, line,
- 8 * child_type->abi_size,
- 8 * child_type->abi_align,
- val_offset_in_bits,
- ZigLLVM_DIFlags_Zero, child_llvm_di_type);
- di_element_types[maybe_null_index] =
- ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(type->llvm_di_type),
- "maybe", di_file, line,
- 8*g->builtin_types.entry_bool->abi_size,
- 8*g->builtin_types.entry_bool->abi_align,
- maybe_offset_in_bits,
- ZigLLVM_DIFlags_Zero, bool_llvm_di_type);
- ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
- compile_unit_scope,
- buf_ptr(&type->name),
- di_file, line, 8 * type->abi_size, 8 * type->abi_align, ZigLLVM_DIFlags_Zero,
- nullptr, di_element_types, 2, 0, nullptr, "");
-
- ZigLLVMReplaceTemporary(g->dbuilder, type->llvm_di_type, replacement_di_type);
- type->llvm_di_type = replacement_di_type;
- type->data.maybe.resolve_status = ResolveStatusLLVMFull;
-}
-
-static void resolve_llvm_types_error_union(CodeGen *g, ZigType *type) {
- if (type->llvm_di_type != nullptr) return;
-
- ZigType *payload_type = type->data.error_union.payload_type;
- ZigType *err_set_type = type->data.error_union.err_set_type;
-
- if (!type_has_bits(g, payload_type)) {
- assert(type_has_bits(g, err_set_type));
- type->llvm_type = get_llvm_type(g, err_set_type);
- type->llvm_di_type = get_llvm_di_type(g, err_set_type);
- } else if (!type_has_bits(g, err_set_type)) {
- type->llvm_type = get_llvm_type(g, payload_type);
- type->llvm_di_type = get_llvm_di_type(g, payload_type);
- } else {
- LLVMTypeRef err_set_llvm_type = get_llvm_type(g, err_set_type);
- LLVMTypeRef payload_llvm_type = get_llvm_type(g, payload_type);
- LLVMTypeRef elem_types[3];
- elem_types[err_union_err_index] = err_set_llvm_type;
- elem_types[err_union_payload_index] = payload_llvm_type;
-
- type->llvm_type = LLVMStructType(elem_types, 2, false);
- if (LLVMABISizeOfType(g->target_data_ref, type->llvm_type) != type->abi_size) {
- // we need to do our own padding
- type->data.error_union.pad_llvm_type = LLVMArrayType(LLVMInt8Type(), type->data.error_union.pad_bytes);
- elem_types[2] = type->data.error_union.pad_llvm_type;
- type->llvm_type = LLVMStructType(elem_types, 3, false);
- }
-
- ZigLLVMDIScope *compile_unit_scope = ZigLLVMCompileUnitToScope(g->compile_unit);
- ZigLLVMDIFile *di_file = nullptr;
- unsigned line = 0;
- type->llvm_di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
- ZigLLVMTag_DW_structure_type(), buf_ptr(&type->name),
- compile_unit_scope, di_file, line);
-
- uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, err_set_llvm_type);
- uint64_t tag_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, err_set_llvm_type);
- uint64_t tag_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type, err_union_err_index);
-
- uint64_t value_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, payload_llvm_type);
- uint64_t value_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, payload_llvm_type);
- uint64_t value_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, type->llvm_type,
- err_union_payload_index);
-
- uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, type->llvm_type);
- uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, type->llvm_type);
-
- ZigLLVMDIType *di_element_types[2];
- di_element_types[err_union_err_index] = ZigLLVMCreateDebugMemberType(g->dbuilder,
- ZigLLVMTypeToScope(type->llvm_di_type),
- "tag", di_file, line,
- tag_debug_size_in_bits,
- tag_debug_align_in_bits,
- tag_offset_in_bits,
- ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, err_set_type));
- di_element_types[err_union_payload_index] = ZigLLVMCreateDebugMemberType(g->dbuilder,
- ZigLLVMTypeToScope(type->llvm_di_type),
- "value", di_file, line,
- value_debug_size_in_bits,
- value_debug_align_in_bits,
- value_offset_in_bits,
- ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, payload_type));
-
- ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
- compile_unit_scope,
- buf_ptr(&type->name),
- di_file, line,
- debug_size_in_bits,
- debug_align_in_bits,
- ZigLLVM_DIFlags_Zero,
- nullptr, di_element_types, 2, 0, nullptr, "");
-
- ZigLLVMReplaceTemporary(g->dbuilder, type->llvm_di_type, replacement_di_type);
- type->llvm_di_type = replacement_di_type;
- }
-}
-
-static void resolve_llvm_types_array(CodeGen *g, ZigType *type) {
- if (type->llvm_di_type != nullptr) return;
-
- if (!type_has_bits(g, type)) {
- type->llvm_type = g->builtin_types.entry_void->llvm_type;
- type->llvm_di_type = g->builtin_types.entry_void->llvm_di_type;
- return;
- }
-
- ZigType *elem_type = type->data.array.child_type;
-
- uint64_t extra_len_from_sentinel = (type->data.array.sentinel != nullptr) ? 1 : 0;
- uint64_t full_len = type->data.array.len + extra_len_from_sentinel;
- // TODO https://github.com/ziglang/zig/issues/1424
- type->llvm_type = LLVMArrayType(get_llvm_type(g, elem_type), (unsigned)full_len);
-
- uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, type->llvm_type);
- uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, type->llvm_type);
-
- type->llvm_di_type = ZigLLVMCreateDebugArrayType(g->dbuilder, debug_size_in_bits,
- debug_align_in_bits, get_llvm_di_type(g, elem_type), (int)full_len);
-}
-
-static void resolve_llvm_types_fn_type(CodeGen *g, ZigType *fn_type) {
- if (fn_type->llvm_di_type != nullptr) return;
-
- FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
- bool first_arg_return = want_first_arg_sret(g, fn_type_id);
- bool is_async = fn_type_id->cc == CallingConventionAsync;
- bool is_c_abi = !calling_convention_allows_zig_types(fn_type_id->cc);
- bool prefix_arg_error_return_trace = g->have_err_ret_tracing && fn_type_can_fail(fn_type_id);
- // +1 for maybe making the first argument the return value
- // +1 for maybe first argument the error return trace
- // +2 for maybe arguments async allocator and error code pointer
- ZigList gen_param_types = {};
- // +1 because 0 is the return type and
- // +1 for maybe making first arg ret val and
- // +1 for maybe first argument the error return trace
- // +2 for maybe arguments async allocator and error code pointer
- ZigList param_di_types = {};
- ZigType *gen_return_type;
- if (is_async) {
- gen_return_type = g->builtin_types.entry_void;
- param_di_types.append(nullptr);
- } else if (!type_has_bits(g, fn_type_id->return_type)) {
- gen_return_type = g->builtin_types.entry_void;
- param_di_types.append(nullptr);
- } else if (first_arg_return) {
- gen_return_type = g->builtin_types.entry_void;
- param_di_types.append(nullptr);
- ZigType *gen_type = get_pointer_to_type(g, fn_type_id->return_type, false);
- gen_param_types.append(get_llvm_type(g, gen_type));
- param_di_types.append(get_llvm_di_type(g, gen_type));
- } else {
- gen_return_type = fn_type_id->return_type;
- param_di_types.append(get_llvm_di_type(g, gen_return_type));
- }
- fn_type->data.fn.gen_return_type = gen_return_type;
-
- if (prefix_arg_error_return_trace && !is_async) {
- ZigType *gen_type = get_pointer_to_type(g, get_stack_trace_type(g), false);
- gen_param_types.append(get_llvm_type(g, gen_type));
- param_di_types.append(get_llvm_di_type(g, gen_type));
- }
- if (is_async) {
- fn_type->data.fn.gen_param_info = heap::c_allocator.allocate(2);
-
- ZigType *frame_type = get_any_frame_type(g, fn_type_id->return_type);
- gen_param_types.append(get_llvm_type(g, frame_type));
- param_di_types.append(get_llvm_di_type(g, frame_type));
-
- fn_type->data.fn.gen_param_info[0].src_index = 0;
- fn_type->data.fn.gen_param_info[0].gen_index = 0;
- fn_type->data.fn.gen_param_info[0].type = frame_type;
-
- gen_param_types.append(get_llvm_type(g, g->builtin_types.entry_usize));
- param_di_types.append(get_llvm_di_type(g, g->builtin_types.entry_usize));
-
- fn_type->data.fn.gen_param_info[1].src_index = 1;
- fn_type->data.fn.gen_param_info[1].gen_index = 1;
- fn_type->data.fn.gen_param_info[1].type = g->builtin_types.entry_usize;
- } else {
- fn_type->data.fn.gen_param_info = heap::c_allocator.allocate(fn_type_id->param_count);
- for (size_t i = 0; i < fn_type_id->param_count; i += 1) {
- FnTypeParamInfo *src_param_info = &fn_type->data.fn.fn_type_id.param_info[i];
- ZigType *type_entry = src_param_info->type;
- FnGenParamInfo *gen_param_info = &fn_type->data.fn.gen_param_info[i];
-
- gen_param_info->src_index = i;
- gen_param_info->gen_index = SIZE_MAX;
-
- if (is_c_abi || !type_has_bits(g, type_entry))
- continue;
-
- ZigType *gen_type;
- if (handle_is_ptr(g, type_entry)) {
- gen_type = get_pointer_to_type(g, type_entry, true);
- gen_param_info->is_byval = true;
- } else {
- gen_type = type_entry;
- }
- gen_param_info->gen_index = gen_param_types.length;
- gen_param_info->type = gen_type;
- gen_param_types.append(get_llvm_type(g, gen_type));
-
- param_di_types.append(get_llvm_di_type(g, gen_type));
- }
- }
-
- if (is_c_abi) {
- FnWalk fn_walk = {};
- fn_walk.id = FnWalkIdTypes;
- fn_walk.data.types.param_di_types = ¶m_di_types;
- fn_walk.data.types.gen_param_types = &gen_param_types;
- walk_function_params(g, fn_type, &fn_walk);
- }
-
- fn_type->data.fn.gen_param_count = gen_param_types.length;
-
- for (size_t i = 0; i < gen_param_types.length; i += 1) {
- assert(gen_param_types.items[i] != nullptr);
- }
-
- fn_type->data.fn.raw_type_ref = LLVMFunctionType(get_llvm_type(g, gen_return_type),
- gen_param_types.items, (unsigned int)gen_param_types.length, fn_type_id->is_var_args);
- const unsigned fn_addrspace = ZigLLVMDataLayoutGetProgramAddressSpace(g->target_data_ref);
- fn_type->llvm_type = LLVMPointerType(fn_type->data.fn.raw_type_ref, fn_addrspace);
- fn_type->data.fn.raw_di_type = ZigLLVMCreateSubroutineType(g->dbuilder, param_di_types.items, (int)param_di_types.length, 0);
- fn_type->llvm_di_type = ZigLLVMCreateDebugPointerType(g->dbuilder, fn_type->data.fn.raw_di_type,
- LLVMStoreSizeOfType(g->target_data_ref, fn_type->llvm_type),
- LLVMABIAlignmentOfType(g->target_data_ref, fn_type->llvm_type), "");
-
- gen_param_types.deinit();
- param_di_types.deinit();
-}
-
-void resolve_llvm_types_fn(CodeGen *g, ZigFn *fn) {
- Error err;
- if (fn->raw_di_type != nullptr) return;
-
- ZigType *fn_type = fn->type_entry;
- if (!fn_is_async(fn)) {
- resolve_llvm_types_fn_type(g, fn_type);
- fn->raw_type_ref = fn_type->data.fn.raw_type_ref;
- fn->raw_di_type = fn_type->data.fn.raw_di_type;
- return;
- }
-
- ZigType *gen_return_type = g->builtin_types.entry_void;
- ZigList param_di_types = {};
- ZigList gen_param_types = {};
- // first "parameter" is return value
- param_di_types.append(nullptr);
-
- ZigType *frame_type = get_fn_frame_type(g, fn);
- ZigType *ptr_type = get_pointer_to_type(g, frame_type, false);
- if ((err = type_resolve(g, ptr_type, ResolveStatusLLVMFwdDecl)))
- zig_unreachable();
- gen_param_types.append(ptr_type->llvm_type);
- param_di_types.append(ptr_type->llvm_di_type);
-
- // this parameter is used to pass the result pointer when await completes
- gen_param_types.append(get_llvm_type(g, g->builtin_types.entry_usize));
- param_di_types.append(get_llvm_di_type(g, g->builtin_types.entry_usize));
-
- fn->raw_type_ref = LLVMFunctionType(get_llvm_type(g, gen_return_type),
- gen_param_types.items, gen_param_types.length, false);
- fn->raw_di_type = ZigLLVMCreateSubroutineType(g->dbuilder, param_di_types.items, (int)param_di_types.length, 0);
-
- param_di_types.deinit();
- gen_param_types.deinit();
-}
-
-static void resolve_llvm_types_anyerror(CodeGen *g) {
- ZigType *entry = g->builtin_types.entry_global_error_set;
- entry->llvm_type = get_llvm_type(g, g->err_tag_type);
- ZigList err_enumerators = {};
- // reserve index 0 to indicate no error
- err_enumerators.append(ZigLLVMCreateDebugEnumerator(g->dbuilder, "(none)", 0));
- for (size_t i = 1; i < g->errors_by_index.length; i += 1) {
- ErrorTableEntry *error_entry = g->errors_by_index.at(i);
- err_enumerators.append(ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(&error_entry->name), i));
- }
-
- // create debug type for error sets
- uint64_t tag_debug_size_in_bits = g->err_tag_type->size_in_bits;
- uint64_t tag_debug_align_in_bits = 8*g->err_tag_type->abi_align;
- ZigLLVMDIFile *err_set_di_file = nullptr;
- entry->llvm_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder,
- ZigLLVMCompileUnitToScope(g->compile_unit), buf_ptr(&entry->name),
- err_set_di_file, 0,
- tag_debug_size_in_bits,
- tag_debug_align_in_bits,
- err_enumerators.items, err_enumerators.length,
- get_llvm_di_type(g, g->err_tag_type), "");
-
- err_enumerators.deinit();
-}
-
-static void resolve_llvm_types_async_frame(CodeGen *g, ZigType *frame_type, ResolveStatus wanted_resolve_status) {
- Error err;
- if ((err = type_resolve(g, frame_type, ResolveStatusSizeKnown)))
- zig_unreachable();
-
- ZigType *passed_frame_type = fn_is_async(frame_type->data.frame.fn) ? frame_type : nullptr;
- resolve_llvm_types_struct(g, frame_type->data.frame.locals_struct, wanted_resolve_status, passed_frame_type);
- frame_type->llvm_type = frame_type->data.frame.locals_struct->llvm_type;
- frame_type->llvm_di_type = frame_type->data.frame.locals_struct->llvm_di_type;
-}
-
-static void resolve_llvm_types_any_frame(CodeGen *g, ZigType *any_frame_type, ResolveStatus wanted_resolve_status) {
- if (any_frame_type->llvm_di_type != nullptr) return;
-
- Buf *name = buf_sprintf("(%s header)", buf_ptr(&any_frame_type->name));
- LLVMTypeRef frame_header_type = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(name));
- any_frame_type->llvm_type = LLVMPointerType(frame_header_type, 0);
-
- unsigned dwarf_kind = ZigLLVMTag_DW_structure_type();
- ZigLLVMDIFile *di_file = nullptr;
- ZigLLVMDIScope *di_scope = ZigLLVMCompileUnitToScope(g->compile_unit);
- unsigned line = 0;
- ZigLLVMDIType *frame_header_di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
- dwarf_kind, buf_ptr(name), di_scope, di_file, line);
- any_frame_type->llvm_di_type = ZigLLVMCreateDebugPointerType(g->dbuilder, frame_header_di_type,
- 8*g->pointer_size_bytes, 8*g->builtin_types.entry_usize->abi_align, buf_ptr(&any_frame_type->name));
-
- LLVMTypeRef llvm_void = LLVMVoidType();
- LLVMTypeRef arg_types[] = {any_frame_type->llvm_type, g->builtin_types.entry_usize->llvm_type};
- LLVMTypeRef fn_type = LLVMFunctionType(llvm_void, arg_types, 2, false);
- LLVMTypeRef usize_type_ref = get_llvm_type(g, g->builtin_types.entry_usize);
- ZigLLVMDIType *usize_di_type = get_llvm_di_type(g, g->builtin_types.entry_usize);
- ZigLLVMDIScope *compile_unit_scope = ZigLLVMCompileUnitToScope(g->compile_unit);
-
- ZigType *result_type = any_frame_type->data.any_frame.result_type;
- ZigType *ptr_result_type = (result_type == nullptr) ? nullptr : get_pointer_to_type(g, result_type, false);
- const unsigned fn_addrspace = ZigLLVMDataLayoutGetProgramAddressSpace(g->target_data_ref);
- LLVMTypeRef ptr_fn_llvm_type = LLVMPointerType(fn_type, fn_addrspace);
- if (result_type == nullptr) {
- g->anyframe_fn_type = ptr_fn_llvm_type;
- }
-
- ZigList field_types = {};
- ZigList di_element_types = {};
-
- // label (grep this): [fn_frame_struct_layout]
- field_types.append(ptr_fn_llvm_type); // fn_ptr
- field_types.append(usize_type_ref); // resume_index
- field_types.append(usize_type_ref); // awaiter
-
- bool have_result_type = result_type != nullptr && type_has_bits(g, result_type);
- if (have_result_type) {
- field_types.append(get_llvm_type(g, ptr_result_type)); // result_ptr_callee
- field_types.append(get_llvm_type(g, ptr_result_type)); // result_ptr_awaiter
- field_types.append(get_llvm_type(g, result_type)); // result
- if (codegen_fn_has_err_ret_tracing_arg(g, result_type)) {
- ZigType *ptr_stack_trace = get_pointer_to_type(g, get_stack_trace_type(g), false);
- field_types.append(get_llvm_type(g, ptr_stack_trace)); // ptr_stack_trace_callee
- field_types.append(get_llvm_type(g, ptr_stack_trace)); // ptr_stack_trace_awaiter
- }
- }
- LLVMStructSetBody(frame_header_type, field_types.items, field_types.length, false);
-
- di_element_types.append(
- ZigLLVMCreateDebugMemberType(g->dbuilder,
- ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "fn_ptr",
- di_file, line,
- 8*LLVMABISizeOfType(g->target_data_ref, field_types.at(di_element_types.length)),
- 8*LLVMABIAlignmentOfType(g->target_data_ref, field_types.at(di_element_types.length)),
- 8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, di_element_types.length),
- ZigLLVM_DIFlags_Zero, usize_di_type));
- di_element_types.append(
- ZigLLVMCreateDebugMemberType(g->dbuilder,
- ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "resume_index",
- di_file, line,
- 8*LLVMABISizeOfType(g->target_data_ref, field_types.at(di_element_types.length)),
- 8*LLVMABIAlignmentOfType(g->target_data_ref, field_types.at(di_element_types.length)),
- 8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, di_element_types.length),
- ZigLLVM_DIFlags_Zero, usize_di_type));
- di_element_types.append(
- ZigLLVMCreateDebugMemberType(g->dbuilder,
- ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "awaiter",
- di_file, line,
- 8*LLVMABISizeOfType(g->target_data_ref, field_types.at(di_element_types.length)),
- 8*LLVMABIAlignmentOfType(g->target_data_ref, field_types.at(di_element_types.length)),
- 8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, di_element_types.length),
- ZigLLVM_DIFlags_Zero, usize_di_type));
-
- if (have_result_type) {
- di_element_types.append(
- ZigLLVMCreateDebugMemberType(g->dbuilder,
- ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "result_ptr_callee",
- di_file, line,
- 8*LLVMABISizeOfType(g->target_data_ref, field_types.at(di_element_types.length)),
- 8*LLVMABIAlignmentOfType(g->target_data_ref, field_types.at(di_element_types.length)),
- 8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, di_element_types.length),
- ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, ptr_result_type)));
- di_element_types.append(
- ZigLLVMCreateDebugMemberType(g->dbuilder,
- ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "result_ptr_awaiter",
- di_file, line,
- 8*LLVMABISizeOfType(g->target_data_ref, field_types.at(di_element_types.length)),
- 8*LLVMABIAlignmentOfType(g->target_data_ref, field_types.at(di_element_types.length)),
- 8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, di_element_types.length),
- ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, ptr_result_type)));
- di_element_types.append(
- ZigLLVMCreateDebugMemberType(g->dbuilder,
- ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "result",
- di_file, line,
- 8*LLVMABISizeOfType(g->target_data_ref, field_types.at(di_element_types.length)),
- 8*LLVMABIAlignmentOfType(g->target_data_ref, field_types.at(di_element_types.length)),
- 8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, di_element_types.length),
- ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, result_type)));
-
- if (codegen_fn_has_err_ret_tracing_arg(g, result_type)) {
- ZigType *ptr_stack_trace = get_pointer_to_type(g, get_stack_trace_type(g), false);
- di_element_types.append(
- ZigLLVMCreateDebugMemberType(g->dbuilder,
- ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "ptr_stack_trace_callee",
- di_file, line,
- 8*LLVMABISizeOfType(g->target_data_ref, field_types.at(di_element_types.length)),
- 8*LLVMABIAlignmentOfType(g->target_data_ref, field_types.at(di_element_types.length)),
- 8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, di_element_types.length),
- ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, ptr_stack_trace)));
- di_element_types.append(
- ZigLLVMCreateDebugMemberType(g->dbuilder,
- ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "ptr_stack_trace_awaiter",
- di_file, line,
- 8*LLVMABISizeOfType(g->target_data_ref, field_types.at(di_element_types.length)),
- 8*LLVMABIAlignmentOfType(g->target_data_ref, field_types.at(di_element_types.length)),
- 8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, di_element_types.length),
- ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, ptr_stack_trace)));
- }
- };
-
- ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
- compile_unit_scope, buf_ptr(name),
- di_file, line,
- 8*LLVMABISizeOfType(g->target_data_ref, frame_header_type),
- 8*LLVMABIAlignmentOfType(g->target_data_ref, frame_header_type),
- ZigLLVM_DIFlags_Zero,
- nullptr, di_element_types.items, di_element_types.length, 0, nullptr, "");
-
- ZigLLVMReplaceTemporary(g->dbuilder, frame_header_di_type, replacement_di_type);
-
- field_types.deinit();
- di_element_types.deinit();
-}
-
-static void resolve_llvm_types(CodeGen *g, ZigType *type, ResolveStatus wanted_resolve_status) {
- assert(wanted_resolve_status > ResolveStatusSizeKnown);
- switch (type->id) {
- case ZigTypeIdInvalid:
- case ZigTypeIdMetaType:
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdEnumLiteral:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdBoundFn:
- zig_unreachable();
- case ZigTypeIdFloat:
- case ZigTypeIdOpaque:
- case ZigTypeIdVoid:
- case ZigTypeIdBool:
- case ZigTypeIdUnreachable:
- assert(type->llvm_di_type != nullptr);
- return;
- case ZigTypeIdStruct:
- if (type->data.structure.special == StructSpecialSlice)
- return resolve_llvm_types_slice(g, type, wanted_resolve_status);
- else
- return resolve_llvm_types_struct(g, type, wanted_resolve_status, nullptr);
- case ZigTypeIdEnum:
- return resolve_llvm_types_enum(g, type, wanted_resolve_status);
- case ZigTypeIdUnion:
- return resolve_llvm_types_union(g, type, wanted_resolve_status);
- case ZigTypeIdPointer:
- return resolve_llvm_types_pointer(g, type, wanted_resolve_status);
- case ZigTypeIdInt:
- return resolve_llvm_types_integer(g, type);
- case ZigTypeIdOptional:
- return resolve_llvm_types_optional(g, type, wanted_resolve_status);
- case ZigTypeIdErrorUnion:
- return resolve_llvm_types_error_union(g, type);
- case ZigTypeIdArray:
- return resolve_llvm_types_array(g, type);
- case ZigTypeIdFn:
- return resolve_llvm_types_fn_type(g, type);
- case ZigTypeIdErrorSet: {
- if (type->llvm_di_type != nullptr) return;
-
- if (g->builtin_types.entry_global_error_set->llvm_type == nullptr) {
- resolve_llvm_types_anyerror(g);
- }
- type->llvm_type = g->builtin_types.entry_global_error_set->llvm_type;
- type->llvm_di_type = g->builtin_types.entry_global_error_set->llvm_di_type;
- return;
- }
- case ZigTypeIdVector: {
- if (type->llvm_di_type != nullptr) return;
-
- type->llvm_type = LLVMVectorType(get_llvm_type(g, type->data.vector.elem_type), type->data.vector.len);
- type->llvm_di_type = ZigLLVMDIBuilderCreateVectorType(g->dbuilder, 8 * type->abi_size,
- type->abi_align, get_llvm_di_type(g, type->data.vector.elem_type), type->data.vector.len);
- return;
- }
- case ZigTypeIdFnFrame:
- return resolve_llvm_types_async_frame(g, type, wanted_resolve_status);
- case ZigTypeIdAnyFrame:
- return resolve_llvm_types_any_frame(g, type, wanted_resolve_status);
- }
- zig_unreachable();
-}
-
-LLVMTypeRef get_llvm_type(CodeGen *g, ZigType *type) {
- assertNoError(type_resolve(g, type, ResolveStatusLLVMFull));
- assert(type->abi_size == 0 || type->abi_size >= LLVMABISizeOfType(g->target_data_ref, type->llvm_type));
- assert(type->abi_align == 0 || type->abi_align >= LLVMABIAlignmentOfType(g->target_data_ref, type->llvm_type));
- return type->llvm_type;
-}
-
-ZigLLVMDIType *get_llvm_di_type(CodeGen *g, ZigType *type) {
- assertNoError(type_resolve(g, type, ResolveStatusLLVMFull));
- return type->llvm_di_type;
-}
-
-void src_assert_impl(bool ok, AstNode *source_node, char const *file, unsigned int line) {
- if (ok) return;
- if (source_node == nullptr) {
- fprintf(stderr, "when analyzing (unknown source location) ");
- } else {
- fprintf(stderr, "when analyzing %s:%u:%u ",
- buf_ptr(source_node->owner->data.structure.root_struct->path),
- (unsigned)source_node->line + 1, (unsigned)source_node->column + 1);
- }
- fprintf(stderr, "in compiler source at %s:%u: ", file, line);
- const char *msg = "assertion failed. This is a bug in the Zig compiler.";
- stage2_panic(msg, strlen(msg));
-}
-
-Error analyze_import(CodeGen *g, ZigType *source_import, Buf *import_target_str,
- ZigType **out_import, Buf **out_import_target_path, Buf *out_full_path)
-{
- Error err;
-
- Buf *search_dir;
- ZigPackage *cur_scope_pkg = source_import->data.structure.root_struct->package;
- assert(cur_scope_pkg);
- ZigPackage *target_package;
- auto package_entry = cur_scope_pkg->package_table.maybe_get(import_target_str);
- SourceKind source_kind;
- if (package_entry) {
- target_package = package_entry->value;
- *out_import_target_path = &target_package->root_src_path;
- search_dir = &target_package->root_src_dir;
- source_kind = SourceKindPkgMain;
- } else {
- // try it as a filename
- target_package = cur_scope_pkg;
- *out_import_target_path = import_target_str;
-
- // search relative to importing file
- search_dir = buf_alloc();
- os_path_dirname(source_import->data.structure.root_struct->path, search_dir);
-
- source_kind = SourceKindNonRoot;
- }
-
- buf_resize(out_full_path, 0);
- os_path_join(search_dir, *out_import_target_path, out_full_path);
-
- Buf *import_code = buf_alloc();
- Buf *resolved_path = buf_alloc();
-
- Buf *resolve_paths[] = { out_full_path, };
- *resolved_path = os_path_resolve(resolve_paths, 1);
-
- auto import_entry = g->import_table.maybe_get(resolved_path);
- if (import_entry) {
- *out_import = import_entry->value;
- return ErrorNone;
- }
-
- if (source_kind == SourceKindNonRoot) {
- Buf *pkg_root_src_dir = &cur_scope_pkg->root_src_dir;
- Buf resolved_root_src_dir = os_path_resolve(&pkg_root_src_dir, 1);
- if (!buf_starts_with_buf(resolved_path, &resolved_root_src_dir)) {
- return ErrorImportOutsidePkgPath;
- }
- }
-
- if ((err = file_fetch(g, resolved_path, import_code))) {
- return err;
- }
-
- *out_import = add_source_file(g, target_package, resolved_path, import_code, source_kind);
- return ErrorNone;
-}
-
-
-void IrExecutableSrc::src() {
- if (this->source_node != nullptr) {
- this->source_node->src();
- }
- if (this->parent_exec != nullptr) {
- this->parent_exec->src();
- }
-}
-
-void IrExecutableGen::src() {
- IrExecutableGen *it;
- for (it = this; it != nullptr && it->source_node != nullptr; it = it->parent_exec) {
- it->source_node->src();
- }
-}
-
-bool is_anon_container(ZigType *ty) {
- return ty->id == ZigTypeIdStruct && (
- ty->data.structure.special == StructSpecialInferredTuple ||
- ty->data.structure.special == StructSpecialInferredStruct);
-}
-
-bool is_opt_err_set(ZigType *ty) {
- return ty->id == ZigTypeIdErrorSet ||
- (ty->id == ZigTypeIdOptional && ty->data.maybe.child_type->id == ZigTypeIdErrorSet);
-}
-
-// Returns whether the x_optional field of ZigValue is active.
-bool type_has_optional_repr(ZigType *ty) {
- if (ty->id != ZigTypeIdOptional) {
- return false;
- } else if (get_src_ptr_type(ty) != nullptr) {
- return false;
- } else if (is_opt_err_set(ty)) {
- return false;
- } else {
- return true;
- }
-}
-
-void copy_const_val(CodeGen *g, ZigValue *dest, ZigValue *src) {
- uint32_t prev_align = dest->llvm_align;
- ConstParent prev_parent = dest->parent;
- memcpy(dest, src, sizeof(ZigValue));
- dest->llvm_align = prev_align;
- if (src->special != ConstValSpecialStatic)
- return;
- dest->parent = prev_parent;
- if (dest->type->id == ZigTypeIdStruct) {
- dest->data.x_struct.fields = alloc_const_vals_ptrs(g, dest->type->data.structure.src_field_count);
- for (size_t i = 0; i < dest->type->data.structure.src_field_count; i += 1) {
- copy_const_val(g, dest->data.x_struct.fields[i], src->data.x_struct.fields[i]);
- dest->data.x_struct.fields[i]->parent.id = ConstParentIdStruct;
- dest->data.x_struct.fields[i]->parent.data.p_struct.struct_val = dest;
- dest->data.x_struct.fields[i]->parent.data.p_struct.field_index = i;
- }
- } else if (dest->type->id == ZigTypeIdArray) {
- switch (dest->data.x_array.special) {
- case ConstArraySpecialNone: {
- dest->data.x_array.data.s_none.elements = g->pass1_arena->allocate(dest->type->data.array.len);
- for (uint64_t i = 0; i < dest->type->data.array.len; i += 1) {
- copy_const_val(g, &dest->data.x_array.data.s_none.elements[i], &src->data.x_array.data.s_none.elements[i]);
- dest->data.x_array.data.s_none.elements[i].parent.id = ConstParentIdArray;
- dest->data.x_array.data.s_none.elements[i].parent.data.p_array.array_val = dest;
- dest->data.x_array.data.s_none.elements[i].parent.data.p_array.elem_index = i;
- }
- break;
- }
- case ConstArraySpecialUndef: {
- // Nothing to copy; the above memcpy did everything we needed.
- break;
- }
- case ConstArraySpecialBuf: {
- dest->data.x_array.data.s_buf = buf_create_from_buf(src->data.x_array.data.s_buf);
- break;
- }
- }
- } else if (dest->type->id == ZigTypeIdUnion) {
- bigint_init_bigint(&dest->data.x_union.tag, &src->data.x_union.tag);
- dest->data.x_union.payload = g->pass1_arena->create();
- copy_const_val(g, dest->data.x_union.payload, src->data.x_union.payload);
- dest->data.x_union.payload->parent.id = ConstParentIdUnion;
- dest->data.x_union.payload->parent.data.p_union.union_val = dest;
- } else if (type_has_optional_repr(dest->type) && dest->data.x_optional != nullptr) {
- dest->data.x_optional = g->pass1_arena->create();
- copy_const_val(g, dest->data.x_optional, src->data.x_optional);
- dest->data.x_optional->parent.id = ConstParentIdOptionalPayload;
- dest->data.x_optional->parent.data.p_optional_payload.optional_val = dest;
- }
-}
-
-bool optional_value_is_null(ZigValue *val) {
- assert(val->special == ConstValSpecialStatic);
- if (get_src_ptr_type(val->type) != nullptr) {
- if (val->data.x_ptr.special == ConstPtrSpecialNull) {
- return true;
- } else if (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) {
- return val->data.x_ptr.data.hard_coded_addr.addr == 0;
- } else {
- return false;
- }
- } else if (is_opt_err_set(val->type)) {
- return val->data.x_err_set == nullptr;
- } else {
- return val->data.x_optional == nullptr;
- }
-}
-
-bool type_is_numeric(ZigType *ty) {
- switch (ty->id) {
- case ZigTypeIdInvalid:
- zig_unreachable();
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdInt:
- case ZigTypeIdFloat:
- case ZigTypeIdUndefined:
- return true;
-
- case ZigTypeIdVector:
- return type_is_numeric(ty->data.vector.elem_type);
-
- case ZigTypeIdMetaType:
- case ZigTypeIdVoid:
- case ZigTypeIdBool:
- case ZigTypeIdUnreachable:
- case ZigTypeIdPointer:
- case ZigTypeIdArray:
- case ZigTypeIdStruct:
- case ZigTypeIdNull:
- case ZigTypeIdOptional:
- case ZigTypeIdErrorUnion:
- case ZigTypeIdErrorSet:
- case ZigTypeIdEnum:
- case ZigTypeIdUnion:
- case ZigTypeIdFn:
- case ZigTypeIdBoundFn:
- case ZigTypeIdOpaque:
- case ZigTypeIdFnFrame:
- case ZigTypeIdAnyFrame:
- case ZigTypeIdEnumLiteral:
- return false;
- }
- zig_unreachable();
-}
-
-static void dump_value_indent_error_set(ZigValue *val, int indent) {
- fprintf(stderr, "\n");
-}
-
-static void dump_value_indent(ZigValue *val, int indent);
-
-static void dump_value_indent_ptr(ZigValue *val, int indent) {
- switch (val->data.x_ptr.special) {
- case ConstPtrSpecialInvalid:
- fprintf(stderr, "\n");
- return;
- case ConstPtrSpecialNull:
- fprintf(stderr, "\n");
- return;
- case ConstPtrSpecialRef:
- fprintf(stderr, "[data.x_ptr.data.ref.pointee, indent + 1);
- break;
- case ConstPtrSpecialBaseStruct: {
- ZigValue *struct_val = val->data.x_ptr.data.base_struct.struct_val;
- size_t field_index = val->data.x_ptr.data.base_struct.field_index;
- fprintf(stderr, "data.x_struct.fields[field_index];
- if (field_val != nullptr) {
- dump_value_indent(field_val, indent + 1);
- } else {
- for (int i = 0; i < indent; i += 1) {
- fprintf(stderr, " ");
- }
- fprintf(stderr, "(invalid null field)\n");
- }
- }
- break;
- }
- case ConstPtrSpecialBaseOptionalPayload: {
- ZigValue *optional_val = val->data.x_ptr.data.base_optional_payload.optional_val;
- fprintf(stderr, "\n");
-}
-
-static void dump_value_indent(ZigValue *val, int indent) {
- for (int i = 0; i < indent; i += 1) {
- fprintf(stderr, " ");
- }
- fprintf(stderr, "Value@%p(", val);
- if (val->type != nullptr) {
- fprintf(stderr, "%s)", buf_ptr(&val->type->name));
- } else {
- fprintf(stderr, "type=nullptr)");
- }
- switch (val->special) {
- case ConstValSpecialUndef:
- fprintf(stderr, "[undefined]\n");
- return;
- case ConstValSpecialLazy:
- fprintf(stderr, "[lazy]\n");
- return;
- case ConstValSpecialRuntime:
- fprintf(stderr, "[runtime]\n");
- return;
- case ConstValSpecialStatic:
- break;
- }
- if (val->type == nullptr)
- return;
- switch (val->type->id) {
- case ZigTypeIdInvalid:
- fprintf(stderr, "\n");
- return;
- case ZigTypeIdUnreachable:
- fprintf(stderr, "\n");
- return;
- case ZigTypeIdUndefined:
- fprintf(stderr, "\n");
- return;
- case ZigTypeIdVoid:
- fprintf(stderr, "<{}>\n");
- return;
- case ZigTypeIdMetaType:
- fprintf(stderr, "<%s>\n", buf_ptr(&val->data.x_type->name));
- return;
- case ZigTypeIdBool:
- fprintf(stderr, "<%s>\n", val->data.x_bool ? "true" : "false");
- return;
- case ZigTypeIdComptimeInt:
- case ZigTypeIdInt: {
- Buf *tmp_buf = buf_alloc();
- bigint_append_buf(tmp_buf, &val->data.x_bigint, 10);
- fprintf(stderr, "<%s>\n", buf_ptr(tmp_buf));
- buf_destroy(tmp_buf);
- return;
- }
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdFloat:
- fprintf(stderr, "\n");
- return;
-
- case ZigTypeIdStruct:
- fprintf(stderr, "type->data.structure.src_field_count; i += 1) {
- for (int j = 0; j < indent; j += 1) {
- fprintf(stderr, " ");
- }
- fprintf(stderr, "%s: ", buf_ptr(val->type->data.structure.fields[i]->name));
- if (val->data.x_struct.fields == nullptr) {
- fprintf(stderr, "\n");
- } else {
- dump_value_indent(val->data.x_struct.fields[i], 1);
- }
- }
- for (int i = 0; i < indent; i += 1) {
- fprintf(stderr, " ");
- }
- fprintf(stderr, ">\n");
- return;
-
- case ZigTypeIdOptional:
- if (get_src_ptr_type(val->type) != nullptr) {
- return dump_value_indent_ptr(val, indent);
- } else if (val->type->data.maybe.child_type->id == ZigTypeIdErrorSet) {
- return dump_value_indent_error_set(val, indent);
- } else {
- fprintf(stderr, "<\n");
- dump_value_indent(val->data.x_optional, indent + 1);
-
- for (int i = 0; i < indent; i += 1) {
- fprintf(stderr, " ");
- }
- fprintf(stderr, ">\n");
- return;
- }
- case ZigTypeIdErrorUnion:
- if (val->data.x_err_union.payload != nullptr) {
- fprintf(stderr, "<\n");
- dump_value_indent(val->data.x_err_union.payload, indent + 1);
- } else {
- fprintf(stderr, "<\n");
- dump_value_indent(val->data.x_err_union.error_set, 0);
- }
- for (int i = 0; i < indent; i += 1) {
- fprintf(stderr, " ");
- }
- fprintf(stderr, ">\n");
- return;
-
- case ZigTypeIdPointer:
- return dump_value_indent_ptr(val, indent);
-
- case ZigTypeIdErrorSet:
- return dump_value_indent_error_set(val, indent);
-
- case ZigTypeIdVector:
- case ZigTypeIdArray:
- case ZigTypeIdNull:
- case ZigTypeIdEnum:
- case ZigTypeIdUnion:
- case ZigTypeIdFn:
- case ZigTypeIdBoundFn:
- case ZigTypeIdOpaque:
- case ZigTypeIdFnFrame:
- case ZigTypeIdAnyFrame:
- case ZigTypeIdEnumLiteral:
- fprintf(stderr, "\n");
- return;
- }
- zig_unreachable();
-}
-
-void ZigValue::dump() {
- dump_value_indent(this, 0);
-}
-
-// float ops that take a single argument
-//TODO Powi, Pow, minnum, maxnum, maximum, minimum, copysign, lround, llround, lrint, llrint
-const char *float_op_to_name(BuiltinFnId op) {
- switch (op) {
- case BuiltinFnIdSqrt:
- return "sqrt";
- case BuiltinFnIdSin:
- return "sin";
- case BuiltinFnIdCos:
- return "cos";
- case BuiltinFnIdExp:
- return "exp";
- case BuiltinFnIdExp2:
- return "exp2";
- case BuiltinFnIdLog:
- return "log";
- case BuiltinFnIdLog10:
- return "log10";
- case BuiltinFnIdLog2:
- return "log2";
- case BuiltinFnIdFabs:
- return "fabs";
- case BuiltinFnIdFloor:
- return "floor";
- case BuiltinFnIdCeil:
- return "ceil";
- case BuiltinFnIdTrunc:
- return "trunc";
- case BuiltinFnIdNearbyInt:
- return "nearbyint";
- case BuiltinFnIdRound:
- return "round";
- default:
- zig_unreachable();
- }
-}
-
diff --git a/src/analyze.hpp b/src/analyze.hpp
deleted file mode 100644
index 07601e6dea0c7bc5c68885c1c95bcb7f37d04349..0000000000000000000000000000000000000000
--- a/src/analyze.hpp
+++ /dev/null
@@ -1,297 +0,0 @@
-/*
- * Copyright (c) 2015 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#ifndef ZIG_ANALYZE_HPP
-#define ZIG_ANALYZE_HPP
-
-#include "all_types.hpp"
-
-void semantic_analyze(CodeGen *g);
-ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg);
-ErrorMsg *add_token_error(CodeGen *g, ZigType *owner, Token *token, Buf *msg);
-ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, const AstNode *node, Buf *msg);
-ZigType *new_type_table_entry(ZigTypeId id);
-ZigType *get_fn_frame_type(CodeGen *g, ZigFn *fn);
-ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const);
-ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type,
- bool is_const, bool is_volatile, PtrLen ptr_len,
- uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count,
- bool allow_zero);
-ZigType *get_pointer_to_type_extra2(CodeGen *g, ZigType *child_type,
- bool is_const, bool is_volatile, PtrLen ptr_len,
- uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count,
- bool allow_zero, uint32_t vector_index, InferredStructField *inferred_struct_field,
- ZigValue *sentinel);
-uint64_t type_size(CodeGen *g, ZigType *type_entry);
-uint64_t type_size_bits(CodeGen *g, ZigType *type_entry);
-ZigType *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits);
-ZigType *get_vector_type(CodeGen *g, uint32_t len, ZigType *elem_type);
-ZigType **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type);
-ZigType *get_c_int_type(CodeGen *g, CIntType c_int_type);
-ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id);
-ZigType *get_optional_type(CodeGen *g, ZigType *child_type);
-ZigType *get_optional_type2(CodeGen *g, ZigType *child_type);
-ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size, ZigValue *sentinel);
-ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type);
-ZigType *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind,
- AstNode *decl_node, const char *full_name, Buf *bare_name, ContainerLayout layout);
-ZigType *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x);
-ZigType *get_error_union_type(CodeGen *g, ZigType *err_set_type, ZigType *payload_type);
-ZigType *get_bound_fn_type(CodeGen *g, ZigFn *fn_entry);
-ZigType *get_opaque_type(CodeGen *g, Scope *scope, AstNode *source_node, const char *full_name, Buf *bare_name);
-ZigType *get_test_fn_type(CodeGen *g);
-ZigType *get_any_frame_type(CodeGen *g, ZigType *result_type);
-bool handle_is_ptr(CodeGen *g, ZigType *type_entry);
-
-bool type_has_bits(CodeGen *g, ZigType *type_entry);
-Error type_has_bits2(CodeGen *g, ZigType *type_entry, bool *result);
-
-Error type_allowed_in_extern(CodeGen *g, ZigType *type_entry, bool *result);
-bool ptr_allows_addr_zero(ZigType *ptr_type);
-
-// Deprecated, use `type_is_nonnull_ptr2`
-bool type_is_nonnull_ptr(CodeGen *g, ZigType *type);
-Error type_is_nonnull_ptr2(CodeGen *g, ZigType *type, bool *result);
-
-ZigType *get_codegen_ptr_type_bail(CodeGen *g, ZigType *type);
-Error get_codegen_ptr_type(CodeGen *g, ZigType *type, ZigType **result);
-
-enum SourceKind {
- SourceKindRoot,
- SourceKindPkgMain,
- SourceKindNonRoot,
- SourceKindCImport,
-};
-ZigType *add_source_file(CodeGen *g, ZigPackage *package, Buf *abs_full_path, Buf *source_code,
- SourceKind source_kind);
-
-ZigVar *find_variable(CodeGen *g, Scope *orig_context, Buf *name, ScopeFnDef **crossed_fndef_scope);
-Tld *find_decl(CodeGen *g, Scope *scope, Buf *name);
-Tld *find_container_decl(CodeGen *g, ScopeDecls *decls_scope, Buf *name);
-void resolve_top_level_decl(CodeGen *g, Tld *tld, AstNode *source_node, bool allow_lazy);
-
-ZigType *get_src_ptr_type(ZigType *type);
-uint32_t get_ptr_align(CodeGen *g, ZigType *type);
-bool get_ptr_const(CodeGen *g, ZigType *type);
-ZigType *validate_var_type(CodeGen *g, AstNodeVariableDeclaration *source_node, ZigType *type_entry);
-ZigType *container_ref_type(ZigType *type_entry);
-bool type_is_complete(ZigType *type_entry);
-bool type_is_resolved(ZigType *type_entry, ResolveStatus status);
-bool type_is_invalid(ZigType *type_entry);
-bool type_is_global_error_set(ZigType *err_set_type);
-ScopeDecls *get_container_scope(ZigType *type_entry);
-TypeStructField *find_struct_type_field(ZigType *type_entry, Buf *name);
-TypeEnumField *find_enum_type_field(ZigType *enum_type, Buf *name);
-TypeUnionField *find_union_type_field(ZigType *type_entry, Buf *name);
-TypeEnumField *find_enum_field_by_tag(ZigType *enum_type, const BigInt *tag);
-TypeUnionField *find_union_field_by_tag(ZigType *type_entry, const BigInt *tag);
-
-bool is_ref(ZigType *type_entry);
-bool is_array_ref(ZigType *type_entry);
-bool is_container_ref(ZigType *type_entry);
-Error is_valid_vector_elem_type(CodeGen *g, ZigType *elem_type, bool *result);
-void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node);
-ZigFn *scope_fn_entry(Scope *scope);
-ZigPackage *scope_package(Scope *scope);
-ZigType *get_scope_import(Scope *scope);
-ScopeTypeOf *get_scope_typeof(Scope *scope);
-void init_tld(Tld *tld, TldId id, Buf *name, VisibMod visib_mod, AstNode *source_node, Scope *parent_scope);
-ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf *name,
- bool is_const, ZigValue *init_value, Tld *src_tld, ZigType *var_type);
-ZigType *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node);
-void append_namespace_qualification(CodeGen *g, Buf *buf, ZigType *container_type);
-ZigFn *create_fn(CodeGen *g, AstNode *proto_node);
-void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, CallingConvention cc, size_t param_count_alloc);
-AstNode *get_param_decl_node(ZigFn *fn_entry, size_t index);
-Error ATTRIBUTE_MUST_USE type_resolve(CodeGen *g, ZigType *type_entry, ResolveStatus status);
-void complete_enum(CodeGen *g, ZigType *enum_type);
-bool ir_get_var_is_comptime(ZigVar *var);
-bool const_values_equal(CodeGen *g, ZigValue *a, ZigValue *b);
-void eval_min_max_value(CodeGen *g, ZigType *type_entry, ZigValue *const_val, bool is_max);
-void eval_min_max_value_int(CodeGen *g, ZigType *int_type, BigInt *bigint, bool is_max);
-
-void render_const_value(CodeGen *g, Buf *buf, ZigValue *const_val);
-
-ScopeDecls *create_decls_scope(CodeGen *g, AstNode *node, Scope *parent, ZigType *container_type, ZigType *import, Buf *bare_name);
-ScopeBlock *create_block_scope(CodeGen *g, AstNode *node, Scope *parent);
-ScopeDefer *create_defer_scope(CodeGen *g, AstNode *node, Scope *parent);
-ScopeDeferExpr *create_defer_expr_scope(CodeGen *g, AstNode *node, Scope *parent);
-Scope *create_var_scope(CodeGen *g, AstNode *node, Scope *parent, ZigVar *var);
-ScopeCImport *create_cimport_scope(CodeGen *g, AstNode *node, Scope *parent);
-ScopeLoop *create_loop_scope(CodeGen *g, AstNode *node, Scope *parent);
-ScopeSuspend *create_suspend_scope(CodeGen *g, AstNode *node, Scope *parent);
-ScopeFnDef *create_fndef_scope(CodeGen *g, AstNode *node, Scope *parent, ZigFn *fn_entry);
-Scope *create_comptime_scope(CodeGen *g, AstNode *node, Scope *parent);
-Scope *create_nosuspend_scope(CodeGen *g, AstNode *node, Scope *parent);
-Scope *create_runtime_scope(CodeGen *g, AstNode *node, Scope *parent, IrInstSrc *is_comptime);
-Scope *create_typeof_scope(CodeGen *g, AstNode *node, Scope *parent);
-ScopeExpr *create_expr_scope(CodeGen *g, AstNode *node, Scope *parent);
-
-void init_const_str_lit(CodeGen *g, ZigValue *const_val, Buf *str);
-ZigValue *create_const_str_lit(CodeGen *g, Buf *str);
-
-void init_const_bigint(ZigValue *const_val, ZigType *type, const BigInt *bigint);
-ZigValue *create_const_bigint(CodeGen *g, ZigType *type, const BigInt *bigint);
-
-void init_const_unsigned_negative(ZigValue *const_val, ZigType *type, uint64_t x, bool negative);
-ZigValue *create_const_unsigned_negative(CodeGen *g, ZigType *type, uint64_t x, bool negative);
-
-void init_const_signed(ZigValue *const_val, ZigType *type, int64_t x);
-ZigValue *create_const_signed(CodeGen *g, ZigType *type, int64_t x);
-
-void init_const_usize(CodeGen *g, ZigValue *const_val, uint64_t x);
-ZigValue *create_const_usize(CodeGen *g, uint64_t x);
-
-void init_const_float(ZigValue *const_val, ZigType *type, double value);
-ZigValue *create_const_float(CodeGen *g, ZigType *type, double value);
-
-void init_const_enum(ZigValue *const_val, ZigType *type, const BigInt *tag);
-ZigValue *create_const_enum(CodeGen *g, ZigType *type, const BigInt *tag);
-
-void init_const_bool(CodeGen *g, ZigValue *const_val, bool value);
-ZigValue *create_const_bool(CodeGen *g, bool value);
-
-void init_const_type(CodeGen *g, ZigValue *const_val, ZigType *type_value);
-ZigValue *create_const_type(CodeGen *g, ZigType *type_value);
-
-void init_const_runtime(ZigValue *const_val, ZigType *type);
-ZigValue *create_const_runtime(CodeGen *g, ZigType *type);
-
-void init_const_ptr_ref(CodeGen *g, ZigValue *const_val, ZigValue *pointee_val, bool is_const);
-ZigValue *create_const_ptr_ref(CodeGen *g, ZigValue *pointee_val, bool is_const);
-
-void init_const_ptr_hard_coded_addr(CodeGen *g, ZigValue *const_val, ZigType *pointee_type,
- size_t addr, bool is_const);
-ZigValue *create_const_ptr_hard_coded_addr(CodeGen *g, ZigType *pointee_type,
- size_t addr, bool is_const);
-
-void init_const_ptr_array(CodeGen *g, ZigValue *const_val, ZigValue *array_val,
- size_t elem_index, bool is_const, PtrLen ptr_len);
-ZigValue *create_const_ptr_array(CodeGen *g, ZigValue *array_val, size_t elem_index,
- bool is_const, PtrLen ptr_len);
-
-void init_const_slice(CodeGen *g, ZigValue *const_val, ZigValue *array_val,
- size_t start, size_t len, bool is_const);
-ZigValue *create_const_slice(CodeGen *g, ZigValue *array_val, size_t start, size_t len, bool is_const);
-
-void init_const_null(ZigValue *const_val, ZigType *type);
-ZigValue *create_const_null(CodeGen *g, ZigType *type);
-
-void init_const_fn(ZigValue *const_val, ZigFn *fn);
-ZigValue *create_const_fn(CodeGen *g, ZigFn *fn);
-
-ZigValue **alloc_const_vals_ptrs(CodeGen *g, size_t count);
-ZigValue **realloc_const_vals_ptrs(CodeGen *g, ZigValue **ptr, size_t old_count, size_t new_count);
-
-TypeStructField **alloc_type_struct_fields(size_t count);
-TypeStructField **realloc_type_struct_fields(TypeStructField **ptr, size_t old_count, size_t new_count);
-
-ZigType *make_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits);
-void expand_undef_array(CodeGen *g, ZigValue *const_val);
-void expand_undef_struct(CodeGen *g, ZigValue *const_val);
-void update_compile_var(CodeGen *g, Buf *name, ZigValue *value);
-
-const char *type_id_name(ZigTypeId id);
-ZigTypeId type_id_at_index(size_t index);
-size_t type_id_len();
-size_t type_id_index(ZigType *entry);
-ZigType *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id);
-bool optional_value_is_null(ZigValue *val);
-
-uint32_t get_abi_alignment(CodeGen *g, ZigType *type_entry);
-ZigType *get_align_amt_type(CodeGen *g);
-ZigPackage *new_anonymous_package(void);
-
-Buf *const_value_to_buffer(ZigValue *const_val);
-void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, const char *symbol_name, GlobalLinkageId linkage, CallingConvention cc);
-void add_var_export(CodeGen *g, ZigVar *fn_table_entry, const char *symbol_name, GlobalLinkageId linkage);
-
-
-ZigValue *get_builtin_value(CodeGen *codegen, const char *name);
-ZigType *get_builtin_type(CodeGen *codegen, const char *name);
-ZigType *get_stack_trace_type(CodeGen *g);
-bool resolve_inferred_error_set(CodeGen *g, ZigType *err_set_type, AstNode *source_node);
-
-ZigType *get_auto_err_set_type(CodeGen *g, ZigFn *fn_entry);
-
-bool fn_type_can_fail(FnTypeId *fn_type_id);
-bool type_can_fail(ZigType *type_entry);
-bool fn_eval_cacheable(Scope *scope, ZigType *return_type);
-AstNode *type_decl_node(ZigType *type_entry);
-
-Error get_primitive_type(CodeGen *g, Buf *name, ZigType **result);
-
-bool calling_convention_allows_zig_types(CallingConvention cc);
-const char *calling_convention_name(CallingConvention cc);
-
-Error ATTRIBUTE_MUST_USE file_fetch(CodeGen *g, Buf *resolved_path, Buf *contents);
-
-void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk);
-X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty);
-bool type_is_c_abi_int_bail(CodeGen *g, ZigType *ty);
-Error type_is_c_abi_int(CodeGen *g, ZigType *ty, bool *result);
-bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id);
-const char *container_string(ContainerKind kind);
-
-uint32_t get_host_int_bytes(CodeGen *g, ZigType *struct_type, TypeStructField *field);
-
-enum ReqCompTime {
- ReqCompTimeInvalid,
- ReqCompTimeNo,
- ReqCompTimeYes,
-};
-ReqCompTime type_requires_comptime(CodeGen *g, ZigType *type_entry);
-
-OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry);
-
-Error ensure_const_val_repr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node,
- ZigValue *const_val, ZigType *wanted_type);
-
-void typecheck_panic_fn(CodeGen *g, TldFn *tld_fn, ZigFn *panic_fn);
-Buf *type_bare_name(ZigType *t);
-Buf *type_h_name(ZigType *t);
-
-LLVMTypeRef get_llvm_type(CodeGen *g, ZigType *type);
-ZigLLVMDIType *get_llvm_di_type(CodeGen *g, ZigType *type);
-
-void add_cc_args(CodeGen *g, ZigList &args, const char *out_dep_path, bool translate_c,
- FileExt source_kind);
-
-void src_assert_impl(bool ok, AstNode *source_node, const char *file, unsigned int line);
-bool is_container(ZigType *type_entry);
-ZigValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *type_entry,
- Buf *type_name, UndefAllowed undef);
-
-void resolve_llvm_types_fn(CodeGen *g, ZigFn *fn);
-bool fn_is_async(ZigFn *fn);
-CallingConvention cc_from_fn_proto(AstNodeFnProto *fn_proto);
-bool is_valid_return_type(ZigType* type);
-bool is_valid_param_type(ZigType* type);
-
-Error type_val_resolve_abi_align(CodeGen *g, AstNode *source_node, ZigValue *type_val, uint32_t *abi_align);
-Error type_val_resolve_abi_size(CodeGen *g, AstNode *source_node, ZigValue *type_val,
- size_t *abi_size, size_t *size_in_bits);
-Error type_val_resolve_zero_bits(CodeGen *g, ZigValue *type_val, ZigType *parent_type,
- ZigValue *parent_type_val, bool *is_zero_bits);
-ZigType *resolve_union_field_type(CodeGen *g, TypeUnionField *union_field);
-ZigType *resolve_struct_field_type(CodeGen *g, TypeStructField *struct_field);
-
-void add_async_error_notes(CodeGen *g, ErrorMsg *msg, ZigFn *fn);
-
-Error analyze_import(CodeGen *codegen, ZigType *source_import, Buf *import_target_str,
- ZigType **out_import, Buf **out_import_target_path, Buf *out_full_path);
-ZigValue *get_the_one_possible_value(CodeGen *g, ZigType *type_entry);
-bool is_anon_container(ZigType *ty);
-void copy_const_val(CodeGen *g, ZigValue *dest, ZigValue *src);
-bool type_has_optional_repr(ZigType *ty);
-bool is_opt_err_set(ZigType *ty);
-bool type_is_numeric(ZigType *ty);
-const char *float_op_to_name(BuiltinFnId op);
-
-#define src_assert(OK, SOURCE_NODE) src_assert_impl((OK), (SOURCE_NODE), __FILE__, __LINE__)
-
-#endif
diff --git a/src/ast_render.cpp b/src/ast_render.cpp
deleted file mode 100644
index ad308bf416a900d791892d94dbb3d09bb6fa0039..0000000000000000000000000000000000000000
--- a/src/ast_render.cpp
+++ /dev/null
@@ -1,1246 +0,0 @@
-/*
- * Copyright (c) 2016 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#include "analyze.hpp"
-#include "ast_render.hpp"
-#include "os.hpp"
-
-#include
-
-static const char *bin_op_str(BinOpType bin_op) {
- switch (bin_op) {
- case BinOpTypeInvalid: return "(invalid)";
- case BinOpTypeBoolOr: return "or";
- case BinOpTypeBoolAnd: return "and";
- case BinOpTypeCmpEq: return "==";
- case BinOpTypeCmpNotEq: return "!=";
- case BinOpTypeCmpLessThan: return "<";
- case BinOpTypeCmpGreaterThan: return ">";
- case BinOpTypeCmpLessOrEq: return "<=";
- case BinOpTypeCmpGreaterOrEq: return ">=";
- case BinOpTypeBinOr: return "|";
- case BinOpTypeBinXor: return "^";
- case BinOpTypeBinAnd: return "&";
- case BinOpTypeBitShiftLeft: return "<<";
- case BinOpTypeBitShiftRight: return ">>";
- case BinOpTypeAdd: return "+";
- case BinOpTypeAddWrap: return "+%";
- case BinOpTypeSub: return "-";
- case BinOpTypeSubWrap: return "-%";
- case BinOpTypeMult: return "*";
- case BinOpTypeMultWrap: return "*%";
- case BinOpTypeDiv: return "/";
- case BinOpTypeMod: return "%";
- case BinOpTypeAssign: return "=";
- case BinOpTypeAssignTimes: return "*=";
- case BinOpTypeAssignTimesWrap: return "*%=";
- case BinOpTypeAssignDiv: return "/=";
- case BinOpTypeAssignMod: return "%=";
- case BinOpTypeAssignPlus: return "+=";
- case BinOpTypeAssignPlusWrap: return "+%=";
- case BinOpTypeAssignMinus: return "-=";
- case BinOpTypeAssignMinusWrap: return "-%=";
- case BinOpTypeAssignBitShiftLeft: return "<<=";
- case BinOpTypeAssignBitShiftRight: return ">>=";
- case BinOpTypeAssignBitAnd: return "&=";
- case BinOpTypeAssignBitXor: return "^=";
- case BinOpTypeAssignBitOr: return "|=";
- case BinOpTypeAssignMergeErrorSets: return "||=";
- case BinOpTypeUnwrapOptional: return "orelse";
- case BinOpTypeArrayCat: return "++";
- case BinOpTypeArrayMult: return "**";
- case BinOpTypeErrorUnion: return "!";
- case BinOpTypeMergeErrorSets: return "||";
- }
- zig_unreachable();
-}
-
-static const char *prefix_op_str(PrefixOp prefix_op) {
- switch (prefix_op) {
- case PrefixOpInvalid: return "(invalid)";
- case PrefixOpNegation: return "-";
- case PrefixOpNegationWrap: return "-%";
- case PrefixOpBoolNot: return "!";
- case PrefixOpBinNot: return "~";
- case PrefixOpOptional: return "?";
- case PrefixOpAddrOf: return "&";
- }
- zig_unreachable();
-}
-
-static const char *visib_mod_string(VisibMod mod) {
- switch (mod) {
- case VisibModPub: return "pub ";
- case VisibModPrivate: return "";
- }
- zig_unreachable();
-}
-
-static const char *return_string(ReturnKind kind) {
- switch (kind) {
- case ReturnKindUnconditional: return "return";
- case ReturnKindError: return "try";
- }
- zig_unreachable();
-}
-
-static const char *defer_string(ReturnKind kind) {
- switch (kind) {
- case ReturnKindUnconditional: return "defer";
- case ReturnKindError: return "errdefer";
- }
- zig_unreachable();
-}
-
-static const char *layout_string(ContainerLayout layout) {
- switch (layout) {
- case ContainerLayoutAuto: return "";
- case ContainerLayoutExtern: return "extern ";
- case ContainerLayoutPacked: return "packed ";
- }
- zig_unreachable();
-}
-
-static const char *extern_string(bool is_extern) {
- return is_extern ? "extern " : "";
-}
-
-static const char *export_string(bool is_export) {
- return is_export ? "export " : "";
-}
-
-//static const char *calling_convention_string(CallingConvention cc) {
-// switch (cc) {
-// case CallingConventionUnspecified: return "";
-// case CallingConventionC: return "extern ";
-// case CallingConventionCold: return "coldcc ";
-// case CallingConventionNaked: return "nakedcc ";
-// case CallingConventionStdcall: return "stdcallcc ";
-// }
-// zig_unreachable();
-//}
-
-static const char *inline_string(FnInline fn_inline) {
- switch (fn_inline) {
- case FnInlineAlways: return "inline ";
- case FnInlineNever: return "noinline ";
- case FnInlineAuto: return "";
- }
- zig_unreachable();
-}
-
-static const char *const_or_var_string(bool is_const) {
- return is_const ? "const" : "var";
-}
-
-static const char *thread_local_string(Token *tok) {
- return (tok == nullptr) ? "" : "threadlocal ";
-}
-
-static const char *token_to_ptr_len_str(Token *tok) {
- assert(tok != nullptr);
- switch (tok->id) {
- case TokenIdStar:
- case TokenIdStarStar:
- return "*";
- case TokenIdLBracket:
- return "[*]";
- case TokenIdSymbol:
- return "[*c]";
- default:
- zig_unreachable();
- }
-}
-
-static const char *node_type_str(NodeType node_type) {
- switch (node_type) {
- case NodeTypeFnDef:
- return "FnDef";
- case NodeTypeFnProto:
- return "FnProto";
- case NodeTypeParamDecl:
- return "ParamDecl";
- case NodeTypeBlock:
- return "Block";
- case NodeTypeGroupedExpr:
- return "Parens";
- case NodeTypeBinOpExpr:
- return "BinOpExpr";
- case NodeTypeCatchExpr:
- return "CatchExpr";
- case NodeTypeFnCallExpr:
- return "FnCallExpr";
- case NodeTypeArrayAccessExpr:
- return "ArrayAccessExpr";
- case NodeTypeSliceExpr:
- return "SliceExpr";
- case NodeTypeReturnExpr:
- return "ReturnExpr";
- case NodeTypeDefer:
- return "Defer";
- case NodeTypeVariableDeclaration:
- return "VariableDeclaration";
- case NodeTypeTestDecl:
- return "TestDecl";
- case NodeTypeIntLiteral:
- return "IntLiteral";
- case NodeTypeFloatLiteral:
- return "FloatLiteral";
- case NodeTypeStringLiteral:
- return "StringLiteral";
- case NodeTypeCharLiteral:
- return "CharLiteral";
- case NodeTypeSymbol:
- return "Symbol";
- case NodeTypePrefixOpExpr:
- return "PrefixOpExpr";
- case NodeTypeUsingNamespace:
- return "UsingNamespace";
- case NodeTypeBoolLiteral:
- return "BoolLiteral";
- case NodeTypeNullLiteral:
- return "NullLiteral";
- case NodeTypeUndefinedLiteral:
- return "UndefinedLiteral";
- case NodeTypeIfBoolExpr:
- return "IfBoolExpr";
- case NodeTypeWhileExpr:
- return "WhileExpr";
- case NodeTypeForExpr:
- return "ForExpr";
- case NodeTypeSwitchExpr:
- return "SwitchExpr";
- case NodeTypeSwitchProng:
- return "SwitchProng";
- case NodeTypeSwitchRange:
- return "SwitchRange";
- case NodeTypeCompTime:
- return "CompTime";
- case NodeTypeNoSuspend:
- return "NoSuspend";
- case NodeTypeBreak:
- return "Break";
- case NodeTypeContinue:
- return "Continue";
- case NodeTypeUnreachable:
- return "Unreachable";
- case NodeTypeAsmExpr:
- return "AsmExpr";
- case NodeTypeFieldAccessExpr:
- return "FieldAccessExpr";
- case NodeTypePtrDeref:
- return "PtrDerefExpr";
- case NodeTypeUnwrapOptional:
- return "UnwrapOptional";
- case NodeTypeContainerDecl:
- return "ContainerDecl";
- case NodeTypeStructField:
- return "StructField";
- case NodeTypeStructValueField:
- return "StructValueField";
- case NodeTypeContainerInitExpr:
- return "ContainerInitExpr";
- case NodeTypeArrayType:
- return "ArrayType";
- case NodeTypeInferredArrayType:
- return "InferredArrayType";
- case NodeTypeErrorType:
- return "ErrorType";
- case NodeTypeIfErrorExpr:
- return "IfErrorExpr";
- case NodeTypeIfOptional:
- return "IfOptional";
- case NodeTypeErrorSetDecl:
- return "ErrorSetDecl";
- case NodeTypeResume:
- return "Resume";
- case NodeTypeAwaitExpr:
- return "AwaitExpr";
- case NodeTypeSuspend:
- return "Suspend";
- case NodeTypePointerType:
- return "PointerType";
- case NodeTypeAnyFrameType:
- return "AnyFrameType";
- case NodeTypeEnumLiteral:
- return "EnumLiteral";
- case NodeTypeErrorSetField:
- return "ErrorSetField";
- case NodeTypeAnyTypeField:
- return "AnyTypeField";
- }
- zig_unreachable();
-}
-
-struct AstPrint {
- int indent;
- FILE *f;
-};
-
-static void ast_print_visit(AstNode **node_ptr, void *context) {
- AstNode *node = *node_ptr;
- AstPrint *ap = (AstPrint *)context;
-
- for (int i = 0; i < ap->indent; i += 1) {
- fprintf(ap->f, " ");
- }
-
- fprintf(ap->f, "%s\n", node_type_str(node->type));
-
- AstPrint new_ap;
- new_ap.indent = ap->indent + 2;
- new_ap.f = ap->f;
-
- ast_visit_node_children(node, ast_print_visit, &new_ap);
-}
-
-void ast_print(FILE *f, AstNode *node, int indent) {
- AstPrint ap;
- ap.indent = indent;
- ap.f = f;
- ast_visit_node_children(node, ast_print_visit, &ap);
-}
-
-
-struct AstRender {
- int indent;
- int indent_size;
- FILE *f;
-};
-
-static void print_indent(AstRender *ar) {
- for (int i = 0; i < ar->indent; i += 1) {
- fprintf(ar->f, " ");
- }
-}
-
-static bool is_alpha_under(uint8_t c) {
- return (c >= 'a' && c <= 'z') ||
- (c >= 'A' && c <= 'Z') || c == '_';
-}
-
-static bool is_digit(uint8_t c) {
- return (c >= '0' && c <= '9');
-}
-
-static bool is_printable(uint8_t c) {
- if (c == 0) {
- return false;
- }
- static const uint8_t printables[] =
- " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.~`!@#$%^&*()_-+=\\{}[];'\"?/<>,:";
- for (size_t i = 0; i < array_length(printables); i += 1) {
- if (c == printables[i]) return true;
- }
- return false;
-}
-
-static void string_literal_escape(Buf *source, Buf *dest) {
- buf_resize(dest, 0);
- for (size_t i = 0; i < buf_len(source); i += 1) {
- uint8_t c = *((uint8_t*)buf_ptr(source) + i);
- if (c == '\'') {
- buf_append_str(dest, "\\'");
- } else if (c == '"') {
- buf_append_str(dest, "\\\"");
- } else if (c == '\\') {
- buf_append_str(dest, "\\\\");
- } else if (c == '\n') {
- buf_append_str(dest, "\\n");
- } else if (c == '\r') {
- buf_append_str(dest, "\\r");
- } else if (c == '\t') {
- buf_append_str(dest, "\\t");
- } else if (is_printable(c)) {
- buf_append_char(dest, c);
- } else {
- buf_appendf(dest, "\\x%02x", (int)c);
- }
- }
-}
-
-static bool is_valid_bare_symbol(Buf *symbol) {
- if (buf_len(symbol) == 0) {
- return false;
- }
- uint8_t first_char = *buf_ptr(symbol);
- if (!is_alpha_under(first_char)) {
- return false;
- }
- for (size_t i = 1; i < buf_len(symbol); i += 1) {
- uint8_t c = *((uint8_t*)buf_ptr(symbol) + i);
- if (!is_alpha_under(c) && !is_digit(c)) {
- return false;
- }
- }
- return true;
-}
-
-static void print_symbol(AstRender *ar, Buf *symbol) {
- if (is_zig_keyword(symbol)) {
- fprintf(ar->f, "@\"%s\"", buf_ptr(symbol));
- return;
- }
- if (is_valid_bare_symbol(symbol)) {
- fprintf(ar->f, "%s", buf_ptr(symbol));
- return;
- }
- Buf escaped = BUF_INIT;
- string_literal_escape(symbol, &escaped);
- fprintf(ar->f, "@\"%s\"", buf_ptr(&escaped));
-}
-
-static bool statement_terminates_without_semicolon(AstNode *node) {
- switch (node->type) {
- case NodeTypeIfBoolExpr:
- if (node->data.if_bool_expr.else_node)
- return statement_terminates_without_semicolon(node->data.if_bool_expr.else_node);
- return node->data.if_bool_expr.then_block->type == NodeTypeBlock;
- case NodeTypeIfErrorExpr:
- if (node->data.if_err_expr.else_node)
- return statement_terminates_without_semicolon(node->data.if_err_expr.else_node);
- return node->data.if_err_expr.then_node->type == NodeTypeBlock;
- case NodeTypeIfOptional:
- if (node->data.test_expr.else_node)
- return statement_terminates_without_semicolon(node->data.test_expr.else_node);
- return node->data.test_expr.then_node->type == NodeTypeBlock;
- case NodeTypeWhileExpr:
- return node->data.while_expr.body->type == NodeTypeBlock;
- case NodeTypeForExpr:
- return node->data.for_expr.body->type == NodeTypeBlock;
- case NodeTypeCompTime:
- return node->data.comptime_expr.expr->type == NodeTypeBlock;
- case NodeTypeDefer:
- return node->data.defer.expr->type == NodeTypeBlock;
- case NodeTypeSuspend:
- return node->data.suspend.block != nullptr && node->data.suspend.block->type == NodeTypeBlock;
- case NodeTypeSwitchExpr:
- case NodeTypeBlock:
- return true;
- default:
- return false;
- }
-}
-
-static void render_node_extra(AstRender *ar, AstNode *node, bool grouped);
-
-static void render_node_grouped(AstRender *ar, AstNode *node) {
- return render_node_extra(ar, node, true);
-}
-
-static void render_node_ungrouped(AstRender *ar, AstNode *node) {
- return render_node_extra(ar, node, false);
-}
-
-static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
- switch (node->type) {
- case NodeTypeSwitchProng:
- case NodeTypeSwitchRange:
- case NodeTypeStructValueField:
- zig_unreachable();
- case NodeTypeFnProto:
- {
- const char *pub_str = visib_mod_string(node->data.fn_proto.visib_mod);
- const char *extern_str = extern_string(node->data.fn_proto.is_extern);
- const char *export_str = export_string(node->data.fn_proto.is_export);
- const char *inline_str = inline_string(node->data.fn_proto.fn_inline);
- fprintf(ar->f, "%s%s%s%sfn ", pub_str, inline_str, export_str, extern_str);
- if (node->data.fn_proto.name != nullptr) {
- print_symbol(ar, node->data.fn_proto.name);
- }
- fprintf(ar->f, "(");
- size_t arg_count = node->data.fn_proto.params.length;
- for (size_t arg_i = 0; arg_i < arg_count; arg_i += 1) {
- AstNode *param_decl = node->data.fn_proto.params.at(arg_i);
- assert(param_decl->type == NodeTypeParamDecl);
- if (param_decl->data.param_decl.name != nullptr) {
- const char *noalias_str = param_decl->data.param_decl.is_noalias ? "noalias " : "";
- const char *inline_str = param_decl->data.param_decl.is_comptime ? "comptime " : "";
- fprintf(ar->f, "%s%s", noalias_str, inline_str);
- print_symbol(ar, param_decl->data.param_decl.name);
- fprintf(ar->f, ": ");
- }
- if (param_decl->data.param_decl.is_var_args) {
- fprintf(ar->f, "...");
- } else if (param_decl->data.param_decl.anytype_token != nullptr) {
- fprintf(ar->f, "anytype");
- } else {
- render_node_grouped(ar, param_decl->data.param_decl.type);
- }
-
- if (arg_i + 1 < arg_count) {
- fprintf(ar->f, ", ");
- }
- }
- if (node->data.fn_proto.is_var_args) {
- fprintf(ar->f, ", ...");
- }
- fprintf(ar->f, ")");
- if (node->data.fn_proto.align_expr) {
- fprintf(ar->f, " align(");
- render_node_grouped(ar, node->data.fn_proto.align_expr);
- fprintf(ar->f, ")");
- }
- if (node->data.fn_proto.section_expr) {
- fprintf(ar->f, " section(");
- render_node_grouped(ar, node->data.fn_proto.section_expr);
- fprintf(ar->f, ")");
- }
- if (node->data.fn_proto.callconv_expr) {
- fprintf(ar->f, " callconv(");
- render_node_grouped(ar, node->data.fn_proto.callconv_expr);
- fprintf(ar->f, ")");
- }
-
- if (node->data.fn_proto.return_anytype_token != nullptr) {
- fprintf(ar->f, "anytype");
- } else {
- AstNode *return_type_node = node->data.fn_proto.return_type;
- assert(return_type_node != nullptr);
- fprintf(ar->f, " ");
- if (node->data.fn_proto.auto_err_set) {
- fprintf(ar->f, "!");
- }
- render_node_grouped(ar, return_type_node);
- }
- break;
- }
- case NodeTypeFnDef:
- {
- render_node_grouped(ar, node->data.fn_def.fn_proto);
- fprintf(ar->f, " ");
- render_node_grouped(ar, node->data.fn_def.body);
- break;
- }
- case NodeTypeBlock:
- if (node->data.block.name != nullptr) {
- fprintf(ar->f, "%s: ", buf_ptr(node->data.block.name));
- }
- if (node->data.block.statements.length == 0) {
- fprintf(ar->f, "{}");
- break;
- }
- fprintf(ar->f, "{\n");
- ar->indent += ar->indent_size;
- for (size_t i = 0; i < node->data.block.statements.length; i += 1) {
- AstNode *statement = node->data.block.statements.at(i);
- print_indent(ar);
- render_node_grouped(ar, statement);
-
- if (!statement_terminates_without_semicolon(statement))
- fprintf(ar->f, ";");
-
- fprintf(ar->f, "\n");
- }
- ar->indent -= ar->indent_size;
- print_indent(ar);
- fprintf(ar->f, "}");
- break;
- case NodeTypeGroupedExpr:
- fprintf(ar->f, "(");
- render_node_ungrouped(ar, node->data.grouped_expr);
- fprintf(ar->f, ")");
- break;
- case NodeTypeReturnExpr:
- {
- const char *return_str = return_string(node->data.return_expr.kind);
- fprintf(ar->f, "%s", return_str);
- if (node->data.return_expr.expr) {
- fprintf(ar->f, " ");
- render_node_grouped(ar, node->data.return_expr.expr);
- }
- break;
- }
- case NodeTypeBreak:
- {
- fprintf(ar->f, "break");
- if (node->data.break_expr.name != nullptr) {
- fprintf(ar->f, " :%s", buf_ptr(node->data.break_expr.name));
- }
- if (node->data.break_expr.expr) {
- fprintf(ar->f, " ");
- render_node_grouped(ar, node->data.break_expr.expr);
- }
- break;
- }
- case NodeTypeDefer:
- {
- const char *defer_str = defer_string(node->data.defer.kind);
- fprintf(ar->f, "%s ", defer_str);
- render_node_grouped(ar, node->data.defer.expr);
- break;
- }
- case NodeTypeVariableDeclaration:
- {
- const char *pub_str = visib_mod_string(node->data.variable_declaration.visib_mod);
- const char *extern_str = extern_string(node->data.variable_declaration.is_extern);
- const char *thread_local_str = thread_local_string(node->data.variable_declaration.threadlocal_tok);
- const char *const_or_var = const_or_var_string(node->data.variable_declaration.is_const);
- fprintf(ar->f, "%s%s%s%s ", pub_str, extern_str, thread_local_str, const_or_var);
- print_symbol(ar, node->data.variable_declaration.symbol);
-
- if (node->data.variable_declaration.type) {
- fprintf(ar->f, ": ");
- render_node_grouped(ar, node->data.variable_declaration.type);
- }
- if (node->data.variable_declaration.align_expr) {
- fprintf(ar->f, "align(");
- render_node_grouped(ar, node->data.variable_declaration.align_expr);
- fprintf(ar->f, ") ");
- }
- if (node->data.variable_declaration.section_expr) {
- fprintf(ar->f, "section(");
- render_node_grouped(ar, node->data.variable_declaration.section_expr);
- fprintf(ar->f, ") ");
- }
- if (node->data.variable_declaration.expr) {
- fprintf(ar->f, " = ");
- render_node_grouped(ar, node->data.variable_declaration.expr);
- }
- break;
- }
- case NodeTypeBinOpExpr:
- if (!grouped) fprintf(ar->f, "(");
- render_node_ungrouped(ar, node->data.bin_op_expr.op1);
- fprintf(ar->f, " %s ", bin_op_str(node->data.bin_op_expr.bin_op));
- render_node_ungrouped(ar, node->data.bin_op_expr.op2);
- if (!grouped) fprintf(ar->f, ")");
- break;
- case NodeTypeFloatLiteral:
- {
- Buf rendered_buf = BUF_INIT;
- buf_resize(&rendered_buf, 0);
- bigfloat_append_buf(&rendered_buf, node->data.float_literal.bigfloat);
- fprintf(ar->f, "%s", buf_ptr(&rendered_buf));
- }
- break;
- case NodeTypeIntLiteral:
- {
- Buf rendered_buf = BUF_INIT;
- buf_resize(&rendered_buf, 0);
- bigint_append_buf(&rendered_buf, node->data.int_literal.bigint, 10);
- fprintf(ar->f, "%s", buf_ptr(&rendered_buf));
- }
- break;
- case NodeTypeStringLiteral:
- {
- Buf tmp_buf = BUF_INIT;
- string_literal_escape(node->data.string_literal.buf, &tmp_buf);
- fprintf(ar->f, "\"%s\"", buf_ptr(&tmp_buf));
- }
- break;
- case NodeTypeCharLiteral:
- {
- uint8_t c = node->data.char_literal.value;
- if (c == '\'') {
- fprintf(ar->f, "'\\''");
- } else if (c == '\"') {
- fprintf(ar->f, "'\\\"'");
- } else if (c == '\\') {
- fprintf(ar->f, "'\\\\'");
- } else if (c == '\n') {
- fprintf(ar->f, "'\\n'");
- } else if (c == '\r') {
- fprintf(ar->f, "'\\r'");
- } else if (c == '\t') {
- fprintf(ar->f, "'\\t'");
- } else if (is_printable(c)) {
- fprintf(ar->f, "'%c'", c);
- } else {
- fprintf(ar->f, "'\\x%02x'", (int)c);
- }
- break;
- }
- case NodeTypeSymbol:
- print_symbol(ar, node->data.symbol_expr.symbol);
- break;
- case NodeTypePrefixOpExpr:
- {
- if (!grouped) fprintf(ar->f, "(");
- PrefixOp op = node->data.prefix_op_expr.prefix_op;
- fprintf(ar->f, "%s", prefix_op_str(op));
-
- AstNode *child_node = node->data.prefix_op_expr.primary_expr;
- bool new_grouped = child_node->type == NodeTypePrefixOpExpr || child_node->type == NodeTypePointerType;
- render_node_extra(ar, child_node, new_grouped);
- if (!grouped) fprintf(ar->f, ")");
- break;
- }
- case NodeTypePointerType:
- {
- if (!grouped) fprintf(ar->f, "(");
- const char *ptr_len_str = token_to_ptr_len_str(node->data.pointer_type.star_token);
- fprintf(ar->f, "%s", ptr_len_str);
- if (node->data.pointer_type.align_expr != nullptr) {
- fprintf(ar->f, "align(");
- render_node_grouped(ar, node->data.pointer_type.align_expr);
- if (node->data.pointer_type.bit_offset_start != nullptr) {
- assert(node->data.pointer_type.host_int_bytes != nullptr);
-
- Buf offset_start_buf = BUF_INIT;
- buf_resize(&offset_start_buf, 0);
- bigint_append_buf(&offset_start_buf, node->data.pointer_type.bit_offset_start, 10);
-
- Buf offset_end_buf = BUF_INIT;
- buf_resize(&offset_end_buf, 0);
- bigint_append_buf(&offset_end_buf, node->data.pointer_type.host_int_bytes, 10);
-
- fprintf(ar->f, ":%s:%s ", buf_ptr(&offset_start_buf), buf_ptr(&offset_end_buf));
- }
- fprintf(ar->f, ") ");
- }
- if (node->data.pointer_type.is_const) {
- fprintf(ar->f, "const ");
- }
- if (node->data.pointer_type.is_volatile) {
- fprintf(ar->f, "volatile ");
- }
-
- render_node_ungrouped(ar, node->data.pointer_type.op_expr);
- if (!grouped) fprintf(ar->f, ")");
- break;
- }
- case NodeTypeFnCallExpr:
- {
- switch (node->data.fn_call_expr.modifier) {
- case CallModifierNone:
- break;
- case CallModifierNoSuspend:
- fprintf(ar->f, "nosuspend ");
- break;
- case CallModifierAsync:
- fprintf(ar->f, "async ");
- break;
- case CallModifierNeverTail:
- fprintf(ar->f, "notail ");
- break;
- case CallModifierNeverInline:
- fprintf(ar->f, "noinline ");
- break;
- case CallModifierAlwaysTail:
- fprintf(ar->f, "tail ");
- break;
- case CallModifierAlwaysInline:
- fprintf(ar->f, "inline ");
- break;
- case CallModifierCompileTime:
- fprintf(ar->f, "comptime ");
- break;
- case CallModifierBuiltin:
- fprintf(ar->f, "@");
- break;
- }
- AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr;
- bool grouped = (fn_ref_node->type != NodeTypePrefixOpExpr && fn_ref_node->type != NodeTypePointerType);
- render_node_extra(ar, fn_ref_node, grouped);
- fprintf(ar->f, "(");
- for (size_t i = 0; i < node->data.fn_call_expr.params.length; i += 1) {
- AstNode *param = node->data.fn_call_expr.params.at(i);
- if (i != 0) {
- fprintf(ar->f, ", ");
- }
- render_node_grouped(ar, param);
- }
- fprintf(ar->f, ")");
- break;
- }
- case NodeTypeArrayAccessExpr:
- render_node_ungrouped(ar, node->data.array_access_expr.array_ref_expr);
- fprintf(ar->f, "[");
- render_node_grouped(ar, node->data.array_access_expr.subscript);
- fprintf(ar->f, "]");
- break;
- case NodeTypeFieldAccessExpr:
- {
- AstNode *lhs = node->data.field_access_expr.struct_expr;
- Buf *rhs = node->data.field_access_expr.field_name;
- if (lhs->type == NodeTypeErrorType) {
- fprintf(ar->f, "error");
- } else {
- render_node_ungrouped(ar, lhs);
- }
- fprintf(ar->f, ".");
- print_symbol(ar, rhs);
- break;
- }
- case NodeTypePtrDeref:
- {
- AstNode *lhs = node->data.ptr_deref_expr.target;
- render_node_ungrouped(ar, lhs);
- fprintf(ar->f, ".*");
- break;
- }
- case NodeTypeUnwrapOptional:
- {
- AstNode *lhs = node->data.unwrap_optional.expr;
- render_node_ungrouped(ar, lhs);
- fprintf(ar->f, ".?");
- break;
- }
- case NodeTypeUndefinedLiteral:
- fprintf(ar->f, "undefined");
- break;
- case NodeTypeContainerDecl:
- {
- if (!node->data.container_decl.is_root) {
- const char *layout_str = layout_string(node->data.container_decl.layout);
- const char *container_str = container_string(node->data.container_decl.kind);
- fprintf(ar->f, "%s%s", layout_str, container_str);
- if (node->data.container_decl.auto_enum) {
- fprintf(ar->f, "(enum");
- }
- if (node->data.container_decl.init_arg_expr != nullptr) {
- fprintf(ar->f, "(");
- render_node_grouped(ar, node->data.container_decl.init_arg_expr);
- fprintf(ar->f, ")");
- }
- if (node->data.container_decl.auto_enum) {
- fprintf(ar->f, ")");
- }
-
- fprintf(ar->f, " {\n");
- ar->indent += ar->indent_size;
- }
- for (size_t field_i = 0; field_i < node->data.container_decl.fields.length; field_i += 1) {
- AstNode *field_node = node->data.container_decl.fields.at(field_i);
- assert(field_node->type == NodeTypeStructField);
- print_indent(ar);
- print_symbol(ar, field_node->data.struct_field.name);
- if (field_node->data.struct_field.type != nullptr) {
- fprintf(ar->f, ": ");
- render_node_grouped(ar, field_node->data.struct_field.type);
- }
- if (field_node->data.struct_field.value != nullptr) {
- fprintf(ar->f, " = ");
- render_node_grouped(ar, field_node->data.struct_field.value);
- }
- fprintf(ar->f, ",\n");
- }
-
- for (size_t decl_i = 0; decl_i < node->data.container_decl.decls.length; decl_i += 1) {
- AstNode *decls_node = node->data.container_decl.decls.at(decl_i);
- render_node_grouped(ar, decls_node);
-
- if (decls_node->type == NodeTypeUsingNamespace ||
- decls_node->type == NodeTypeVariableDeclaration ||
- decls_node->type == NodeTypeFnProto)
- {
- fprintf(ar->f, ";");
- }
- fprintf(ar->f, "\n");
- }
-
- if (!node->data.container_decl.is_root) {
- ar->indent -= ar->indent_size;
- print_indent(ar);
- fprintf(ar->f, "}");
- }
- break;
- }
- case NodeTypeContainerInitExpr:
- if (node->data.container_init_expr.type != nullptr) {
- render_node_ungrouped(ar, node->data.container_init_expr.type);
- }
- if (node->data.container_init_expr.kind == ContainerInitKindStruct) {
- fprintf(ar->f, "{\n");
- ar->indent += ar->indent_size;
- } else {
- fprintf(ar->f, "{");
- }
- for (size_t i = 0; i < node->data.container_init_expr.entries.length; i += 1) {
- AstNode *entry = node->data.container_init_expr.entries.at(i);
- if (entry->type == NodeTypeStructValueField) {
- Buf *name = entry->data.struct_val_field.name;
- AstNode *expr = entry->data.struct_val_field.expr;
- print_indent(ar);
- fprintf(ar->f, ".%s = ", buf_ptr(name));
- render_node_grouped(ar, expr);
- fprintf(ar->f, ",\n");
- } else {
- if (i != 0)
- fprintf(ar->f, ", ");
- render_node_grouped(ar, entry);
- }
- }
- if (node->data.container_init_expr.kind == ContainerInitKindStruct) {
- ar->indent -= ar->indent_size;
- }
- print_indent(ar);
- fprintf(ar->f, "}");
- break;
- case NodeTypeArrayType:
- {
- fprintf(ar->f, "[");
- if (node->data.array_type.size) {
- render_node_grouped(ar, node->data.array_type.size);
- }
- fprintf(ar->f, "]");
- if (node->data.array_type.is_const) {
- fprintf(ar->f, "const ");
- }
- render_node_ungrouped(ar, node->data.array_type.child_type);
- break;
- }
- case NodeTypeInferredArrayType:
- {
- fprintf(ar->f, "[_]");
- render_node_ungrouped(ar, node->data.inferred_array_type.child_type);
- break;
- }
- case NodeTypeAnyFrameType: {
- fprintf(ar->f, "anyframe");
- if (node->data.anyframe_type.payload_type != nullptr) {
- fprintf(ar->f, "->");
- render_node_grouped(ar, node->data.anyframe_type.payload_type);
- }
- break;
- }
- case NodeTypeErrorType:
- fprintf(ar->f, "anyerror");
- break;
- case NodeTypeAsmExpr:
- {
- AstNodeAsmExpr *asm_expr = &node->data.asm_expr;
- const char *volatile_str = (asm_expr->volatile_token != nullptr) ? " volatile" : "";
- fprintf(ar->f, "asm%s (", volatile_str);
- render_node_ungrouped(ar, asm_expr->asm_template);
- fprintf(ar->f, ")");
- print_indent(ar);
- fprintf(ar->f, ": ");
- for (size_t i = 0; i < asm_expr->output_list.length; i += 1) {
- AsmOutput *asm_output = asm_expr->output_list.at(i);
-
- if (i != 0) {
- fprintf(ar->f, ",\n");
- print_indent(ar);
- }
-
- fprintf(ar->f, "[%s] \"%s\" (",
- buf_ptr(asm_output->asm_symbolic_name),
- buf_ptr(asm_output->constraint));
- if (asm_output->return_type) {
- fprintf(ar->f, "-> ");
- render_node_grouped(ar, asm_output->return_type);
- } else {
- fprintf(ar->f, "%s", buf_ptr(asm_output->variable_name));
- }
- fprintf(ar->f, ")");
- }
- fprintf(ar->f, "\n");
- print_indent(ar);
- fprintf(ar->f, ": ");
- for (size_t i = 0; i < asm_expr->input_list.length; i += 1) {
- AsmInput *asm_input = asm_expr->input_list.at(i);
-
- if (i != 0) {
- fprintf(ar->f, ",\n");
- print_indent(ar);
- }
-
- fprintf(ar->f, "[%s] \"%s\" (",
- buf_ptr(asm_input->asm_symbolic_name),
- buf_ptr(asm_input->constraint));
- render_node_grouped(ar, asm_input->expr);
- fprintf(ar->f, ")");
- }
- fprintf(ar->f, "\n");
- print_indent(ar);
- fprintf(ar->f, ": ");
- for (size_t i = 0; i < asm_expr->clobber_list.length; i += 1) {
- Buf *reg_name = asm_expr->clobber_list.at(i);
- if (i != 0) fprintf(ar->f, ", ");
- fprintf(ar->f, "\"%s\"", buf_ptr(reg_name));
- }
- fprintf(ar->f, ")");
- break;
- }
- case NodeTypeWhileExpr:
- {
- if (node->data.while_expr.name != nullptr) {
- fprintf(ar->f, "%s: ", buf_ptr(node->data.while_expr.name));
- }
- const char *inline_str = node->data.while_expr.is_inline ? "inline " : "";
- fprintf(ar->f, "%swhile (", inline_str);
- render_node_grouped(ar, node->data.while_expr.condition);
- fprintf(ar->f, ") ");
- if (node->data.while_expr.var_symbol) {
- fprintf(ar->f, "|%s| ", buf_ptr(node->data.while_expr.var_symbol));
- }
- if (node->data.while_expr.continue_expr) {
- fprintf(ar->f, ": (");
- render_node_grouped(ar, node->data.while_expr.continue_expr);
- fprintf(ar->f, ") ");
- }
- render_node_grouped(ar, node->data.while_expr.body);
- if (node->data.while_expr.else_node) {
- fprintf(ar->f, " else ");
- if (node->data.while_expr.err_symbol) {
- fprintf(ar->f, "|%s| ", buf_ptr(node->data.while_expr.err_symbol));
- }
- render_node_grouped(ar, node->data.while_expr.else_node);
- }
- break;
- }
- case NodeTypeBoolLiteral:
- {
- const char *bool_str = node->data.bool_literal.value ? "true" : "false";
- fprintf(ar->f, "%s", bool_str);
- break;
- }
- case NodeTypeIfBoolExpr:
- {
- fprintf(ar->f, "if (");
- render_node_grouped(ar, node->data.if_bool_expr.condition);
- fprintf(ar->f, ") ");
- render_node_grouped(ar, node->data.if_bool_expr.then_block);
- if (node->data.if_bool_expr.else_node) {
- fprintf(ar->f, " else ");
- render_node_grouped(ar, node->data.if_bool_expr.else_node);
- }
- break;
- }
- case NodeTypeNullLiteral:
- {
- fprintf(ar->f, "null");
- break;
- }
- case NodeTypeIfErrorExpr:
- {
- fprintf(ar->f, "if (");
- render_node_grouped(ar, node->data.if_err_expr.target_node);
- fprintf(ar->f, ") ");
- if (node->data.if_err_expr.var_symbol) {
- const char *ptr_str = node->data.if_err_expr.var_is_ptr ? "*" : "";
- const char *var_name = buf_ptr(node->data.if_err_expr.var_symbol);
- fprintf(ar->f, "|%s%s| ", ptr_str, var_name);
- }
- render_node_grouped(ar, node->data.if_err_expr.then_node);
- if (node->data.if_err_expr.else_node) {
- fprintf(ar->f, " else ");
- if (node->data.if_err_expr.err_symbol) {
- fprintf(ar->f, "|%s| ", buf_ptr(node->data.if_err_expr.err_symbol));
- }
- render_node_grouped(ar, node->data.if_err_expr.else_node);
- }
- break;
- }
- case NodeTypeIfOptional:
- {
- fprintf(ar->f, "if (");
- render_node_grouped(ar, node->data.test_expr.target_node);
- fprintf(ar->f, ") ");
- if (node->data.test_expr.var_symbol) {
- const char *ptr_str = node->data.test_expr.var_is_ptr ? "*" : "";
- const char *var_name = buf_ptr(node->data.test_expr.var_symbol);
- fprintf(ar->f, "|%s%s| ", ptr_str, var_name);
- }
- render_node_grouped(ar, node->data.test_expr.then_node);
- if (node->data.test_expr.else_node) {
- fprintf(ar->f, " else ");
- render_node_grouped(ar, node->data.test_expr.else_node);
- }
- break;
- }
- case NodeTypeSwitchExpr:
- {
- AstNodeSwitchExpr *switch_expr = &node->data.switch_expr;
- fprintf(ar->f, "switch (");
- render_node_grouped(ar, switch_expr->expr);
- fprintf(ar->f, ") {\n");
- ar->indent += ar->indent_size;
-
- for (size_t prong_i = 0; prong_i < switch_expr->prongs.length; prong_i += 1) {
- AstNode *prong_node = switch_expr->prongs.at(prong_i);
- AstNodeSwitchProng *switch_prong = &prong_node->data.switch_prong;
- print_indent(ar);
- for (size_t item_i = 0; item_i < switch_prong->items.length; item_i += 1) {
- AstNode *item_node = switch_prong->items.at(item_i);
- if (item_i != 0)
- fprintf(ar->f, ", ");
- if (item_node->type == NodeTypeSwitchRange) {
- AstNode *start_node = item_node->data.switch_range.start;
- AstNode *end_node = item_node->data.switch_range.end;
- render_node_grouped(ar, start_node);
- fprintf(ar->f, "...");
- render_node_grouped(ar, end_node);
- } else {
- render_node_grouped(ar, item_node);
- }
- }
- const char *else_str = (switch_prong->items.length == 0) ? "else" : "";
- fprintf(ar->f, "%s => ", else_str);
- if (switch_prong->var_symbol) {
- const char *star_str = switch_prong->var_is_ptr ? "*" : "";
- Buf *var_name = switch_prong->var_symbol->data.symbol_expr.symbol;
- fprintf(ar->f, "|%s%s| ", star_str, buf_ptr(var_name));
- }
- render_node_grouped(ar, switch_prong->expr);
- fprintf(ar->f, ",\n");
- }
-
- ar->indent -= ar->indent_size;
- print_indent(ar);
- fprintf(ar->f, "}");
- break;
- }
- case NodeTypeCompTime:
- {
- fprintf(ar->f, "comptime ");
- render_node_grouped(ar, node->data.comptime_expr.expr);
- break;
- }
- case NodeTypeNoSuspend:
- {
- fprintf(ar->f, "nosuspend ");
- render_node_grouped(ar, node->data.nosuspend_expr.expr);
- break;
- }
- case NodeTypeForExpr:
- {
- if (node->data.for_expr.name != nullptr) {
- fprintf(ar->f, "%s: ", buf_ptr(node->data.for_expr.name));
- }
- const char *inline_str = node->data.for_expr.is_inline ? "inline " : "";
- fprintf(ar->f, "%sfor (", inline_str);
- render_node_grouped(ar, node->data.for_expr.array_expr);
- fprintf(ar->f, ") ");
- if (node->data.for_expr.elem_node) {
- fprintf(ar->f, "|");
- if (node->data.for_expr.elem_is_ptr)
- fprintf(ar->f, "*");
- render_node_grouped(ar, node->data.for_expr.elem_node);
- if (node->data.for_expr.index_node) {
- fprintf(ar->f, ", ");
- render_node_grouped(ar, node->data.for_expr.index_node);
- }
- fprintf(ar->f, "| ");
- }
- render_node_grouped(ar, node->data.for_expr.body);
- if (node->data.for_expr.else_node) {
- fprintf(ar->f, " else");
- render_node_grouped(ar, node->data.for_expr.else_node);
- }
- break;
- }
- case NodeTypeContinue:
- {
- fprintf(ar->f, "continue");
- if (node->data.continue_expr.name != nullptr) {
- fprintf(ar->f, " :%s", buf_ptr(node->data.continue_expr.name));
- }
- break;
- }
- case NodeTypeUnreachable:
- {
- fprintf(ar->f, "unreachable");
- break;
- }
- case NodeTypeSliceExpr:
- {
- render_node_ungrouped(ar, node->data.slice_expr.array_ref_expr);
- fprintf(ar->f, "[");
- render_node_grouped(ar, node->data.slice_expr.start);
- fprintf(ar->f, "..");
- if (node->data.slice_expr.end)
- render_node_grouped(ar, node->data.slice_expr.end);
- fprintf(ar->f, "]");
- break;
- }
- case NodeTypeCatchExpr:
- {
- render_node_ungrouped(ar, node->data.unwrap_err_expr.op1);
- fprintf(ar->f, " catch ");
- if (node->data.unwrap_err_expr.symbol) {
- Buf *var_name = node->data.unwrap_err_expr.symbol->data.symbol_expr.symbol;
- fprintf(ar->f, "|%s| ", buf_ptr(var_name));
- }
- render_node_ungrouped(ar, node->data.unwrap_err_expr.op2);
- break;
- }
- case NodeTypeErrorSetDecl:
- {
- fprintf(ar->f, "error {\n");
- ar->indent += ar->indent_size;
-
- for (size_t i = 0; i < node->data.err_set_decl.decls.length; i += 1) {
- AstNode *field_node = node->data.err_set_decl.decls.at(i);
- switch (field_node->type) {
- case NodeTypeSymbol:
- print_indent(ar);
- print_symbol(ar, field_node->data.symbol_expr.symbol);
- fprintf(ar->f, ",\n");
- break;
- case NodeTypeErrorSetField:
- print_indent(ar);
- print_symbol(ar, field_node->data.err_set_field.field_name->data.symbol_expr.symbol);
- fprintf(ar->f, ",\n");
- break;
- default:
- zig_unreachable();
- }
- }
-
- ar->indent -= ar->indent_size;
- print_indent(ar);
- fprintf(ar->f, "}");
- break;
- }
- case NodeTypeResume:
- {
- fprintf(ar->f, "resume ");
- render_node_grouped(ar, node->data.resume_expr.expr);
- break;
- }
- case NodeTypeAwaitExpr:
- {
- fprintf(ar->f, "await ");
- render_node_grouped(ar, node->data.await_expr.expr);
- break;
- }
- case NodeTypeSuspend:
- {
- if (node->data.suspend.block != nullptr) {
- fprintf(ar->f, "suspend ");
- render_node_grouped(ar, node->data.suspend.block);
- } else {
- fprintf(ar->f, "suspend\n");
- }
- break;
- }
- case NodeTypeEnumLiteral:
- {
- fprintf(ar->f, ".%s", buf_ptr(&node->data.enum_literal.identifier->data.str_lit.str));
- break;
- }
- case NodeTypeAnyTypeField: {
- fprintf(ar->f, "anytype");
- break;
- }
- case NodeTypeParamDecl:
- case NodeTypeTestDecl:
- case NodeTypeStructField:
- case NodeTypeUsingNamespace:
- case NodeTypeErrorSetField:
- zig_panic("TODO more ast rendering");
- }
-}
-
-
-void ast_render(FILE *f, AstNode *node, int indent_size) {
- AstRender ar = {0};
- ar.f = f;
- ar.indent_size = indent_size;
- ar.indent = 0;
-
- render_node_grouped(&ar, node);
-}
-
-void AstNode::src() {
- fprintf(stderr, "%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize "\n",
- buf_ptr(this->owner->data.structure.root_struct->path),
- this->line + 1, this->column + 1);
-}
diff --git a/src/ast_render.hpp b/src/ast_render.hpp
deleted file mode 100644
index cf70b04694403b2d508df7d22a8f8fe0f1eb7752..0000000000000000000000000000000000000000
--- a/src/ast_render.hpp
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (c) 2015 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#ifndef ZIG_AST_RENDER_HPP
-#define ZIG_AST_RENDER_HPP
-
-#include "all_types.hpp"
-#include "parser.hpp"
-
-#include
-
-void ast_print(FILE *f, AstNode *node, int indent);
-
-void ast_render(FILE *f, AstNode *node, int indent_size);
-
-#endif
diff --git a/src/astgen.zig b/src/astgen.zig
new file mode 100644
index 0000000000000000000000000000000000000000..2c091a86eccd3cc157cb6fcbb8c2dce3e7473fd0
--- /dev/null
+++ b/src/astgen.zig
@@ -0,0 +1,2396 @@
+const std = @import("std");
+const mem = std.mem;
+const Allocator = std.mem.Allocator;
+const Value = @import("value.zig").Value;
+const Type = @import("type.zig").Type;
+const TypedValue = @import("TypedValue.zig");
+const assert = std.debug.assert;
+const zir = @import("zir.zig");
+const Module = @import("Module.zig");
+const ast = std.zig.ast;
+const trace = @import("tracy.zig").trace;
+const Scope = Module.Scope;
+const InnerError = Module.InnerError;
+
+pub const ResultLoc = union(enum) {
+ /// The expression is the right-hand side of assignment to `_`. Only the side-effects of the
+ /// expression should be generated.
+ discard,
+ /// The expression has an inferred type, and it will be evaluated as an rvalue.
+ none,
+ /// The expression must generate a pointer rather than a value. For example, the left hand side
+ /// of an assignment uses this kind of result location.
+ ref,
+ /// The expression will be type coerced into this type, but it will be evaluated as an rvalue.
+ ty: *zir.Inst,
+ /// The expression must store its result into this typed pointer.
+ ptr: *zir.Inst,
+ /// The expression must store its result into this allocation, which has an inferred type.
+ inferred_ptr: *zir.Inst.Tag.alloc_inferred.Type(),
+ /// The expression must store its result into this pointer, which is a typed pointer that
+ /// has been bitcasted to whatever the expression's type is.
+ bitcasted_ptr: *zir.Inst.UnOp,
+ /// There is a pointer for the expression to store its result into, however, its type
+ /// is inferred based on peer type resolution for a `zir.Inst.Block`.
+ block_ptr: *zir.Inst.Block,
+};
+
+pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*zir.Inst {
+ const type_src = scope.tree().token_locs[type_node.firstToken()].start;
+ const type_type = try addZIRInstConst(mod, scope, type_src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.type_type),
+ });
+ const type_rl: ResultLoc = .{ .ty = type_type };
+ return expr(mod, scope, type_rl, type_node);
+}
+
+fn lvalExpr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst {
+ switch (node.tag) {
+ .Root => unreachable,
+ .Use => unreachable,
+ .TestDecl => unreachable,
+ .DocComment => unreachable,
+ .VarDecl => unreachable,
+ .SwitchCase => unreachable,
+ .SwitchElse => unreachable,
+ .Else => unreachable,
+ .Payload => unreachable,
+ .PointerPayload => unreachable,
+ .PointerIndexPayload => unreachable,
+ .ErrorTag => unreachable,
+ .FieldInitializer => unreachable,
+ .ContainerField => unreachable,
+
+ .Assign,
+ .AssignBitAnd,
+ .AssignBitOr,
+ .AssignBitShiftLeft,
+ .AssignBitShiftRight,
+ .AssignBitXor,
+ .AssignDiv,
+ .AssignSub,
+ .AssignSubWrap,
+ .AssignMod,
+ .AssignAdd,
+ .AssignAddWrap,
+ .AssignMul,
+ .AssignMulWrap,
+ .Add,
+ .AddWrap,
+ .Sub,
+ .SubWrap,
+ .Mul,
+ .MulWrap,
+ .Div,
+ .Mod,
+ .BitAnd,
+ .BitOr,
+ .BitShiftLeft,
+ .BitShiftRight,
+ .BitXor,
+ .BangEqual,
+ .EqualEqual,
+ .GreaterThan,
+ .GreaterOrEqual,
+ .LessThan,
+ .LessOrEqual,
+ .ArrayCat,
+ .ArrayMult,
+ .BoolAnd,
+ .BoolOr,
+ .Asm,
+ .StringLiteral,
+ .IntegerLiteral,
+ .Call,
+ .Unreachable,
+ .Return,
+ .If,
+ .While,
+ .BoolNot,
+ .AddressOf,
+ .FloatLiteral,
+ .UndefinedLiteral,
+ .BoolLiteral,
+ .NullLiteral,
+ .OptionalType,
+ .Block,
+ .LabeledBlock,
+ .Break,
+ .PtrType,
+ .GroupedExpression,
+ .ArrayType,
+ .ArrayTypeSentinel,
+ .EnumLiteral,
+ .MultilineStringLiteral,
+ .CharLiteral,
+ .Defer,
+ .Catch,
+ .ErrorUnion,
+ .MergeErrorSets,
+ .Range,
+ .OrElse,
+ .Await,
+ .BitNot,
+ .Negation,
+ .NegationWrap,
+ .Resume,
+ .Try,
+ .SliceType,
+ .Slice,
+ .ArrayInitializer,
+ .ArrayInitializerDot,
+ .StructInitializer,
+ .StructInitializerDot,
+ .Switch,
+ .For,
+ .Suspend,
+ .Continue,
+ .AnyType,
+ .ErrorType,
+ .FnProto,
+ .AnyFrameType,
+ .ErrorSetDecl,
+ .ContainerDecl,
+ .Comptime,
+ .Nosuspend,
+ => return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}),
+
+ // @field can be assigned to
+ .BuiltinCall => {
+ const call = node.castTag(.BuiltinCall).?;
+ const tree = scope.tree();
+ const builtin_name = tree.tokenSlice(call.builtin_token);
+
+ if (!mem.eql(u8, builtin_name, "@field")) {
+ return mod.failNode(scope, node, "invalid left-hand side to assignment", .{});
+ }
+ },
+
+ // can be assigned to
+ .UnwrapOptional, .Deref, .Period, .ArrayAccess, .Identifier => {},
+ }
+ return expr(mod, scope, .ref, node);
+}
+
+/// Turn Zig AST into untyped ZIR istructions.
+pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerError!*zir.Inst {
+ switch (node.tag) {
+ .Root => unreachable, // Top-level declaration.
+ .Use => unreachable, // Top-level declaration.
+ .TestDecl => unreachable, // Top-level declaration.
+ .DocComment => unreachable, // Top-level declaration.
+ .VarDecl => unreachable, // Handled in `blockExpr`.
+ .SwitchCase => unreachable, // Handled in `switchExpr`.
+ .SwitchElse => unreachable, // Handled in `switchExpr`.
+ .Else => unreachable, // Handled explicitly the control flow expression functions.
+ .Payload => unreachable, // Handled explicitly.
+ .PointerPayload => unreachable, // Handled explicitly.
+ .PointerIndexPayload => unreachable, // Handled explicitly.
+ .ErrorTag => unreachable, // Handled explicitly.
+ .FieldInitializer => unreachable, // Handled explicitly.
+ .ContainerField => unreachable, // Handled explicitly.
+
+ .Assign => return rlWrapVoid(mod, scope, rl, node, try assign(mod, scope, node.castTag(.Assign).?)),
+ .AssignBitAnd => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitAnd).?, .bitand)),
+ .AssignBitOr => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitOr).?, .bitor)),
+ .AssignBitShiftLeft => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitShiftLeft).?, .shl)),
+ .AssignBitShiftRight => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitShiftRight).?, .shr)),
+ .AssignBitXor => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitXor).?, .xor)),
+ .AssignDiv => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignDiv).?, .div)),
+ .AssignSub => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignSub).?, .sub)),
+ .AssignSubWrap => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignSubWrap).?, .subwrap)),
+ .AssignMod => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMod).?, .mod_rem)),
+ .AssignAdd => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignAdd).?, .add)),
+ .AssignAddWrap => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignAddWrap).?, .addwrap)),
+ .AssignMul => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMul).?, .mul)),
+ .AssignMulWrap => return rlWrapVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMulWrap).?, .mulwrap)),
+
+ .Add => return simpleBinOp(mod, scope, rl, node.castTag(.Add).?, .add),
+ .AddWrap => return simpleBinOp(mod, scope, rl, node.castTag(.AddWrap).?, .addwrap),
+ .Sub => return simpleBinOp(mod, scope, rl, node.castTag(.Sub).?, .sub),
+ .SubWrap => return simpleBinOp(mod, scope, rl, node.castTag(.SubWrap).?, .subwrap),
+ .Mul => return simpleBinOp(mod, scope, rl, node.castTag(.Mul).?, .mul),
+ .MulWrap => return simpleBinOp(mod, scope, rl, node.castTag(.MulWrap).?, .mulwrap),
+ .Div => return simpleBinOp(mod, scope, rl, node.castTag(.Div).?, .div),
+ .Mod => return simpleBinOp(mod, scope, rl, node.castTag(.Mod).?, .mod_rem),
+ .BitAnd => return simpleBinOp(mod, scope, rl, node.castTag(.BitAnd).?, .bitand),
+ .BitOr => return simpleBinOp(mod, scope, rl, node.castTag(.BitOr).?, .bitor),
+ .BitShiftLeft => return simpleBinOp(mod, scope, rl, node.castTag(.BitShiftLeft).?, .shl),
+ .BitShiftRight => return simpleBinOp(mod, scope, rl, node.castTag(.BitShiftRight).?, .shr),
+ .BitXor => return simpleBinOp(mod, scope, rl, node.castTag(.BitXor).?, .xor),
+
+ .BangEqual => return simpleBinOp(mod, scope, rl, node.castTag(.BangEqual).?, .cmp_neq),
+ .EqualEqual => return simpleBinOp(mod, scope, rl, node.castTag(.EqualEqual).?, .cmp_eq),
+ .GreaterThan => return simpleBinOp(mod, scope, rl, node.castTag(.GreaterThan).?, .cmp_gt),
+ .GreaterOrEqual => return simpleBinOp(mod, scope, rl, node.castTag(.GreaterOrEqual).?, .cmp_gte),
+ .LessThan => return simpleBinOp(mod, scope, rl, node.castTag(.LessThan).?, .cmp_lt),
+ .LessOrEqual => return simpleBinOp(mod, scope, rl, node.castTag(.LessOrEqual).?, .cmp_lte),
+
+ .ArrayCat => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayCat).?, .array_cat),
+ .ArrayMult => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayMult).?, .array_mul),
+
+ .BoolAnd => return boolBinOp(mod, scope, rl, node.castTag(.BoolAnd).?),
+ .BoolOr => return boolBinOp(mod, scope, rl, node.castTag(.BoolOr).?),
+
+ .BoolNot => return rlWrap(mod, scope, rl, try boolNot(mod, scope, node.castTag(.BoolNot).?)),
+ .BitNot => return rlWrap(mod, scope, rl, try bitNot(mod, scope, node.castTag(.BitNot).?)),
+ .Negation => return rlWrap(mod, scope, rl, try negation(mod, scope, node.castTag(.Negation).?, .sub)),
+ .NegationWrap => return rlWrap(mod, scope, rl, try negation(mod, scope, node.castTag(.NegationWrap).?, .subwrap)),
+
+ .Identifier => return try identifier(mod, scope, rl, node.castTag(.Identifier).?),
+ .Asm => return rlWrap(mod, scope, rl, try assembly(mod, scope, node.castTag(.Asm).?)),
+ .StringLiteral => return rlWrap(mod, scope, rl, try stringLiteral(mod, scope, node.castTag(.StringLiteral).?)),
+ .IntegerLiteral => return rlWrap(mod, scope, rl, try integerLiteral(mod, scope, node.castTag(.IntegerLiteral).?)),
+ .BuiltinCall => return builtinCall(mod, scope, rl, node.castTag(.BuiltinCall).?),
+ .Call => return callExpr(mod, scope, rl, node.castTag(.Call).?),
+ .Unreachable => return unreach(mod, scope, node.castTag(.Unreachable).?),
+ .Return => return ret(mod, scope, node.castTag(.Return).?),
+ .If => return ifExpr(mod, scope, rl, node.castTag(.If).?),
+ .While => return whileExpr(mod, scope, rl, node.castTag(.While).?),
+ .Period => return field(mod, scope, rl, node.castTag(.Period).?),
+ .Deref => return rlWrap(mod, scope, rl, try deref(mod, scope, node.castTag(.Deref).?)),
+ .AddressOf => return rlWrap(mod, scope, rl, try addressOf(mod, scope, node.castTag(.AddressOf).?)),
+ .FloatLiteral => return rlWrap(mod, scope, rl, try floatLiteral(mod, scope, node.castTag(.FloatLiteral).?)),
+ .UndefinedLiteral => return rlWrap(mod, scope, rl, try undefLiteral(mod, scope, node.castTag(.UndefinedLiteral).?)),
+ .BoolLiteral => return rlWrap(mod, scope, rl, try boolLiteral(mod, scope, node.castTag(.BoolLiteral).?)),
+ .NullLiteral => return rlWrap(mod, scope, rl, try nullLiteral(mod, scope, node.castTag(.NullLiteral).?)),
+ .OptionalType => return rlWrap(mod, scope, rl, try optionalType(mod, scope, node.castTag(.OptionalType).?)),
+ .UnwrapOptional => return unwrapOptional(mod, scope, rl, node.castTag(.UnwrapOptional).?),
+ .Block => return rlWrapVoid(mod, scope, rl, node, try blockExpr(mod, scope, node.castTag(.Block).?)),
+ .LabeledBlock => return labeledBlockExpr(mod, scope, rl, node.castTag(.LabeledBlock).?, .block),
+ .Break => return rlWrap(mod, scope, rl, try breakExpr(mod, scope, node.castTag(.Break).?)),
+ .PtrType => return rlWrap(mod, scope, rl, try ptrType(mod, scope, node.castTag(.PtrType).?)),
+ .GroupedExpression => return expr(mod, scope, rl, node.castTag(.GroupedExpression).?.expr),
+ .ArrayType => return rlWrap(mod, scope, rl, try arrayType(mod, scope, node.castTag(.ArrayType).?)),
+ .ArrayTypeSentinel => return rlWrap(mod, scope, rl, try arrayTypeSentinel(mod, scope, node.castTag(.ArrayTypeSentinel).?)),
+ .EnumLiteral => return rlWrap(mod, scope, rl, try enumLiteral(mod, scope, node.castTag(.EnumLiteral).?)),
+ .MultilineStringLiteral => return rlWrap(mod, scope, rl, try multilineStrLiteral(mod, scope, node.castTag(.MultilineStringLiteral).?)),
+ .CharLiteral => return rlWrap(mod, scope, rl, try charLiteral(mod, scope, node.castTag(.CharLiteral).?)),
+ .SliceType => return rlWrap(mod, scope, rl, try sliceType(mod, scope, node.castTag(.SliceType).?)),
+ .ErrorUnion => return rlWrap(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.ErrorUnion).?, .error_union_type)),
+ .MergeErrorSets => return rlWrap(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.MergeErrorSets).?, .merge_error_sets)),
+ .AnyFrameType => return rlWrap(mod, scope, rl, try anyFrameType(mod, scope, node.castTag(.AnyFrameType).?)),
+ .ErrorSetDecl => return errorSetDecl(mod, scope, rl, node.castTag(.ErrorSetDecl).?),
+ .ErrorType => return rlWrap(mod, scope, rl, try errorType(mod, scope, node.castTag(.ErrorType).?)),
+ .For => return forExpr(mod, scope, rl, node.castTag(.For).?),
+ .ArrayAccess => return arrayAccess(mod, scope, rl, node.castTag(.ArrayAccess).?),
+ .Slice => return rlWrap(mod, scope, rl, try sliceExpr(mod, scope, node.castTag(.Slice).?)),
+ .Catch => return catchExpr(mod, scope, rl, node.castTag(.Catch).?),
+ .Comptime => return comptimeKeyword(mod, scope, rl, node.castTag(.Comptime).?),
+ .OrElse => return orelseExpr(mod, scope, rl, node.castTag(.OrElse).?),
+
+ .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}),
+ .Range => return mod.failNode(scope, node, "TODO implement astgen.expr for .Range", .{}),
+ .Await => return mod.failNode(scope, node, "TODO implement astgen.expr for .Await", .{}),
+ .Resume => return mod.failNode(scope, node, "TODO implement astgen.expr for .Resume", .{}),
+ .Try => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}),
+ .ArrayInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializer", .{}),
+ .ArrayInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializerDot", .{}),
+ .StructInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializer", .{}),
+ .StructInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializerDot", .{}),
+ .Switch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Switch", .{}),
+ .Suspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Suspend", .{}),
+ .Continue => return mod.failNode(scope, node, "TODO implement astgen.expr for .Continue", .{}),
+ .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}),
+ .FnProto => return mod.failNode(scope, node, "TODO implement astgen.expr for .FnProto", .{}),
+ .ContainerDecl => return mod.failNode(scope, node, "TODO implement astgen.expr for .ContainerDecl", .{}),
+ .Nosuspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Nosuspend", .{}),
+ }
+}
+
+fn comptimeKeyword(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Comptime) InnerError!*zir.Inst {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ return comptimeExpr(mod, scope, rl, node.expr);
+}
+
+pub fn comptimeExpr(mod: *Module, parent_scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerError!*zir.Inst {
+ const tree = parent_scope.tree();
+ const src = tree.token_locs[node.firstToken()].start;
+
+ // Optimization for labeled blocks: don't need to have 2 layers of blocks, we can reuse the existing one.
+ if (node.castTag(.LabeledBlock)) |block_node| {
+ return labeledBlockExpr(mod, parent_scope, rl, block_node, .block_comptime);
+ }
+
+ // Make a scope to collect generated instructions in the sub-expression.
+ var block_scope: Scope.GenZIR = .{
+ .parent = parent_scope,
+ .decl = parent_scope.decl().?,
+ .arena = parent_scope.arena(),
+ .instructions = .{},
+ };
+ defer block_scope.instructions.deinit(mod.gpa);
+
+ // No need to capture the result here because block_comptime_flat implies that the final
+ // instruction is the block's result value.
+ _ = try expr(mod, &block_scope.base, rl, node);
+
+ const block = try addZIRInstBlock(mod, parent_scope, src, .block_comptime_flat, .{
+ .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
+ });
+
+ return &block.base;
+}
+
+fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {
+ const tree = parent_scope.tree();
+ const src = tree.token_locs[node.ltoken].start;
+
+ if (node.getLabel()) |break_label| {
+ // Look for the label in the scope.
+ var scope = parent_scope;
+ while (true) {
+ switch (scope.tag) {
+ .gen_zir => {
+ const gen_zir = scope.cast(Scope.GenZIR).?;
+ if (gen_zir.label) |label| {
+ if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) {
+ if (node.getRHS()) |rhs| {
+ // Most result location types can be forwarded directly; however
+ // if we need to write to a pointer which has an inferred type,
+ // proper type inference requires peer type resolution on the block's
+ // break operand expressions.
+ const branch_rl: ResultLoc = switch (label.result_loc) {
+ .discard, .none, .ty, .ptr, .ref => label.result_loc,
+ .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = label.block_inst },
+ };
+ const operand = try expr(mod, parent_scope, branch_rl, rhs);
+ return try addZIRInst(mod, scope, src, zir.Inst.Break, .{
+ .block = label.block_inst,
+ .operand = operand,
+ }, .{});
+ } else {
+ return try addZIRInst(mod, scope, src, zir.Inst.BreakVoid, .{
+ .block = label.block_inst,
+ }, .{});
+ }
+ }
+ }
+ scope = gen_zir.parent;
+ },
+ .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
+ .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
+ else => {
+ const label_name = try identifierTokenString(mod, parent_scope, break_label);
+ return mod.failTok(parent_scope, break_label, "label not found: '{}'", .{label_name});
+ },
+ }
+ }
+ } else {
+ return mod.failNode(parent_scope, &node.base, "TODO implement break from loop", .{});
+ }
+}
+
+pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block) InnerError!void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ try blockExprStmts(mod, parent_scope, &block_node.base, block_node.statements());
+}
+
+fn labeledBlockExpr(
+ mod: *Module,
+ parent_scope: *Scope,
+ rl: ResultLoc,
+ block_node: *ast.Node.LabeledBlock,
+ zir_tag: zir.Inst.Tag,
+) InnerError!*zir.Inst {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ assert(zir_tag == .block or zir_tag == .block_comptime);
+
+ const tree = parent_scope.tree();
+ const src = tree.token_locs[block_node.lbrace].start;
+
+ // Create the Block ZIR instruction so that we can put it into the GenZIR struct
+ // so that break statements can reference it.
+ const gen_zir = parent_scope.getGenZIR();
+ const block_inst = try gen_zir.arena.create(zir.Inst.Block);
+ block_inst.* = .{
+ .base = .{
+ .tag = zir_tag,
+ .src = src,
+ },
+ .positionals = .{
+ .body = .{ .instructions = undefined },
+ },
+ .kw_args = .{},
+ };
+
+ var block_scope: Scope.GenZIR = .{
+ .parent = parent_scope,
+ .decl = parent_scope.decl().?,
+ .arena = gen_zir.arena,
+ .instructions = .{},
+ // TODO @as here is working around a stage1 miscompilation bug :(
+ .label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{
+ .token = block_node.label,
+ .block_inst = block_inst,
+ .result_loc = rl,
+ }),
+ };
+ defer block_scope.instructions.deinit(mod.gpa);
+
+ try blockExprStmts(mod, &block_scope.base, &block_node.base, block_node.statements());
+
+ block_inst.positionals.body.instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items);
+ try gen_zir.instructions.append(mod.gpa, &block_inst.base);
+
+ return &block_inst.base;
+}
+
+fn blockExprStmts(mod: *Module, parent_scope: *Scope, node: *ast.Node, statements: []*ast.Node) !void {
+ const tree = parent_scope.tree();
+
+ var block_arena = std.heap.ArenaAllocator.init(mod.gpa);
+ defer block_arena.deinit();
+
+ var scope = parent_scope;
+ for (statements) |statement| {
+ const src = tree.token_locs[statement.firstToken()].start;
+ _ = try addZIRNoOp(mod, scope, src, .dbg_stmt);
+ switch (statement.tag) {
+ .VarDecl => {
+ const var_decl_node = statement.castTag(.VarDecl).?;
+ scope = try varDecl(mod, scope, var_decl_node, &block_arena.allocator);
+ },
+ .Assign => try assign(mod, scope, statement.castTag(.Assign).?),
+ .AssignBitAnd => try assignOp(mod, scope, statement.castTag(.AssignBitAnd).?, .bitand),
+ .AssignBitOr => try assignOp(mod, scope, statement.castTag(.AssignBitOr).?, .bitor),
+ .AssignBitShiftLeft => try assignOp(mod, scope, statement.castTag(.AssignBitShiftLeft).?, .shl),
+ .AssignBitShiftRight => try assignOp(mod, scope, statement.castTag(.AssignBitShiftRight).?, .shr),
+ .AssignBitXor => try assignOp(mod, scope, statement.castTag(.AssignBitXor).?, .xor),
+ .AssignDiv => try assignOp(mod, scope, statement.castTag(.AssignDiv).?, .div),
+ .AssignSub => try assignOp(mod, scope, statement.castTag(.AssignSub).?, .sub),
+ .AssignSubWrap => try assignOp(mod, scope, statement.castTag(.AssignSubWrap).?, .subwrap),
+ .AssignMod => try assignOp(mod, scope, statement.castTag(.AssignMod).?, .mod_rem),
+ .AssignAdd => try assignOp(mod, scope, statement.castTag(.AssignAdd).?, .add),
+ .AssignAddWrap => try assignOp(mod, scope, statement.castTag(.AssignAddWrap).?, .addwrap),
+ .AssignMul => try assignOp(mod, scope, statement.castTag(.AssignMul).?, .mul),
+ .AssignMulWrap => try assignOp(mod, scope, statement.castTag(.AssignMulWrap).?, .mulwrap),
+
+ else => {
+ const possibly_unused_result = try expr(mod, scope, .none, statement);
+ if (!possibly_unused_result.tag.isNoReturn()) {
+ _ = try addZIRUnOp(mod, scope, src, .ensure_result_used, possibly_unused_result);
+ }
+ },
+ }
+ }
+}
+
+fn varDecl(
+ mod: *Module,
+ scope: *Scope,
+ node: *ast.Node.VarDecl,
+ block_arena: *Allocator,
+) InnerError!*Scope {
+ // TODO implement detection of shadowing
+ if (node.getComptimeToken()) |comptime_token| {
+ return mod.failTok(scope, comptime_token, "TODO implement comptime locals", .{});
+ }
+ if (node.getAlignNode()) |align_node| {
+ return mod.failNode(scope, align_node, "TODO implement alignment on locals", .{});
+ }
+ const tree = scope.tree();
+ const name_src = tree.token_locs[node.name_token].start;
+ const ident_name = try identifierTokenString(mod, scope, node.name_token);
+ const init_node = node.getInitNode() orelse
+ return mod.fail(scope, name_src, "variables must be initialized", .{});
+
+ switch (tree.token_ids[node.mut_token]) {
+ .Keyword_const => {
+ // Depending on the type of AST the initialization expression is, we may need an lvalue
+ // or an rvalue as a result location. If it is an rvalue, we can use the instruction as
+ // the variable, no memory location needed.
+ const result_loc = if (nodeMayNeedMemoryLocation(init_node)) r: {
+ if (node.getTypeNode()) |type_node| {
+ const type_inst = try typeExpr(mod, scope, type_node);
+ const alloc = try addZIRUnOp(mod, scope, name_src, .alloc, type_inst);
+ break :r ResultLoc{ .ptr = alloc };
+ } else {
+ const alloc = try addZIRNoOpT(mod, scope, name_src, .alloc_inferred);
+ break :r ResultLoc{ .inferred_ptr = alloc };
+ }
+ } else r: {
+ if (node.getTypeNode()) |type_node|
+ break :r ResultLoc{ .ty = try typeExpr(mod, scope, type_node) }
+ else
+ break :r .none;
+ };
+ const init_inst = try expr(mod, scope, result_loc, init_node);
+ const sub_scope = try block_arena.create(Scope.LocalVal);
+ sub_scope.* = .{
+ .parent = scope,
+ .gen_zir = scope.getGenZIR(),
+ .name = ident_name,
+ .inst = init_inst,
+ };
+ return &sub_scope.base;
+ },
+ .Keyword_var => {
+ const var_data: struct { result_loc: ResultLoc, alloc: *zir.Inst } = if (node.getTypeNode()) |type_node| a: {
+ const type_inst = try typeExpr(mod, scope, type_node);
+ const alloc = try addZIRUnOp(mod, scope, name_src, .alloc, type_inst);
+ break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } };
+ } else a: {
+ const alloc = try addZIRNoOp(mod, scope, name_src, .alloc_inferred);
+ break :a .{ .alloc = alloc, .result_loc = .{ .inferred_ptr = alloc.castTag(.alloc_inferred).? } };
+ };
+ const init_inst = try expr(mod, scope, var_data.result_loc, init_node);
+ const sub_scope = try block_arena.create(Scope.LocalPtr);
+ sub_scope.* = .{
+ .parent = scope,
+ .gen_zir = scope.getGenZIR(),
+ .name = ident_name,
+ .ptr = var_data.alloc,
+ };
+ return &sub_scope.base;
+ },
+ else => unreachable,
+ }
+}
+
+fn assign(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) InnerError!void {
+ if (infix_node.lhs.castTag(.Identifier)) |ident| {
+ // This intentionally does not support @"_" syntax.
+ const ident_name = scope.tree().tokenSlice(ident.token);
+ if (mem.eql(u8, ident_name, "_")) {
+ _ = try expr(mod, scope, .discard, infix_node.rhs);
+ return;
+ }
+ }
+ const lvalue = try lvalExpr(mod, scope, infix_node.lhs);
+ _ = try expr(mod, scope, .{ .ptr = lvalue }, infix_node.rhs);
+}
+
+fn assignOp(
+ mod: *Module,
+ scope: *Scope,
+ infix_node: *ast.Node.SimpleInfixOp,
+ op_inst_tag: zir.Inst.Tag,
+) InnerError!void {
+ const lhs_ptr = try lvalExpr(mod, scope, infix_node.lhs);
+ const lhs = try addZIRUnOp(mod, scope, lhs_ptr.src, .deref, lhs_ptr);
+ const lhs_type = try addZIRUnOp(mod, scope, lhs_ptr.src, .typeof, lhs);
+ const rhs = try expr(mod, scope, .{ .ty = lhs_type }, infix_node.rhs);
+
+ const tree = scope.tree();
+ const src = tree.token_locs[infix_node.op_token].start;
+
+ const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);
+ _ = try addZIRBinOp(mod, scope, src, .store, lhs_ptr, result);
+}
+
+fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.op_token].start;
+ const bool_type = try addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.bool_type),
+ });
+ const operand = try expr(mod, scope, .{ .ty = bool_type }, node.rhs);
+ return addZIRUnOp(mod, scope, src, .boolnot, operand);
+}
+
+fn bitNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.op_token].start;
+ const operand = try expr(mod, scope, .none, node.rhs);
+ return addZIRUnOp(mod, scope, src, .bitnot, operand);
+}
+
+fn negation(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp, op_inst_tag: zir.Inst.Tag) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.op_token].start;
+
+ const lhs = try addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.comptime_int),
+ .val = Value.initTag(.zero),
+ });
+ const rhs = try expr(mod, scope, .none, node.rhs);
+
+ return addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);
+}
+
+fn addressOf(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
+ return expr(mod, scope, .ref, node.rhs);
+}
+
+fn optionalType(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.op_token].start;
+ const operand = try typeExpr(mod, scope, node.rhs);
+ return addZIRUnOp(mod, scope, src, .optional_type, operand);
+}
+
+fn sliceType(mod: *Module, scope: *Scope, node: *ast.Node.SliceType) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.op_token].start;
+ return ptrSliceType(mod, scope, src, &node.ptr_info, node.rhs, .Slice);
+}
+
+fn ptrType(mod: *Module, scope: *Scope, node: *ast.Node.PtrType) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.op_token].start;
+ return ptrSliceType(mod, scope, src, &node.ptr_info, node.rhs, switch (tree.token_ids[node.op_token]) {
+ .Asterisk, .AsteriskAsterisk => .One,
+ // TODO stage1 type inference bug
+ .LBracket => @as(std.builtin.TypeInfo.Pointer.Size, switch (tree.token_ids[node.op_token + 2]) {
+ .Identifier => .C,
+ else => .Many,
+ }),
+ else => unreachable,
+ });
+}
+
+fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo, rhs: *ast.Node, size: std.builtin.TypeInfo.Pointer.Size) InnerError!*zir.Inst {
+ const simple = ptr_info.allowzero_token == null and
+ ptr_info.align_info == null and
+ ptr_info.volatile_token == null and
+ ptr_info.sentinel == null;
+
+ if (simple) {
+ const child_type = try typeExpr(mod, scope, rhs);
+ const mutable = ptr_info.const_token == null;
+ // TODO stage1 type inference bug
+ const T = zir.Inst.Tag;
+ return addZIRUnOp(mod, scope, src, switch (size) {
+ .One => if (mutable) T.single_mut_ptr_type else T.single_const_ptr_type,
+ .Many => if (mutable) T.many_mut_ptr_type else T.many_const_ptr_type,
+ .C => if (mutable) T.c_mut_ptr_type else T.c_const_ptr_type,
+ .Slice => if (mutable) T.mut_slice_type else T.const_slice_type,
+ }, child_type);
+ }
+
+ var kw_args: std.meta.fieldInfo(zir.Inst.PtrType, "kw_args").field_type = .{};
+ kw_args.size = size;
+ kw_args.@"allowzero" = ptr_info.allowzero_token != null;
+ if (ptr_info.align_info) |some| {
+ kw_args.@"align" = try expr(mod, scope, .none, some.node);
+ if (some.bit_range) |bit_range| {
+ kw_args.align_bit_start = try expr(mod, scope, .none, bit_range.start);
+ kw_args.align_bit_end = try expr(mod, scope, .none, bit_range.end);
+ }
+ }
+ kw_args.mutable = ptr_info.const_token == null;
+ kw_args.@"volatile" = ptr_info.volatile_token != null;
+ if (ptr_info.sentinel) |some| {
+ kw_args.sentinel = try expr(mod, scope, .none, some);
+ }
+
+ const child_type = try typeExpr(mod, scope, rhs);
+ if (kw_args.sentinel) |some| {
+ kw_args.sentinel = try addZIRBinOp(mod, scope, some.src, .as, child_type, some);
+ }
+
+ return addZIRInst(mod, scope, src, zir.Inst.PtrType, .{ .child_type = child_type }, kw_args);
+}
+
+fn arrayType(mod: *Module, scope: *Scope, node: *ast.Node.ArrayType) !*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.op_token].start;
+ const usize_type = try addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.usize_type),
+ });
+
+ // TODO check for [_]T
+ const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr);
+ const elem_type = try typeExpr(mod, scope, node.rhs);
+
+ return addZIRBinOp(mod, scope, src, .array_type, len, elem_type);
+}
+
+fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.ArrayTypeSentinel) !*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.op_token].start;
+ const usize_type = try addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.usize_type),
+ });
+
+ // TODO check for [_]T
+ const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr);
+ const sentinel_uncasted = try expr(mod, scope, .none, node.sentinel);
+ const elem_type = try typeExpr(mod, scope, node.rhs);
+ const sentinel = try addZIRBinOp(mod, scope, src, .as, elem_type, sentinel_uncasted);
+
+ return addZIRInst(mod, scope, src, zir.Inst.ArrayTypeSentinel, .{
+ .len = len,
+ .sentinel = sentinel,
+ .elem_type = elem_type,
+ }, .{});
+}
+
+fn anyFrameType(mod: *Module, scope: *Scope, node: *ast.Node.AnyFrameType) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.anyframe_token].start;
+ if (node.result) |some| {
+ const return_type = try typeExpr(mod, scope, some.return_type);
+ return addZIRUnOp(mod, scope, src, .anyframe_type, return_type);
+ } else {
+ return addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.anyframe_type),
+ });
+ }
+}
+
+fn typeInixOp(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp, op_inst_tag: zir.Inst.Tag) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.op_token].start;
+ const error_set = try typeExpr(mod, scope, node.lhs);
+ const payload = try typeExpr(mod, scope, node.rhs);
+ return addZIRBinOp(mod, scope, src, op_inst_tag, error_set, payload);
+}
+
+fn enumLiteral(mod: *Module, scope: *Scope, node: *ast.Node.EnumLiteral) !*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.name].start;
+ const name = try identifierTokenString(mod, scope, node.name);
+
+ return addZIRInst(mod, scope, src, zir.Inst.EnumLiteral, .{ .name = name }, .{});
+}
+
+fn unwrapOptional(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.rtoken].start;
+
+ const operand = try expr(mod, scope, .ref, node.lhs);
+ return rlWrapPtr(mod, scope, rl, try addZIRUnOp(mod, scope, src, .unwrap_optional_safe, operand));
+}
+
+fn errorSetDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.ErrorSetDecl) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.error_token].start;
+ const decls = node.decls();
+ const fields = try scope.arena().alloc([]const u8, decls.len);
+
+ for (decls) |decl, i| {
+ const tag = decl.castTag(.ErrorTag).?;
+ fields[i] = try identifierTokenString(mod, scope, tag.name_token);
+ }
+
+ // analyzing the error set results in a decl ref, so we might need to dereference it
+ return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.ErrorSet, .{ .fields = fields }, .{}));
+}
+
+fn errorType(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.token].start;
+ return addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.anyerror_type),
+ });
+}
+
+fn catchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Catch) InnerError!*zir.Inst {
+ return orelseCatchExpr(mod, scope, rl, node.lhs, node.op_token, .iserr, .unwrap_err_unsafe, node.rhs, node.payload);
+}
+
+fn orelseExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {
+ return orelseCatchExpr(mod, scope, rl, node.lhs, node.op_token, .isnull, .unwrap_optional_unsafe, node.rhs, null);
+}
+
+fn orelseCatchExpr(
+ mod: *Module,
+ scope: *Scope,
+ rl: ResultLoc,
+ lhs: *ast.Node,
+ op_token: ast.TokenIndex,
+ cond_op: zir.Inst.Tag,
+ unwrap_op: zir.Inst.Tag,
+ rhs: *ast.Node,
+ payload_node: ?*ast.Node,
+) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[op_token].start;
+
+ const operand_ptr = try expr(mod, scope, .ref, lhs);
+ // TODO we could avoid an unnecessary copy if .iserr, .isnull took a pointer
+ const err_union = try addZIRUnOp(mod, scope, src, .deref, operand_ptr);
+ const cond = try addZIRUnOp(mod, scope, src, cond_op, err_union);
+
+ var block_scope: Scope.GenZIR = .{
+ .parent = scope,
+ .decl = scope.decl().?,
+ .arena = scope.arena(),
+ .instructions = .{},
+ };
+ defer block_scope.instructions.deinit(mod.gpa);
+
+ const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{
+ .condition = cond,
+ .then_body = undefined, // populated below
+ .else_body = undefined, // populated below
+ }, .{});
+
+ const block = try addZIRInstBlock(mod, scope, src, .block, .{
+ .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
+ });
+
+ // Most result location types can be forwarded directly; however
+ // if we need to write to a pointer which has an inferred type,
+ // proper type inference requires peer type resolution on the if's
+ // branches.
+ const branch_rl: ResultLoc = switch (rl) {
+ .discard, .none, .ty, .ptr, .ref => rl,
+ .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
+ };
+
+ var then_scope: Scope.GenZIR = .{
+ .parent = scope,
+ .decl = block_scope.decl,
+ .arena = block_scope.arena,
+ .instructions = .{},
+ };
+ defer then_scope.instructions.deinit(mod.gpa);
+
+ var err_val_scope: Scope.LocalVal = undefined;
+ const then_sub_scope = blk: {
+ const payload = payload_node orelse
+ break :blk &then_scope.base;
+
+ const err_name = tree.tokenSlice(payload.castTag(.Payload).?.error_symbol.firstToken());
+ if (mem.eql(u8, err_name, "_"))
+ break :blk &then_scope.base;
+
+ const unwrapped_err_ptr = try addZIRUnOp(mod, &then_scope.base, src, .unwrap_err_code, operand_ptr);
+ err_val_scope = .{
+ .parent = &then_scope.base,
+ .gen_zir = &then_scope,
+ .name = err_name,
+ .inst = try addZIRUnOp(mod, &then_scope.base, src, .deref, unwrapped_err_ptr),
+ };
+ break :blk &err_val_scope.base;
+ };
+
+ _ = try addZIRInst(mod, &then_scope.base, src, zir.Inst.Break, .{
+ .block = block,
+ .operand = try expr(mod, then_sub_scope, branch_rl, rhs),
+ }, .{});
+
+ var else_scope: Scope.GenZIR = .{
+ .parent = scope,
+ .decl = block_scope.decl,
+ .arena = block_scope.arena,
+ .instructions = .{},
+ };
+ defer else_scope.instructions.deinit(mod.gpa);
+
+ const unwrapped_payload = try addZIRUnOp(mod, &else_scope.base, src, unwrap_op, operand_ptr);
+ _ = try addZIRInst(mod, &else_scope.base, src, zir.Inst.Break, .{
+ .block = block,
+ .operand = unwrapped_payload,
+ }, .{});
+
+ condbr.positionals.then_body = .{ .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items) };
+ condbr.positionals.else_body = .{ .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items) };
+ return rlWrapPtr(mod, scope, rl, &block.base);
+}
+
+/// Return whether the identifier names of two tokens are equal. Resolves @"" tokens without allocating.
+/// OK in theory it could do it without allocating. This implementation allocates when the @"" form is used.
+fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool {
+ const ident_name_1 = try identifierTokenString(mod, scope, token1);
+ const ident_name_2 = try identifierTokenString(mod, scope, token2);
+ return mem.eql(u8, ident_name_1, ident_name_2);
+}
+
+/// Identifier token -> String (allocated in scope.arena())
+fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 {
+ const tree = scope.tree();
+
+ const ident_name = tree.tokenSlice(token);
+ if (mem.startsWith(u8, ident_name, "@")) {
+ const raw_string = ident_name[1..];
+ var bad_index: usize = undefined;
+ return std.zig.parseStringLiteral(scope.arena(), raw_string, &bad_index) catch |err| switch (err) {
+ error.InvalidCharacter => {
+ const bad_byte = raw_string[bad_index];
+ const src = tree.token_locs[token].start;
+ return mod.fail(scope, src + 1 + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte});
+ },
+ else => |e| return e,
+ };
+ }
+ return ident_name;
+}
+
+pub fn identifierStringInst(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.token].start;
+
+ const ident_name = try identifierTokenString(mod, scope, node.token);
+
+ return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = ident_name }, .{});
+}
+
+fn field(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.op_token].start;
+
+ const lhs = try expr(mod, scope, .ref, node.lhs);
+ const field_name = try identifierStringInst(mod, scope, node.rhs.castTag(.Identifier).?);
+
+ return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.FieldPtr, .{ .object_ptr = lhs, .field_name = field_name }, .{}));
+}
+
+fn arrayAccess(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.ArrayAccess) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.rtoken].start;
+
+ const array_ptr = try expr(mod, scope, .ref, node.lhs);
+ const index = try expr(mod, scope, .none, node.index_expr);
+
+ return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.ElemPtr, .{ .array_ptr = array_ptr, .index = index }, .{}));
+}
+
+fn sliceExpr(mod: *Module, scope: *Scope, node: *ast.Node.Slice) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.rtoken].start;
+
+ const usize_type = try addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.usize_type),
+ });
+
+ const array_ptr = try expr(mod, scope, .ref, node.lhs);
+ const start = try expr(mod, scope, .{ .ty = usize_type }, node.start);
+
+ if (node.end == null and node.sentinel == null) {
+ return try addZIRBinOp(mod, scope, src, .slice_start, array_ptr, start);
+ }
+
+ const end = if (node.end) |end| try expr(mod, scope, .{ .ty = usize_type }, end) else null;
+ // we could get the child type here, but it is easier to just do it in semantic analysis.
+ const sentinel = if (node.sentinel) |sentinel| try expr(mod, scope, .none, sentinel) else null;
+
+ return try addZIRInst(
+ mod,
+ scope,
+ src,
+ zir.Inst.Slice,
+ .{ .array_ptr = array_ptr, .start = start },
+ .{ .end = end, .sentinel = sentinel },
+ );
+}
+
+fn deref(mod: *Module, scope: *Scope, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.rtoken].start;
+ const lhs = try expr(mod, scope, .none, node.lhs);
+ return addZIRUnOp(mod, scope, src, .deref, lhs);
+}
+
+fn simpleBinOp(
+ mod: *Module,
+ scope: *Scope,
+ rl: ResultLoc,
+ infix_node: *ast.Node.SimpleInfixOp,
+ op_inst_tag: zir.Inst.Tag,
+) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[infix_node.op_token].start;
+
+ const lhs = try expr(mod, scope, .none, infix_node.lhs);
+ const rhs = try expr(mod, scope, .none, infix_node.rhs);
+
+ const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);
+ return rlWrap(mod, scope, rl, result);
+}
+
+fn boolBinOp(
+ mod: *Module,
+ scope: *Scope,
+ rl: ResultLoc,
+ infix_node: *ast.Node.SimpleInfixOp,
+) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[infix_node.op_token].start;
+ const bool_type = try addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.bool_type),
+ });
+
+ var block_scope: Scope.GenZIR = .{
+ .parent = scope,
+ .decl = scope.decl().?,
+ .arena = scope.arena(),
+ .instructions = .{},
+ };
+ defer block_scope.instructions.deinit(mod.gpa);
+
+ const lhs = try expr(mod, scope, .{ .ty = bool_type }, infix_node.lhs);
+ const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{
+ .condition = lhs,
+ .then_body = undefined, // populated below
+ .else_body = undefined, // populated below
+ }, .{});
+
+ const block = try addZIRInstBlock(mod, scope, src, .block, .{
+ .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
+ });
+
+ var rhs_scope: Scope.GenZIR = .{
+ .parent = scope,
+ .decl = block_scope.decl,
+ .arena = block_scope.arena,
+ .instructions = .{},
+ };
+ defer rhs_scope.instructions.deinit(mod.gpa);
+
+ const rhs = try expr(mod, &rhs_scope.base, .{ .ty = bool_type }, infix_node.rhs);
+ _ = try addZIRInst(mod, &rhs_scope.base, src, zir.Inst.Break, .{
+ .block = block,
+ .operand = rhs,
+ }, .{});
+
+ var const_scope: Scope.GenZIR = .{
+ .parent = scope,
+ .decl = block_scope.decl,
+ .arena = block_scope.arena,
+ .instructions = .{},
+ };
+ defer const_scope.instructions.deinit(mod.gpa);
+
+ const is_bool_and = infix_node.base.tag == .BoolAnd;
+ _ = try addZIRInst(mod, &const_scope.base, src, zir.Inst.Break, .{
+ .block = block,
+ .operand = try addZIRInstConst(mod, &const_scope.base, src, .{
+ .ty = Type.initTag(.bool),
+ .val = if (is_bool_and) Value.initTag(.bool_false) else Value.initTag(.bool_true),
+ }),
+ }, .{});
+
+ if (is_bool_and) {
+ // if lhs // AND
+ // break rhs
+ // else
+ // break false
+ condbr.positionals.then_body = .{ .instructions = try rhs_scope.arena.dupe(*zir.Inst, rhs_scope.instructions.items) };
+ condbr.positionals.else_body = .{ .instructions = try const_scope.arena.dupe(*zir.Inst, const_scope.instructions.items) };
+ } else {
+ // if lhs // OR
+ // break true
+ // else
+ // break rhs
+ condbr.positionals.then_body = .{ .instructions = try const_scope.arena.dupe(*zir.Inst, const_scope.instructions.items) };
+ condbr.positionals.else_body = .{ .instructions = try rhs_scope.arena.dupe(*zir.Inst, rhs_scope.instructions.items) };
+ }
+
+ return rlWrap(mod, scope, rl, &block.base);
+}
+
+const CondKind = union(enum) {
+ bool,
+ optional: ?*zir.Inst,
+ err_union: ?*zir.Inst,
+
+ fn cond(self: *CondKind, mod: *Module, block_scope: *Scope.GenZIR, src: usize, cond_node: *ast.Node) !*zir.Inst {
+ switch (self.*) {
+ .bool => {
+ const bool_type = try addZIRInstConst(mod, &block_scope.base, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.bool_type),
+ });
+ return try expr(mod, &block_scope.base, .{ .ty = bool_type }, cond_node);
+ },
+ .optional => {
+ const cond_ptr = try expr(mod, &block_scope.base, .ref, cond_node);
+ self.* = .{ .optional = cond_ptr };
+ const result = try addZIRUnOp(mod, &block_scope.base, src, .deref, cond_ptr);
+ return try addZIRUnOp(mod, &block_scope.base, src, .isnonnull, result);
+ },
+ .err_union => {
+ const err_ptr = try expr(mod, &block_scope.base, .ref, cond_node);
+ self.* = .{ .err_union = err_ptr };
+ const result = try addZIRUnOp(mod, &block_scope.base, src, .deref, err_ptr);
+ return try addZIRUnOp(mod, &block_scope.base, src, .iserr, result);
+ },
+ }
+ }
+
+ fn thenSubScope(self: CondKind, mod: *Module, then_scope: *Scope.GenZIR, src: usize, payload_node: ?*ast.Node) !*Scope {
+ if (self == .bool) return &then_scope.base;
+
+ const payload = payload_node.?.castTag(.PointerPayload) orelse {
+ // condition is error union and payload is not explicitly ignored
+ _ = try addZIRUnOp(mod, &then_scope.base, src, .ensure_err_payload_void, self.err_union.?);
+ return &then_scope.base;
+ };
+ const is_ptr = payload.ptr_token != null;
+ const ident_node = payload.value_symbol.castTag(.Identifier).?;
+
+ // This intentionally does not support @"_" syntax.
+ const ident_name = then_scope.base.tree().tokenSlice(ident_node.token);
+ if (mem.eql(u8, ident_name, "_")) {
+ if (is_ptr)
+ return mod.failTok(&then_scope.base, payload.ptr_token.?, "pointer modifier invalid on discard", .{});
+ return &then_scope.base;
+ }
+
+ return mod.failNode(&then_scope.base, payload.value_symbol, "TODO implement payload symbols", .{});
+ }
+
+ fn elseSubScope(self: CondKind, mod: *Module, else_scope: *Scope.GenZIR, src: usize, payload_node: ?*ast.Node) !*Scope {
+ if (self != .err_union) return &else_scope.base;
+
+ const payload_ptr = try addZIRUnOp(mod, &else_scope.base, src, .unwrap_err_unsafe, self.err_union.?);
+
+ const payload = payload_node.?.castTag(.Payload).?;
+ const ident_node = payload.error_symbol.castTag(.Identifier).?;
+
+ // This intentionally does not support @"_" syntax.
+ const ident_name = else_scope.base.tree().tokenSlice(ident_node.token);
+ if (mem.eql(u8, ident_name, "_")) {
+ return &else_scope.base;
+ }
+
+ return mod.failNode(&else_scope.base, payload.error_symbol, "TODO implement payload symbols", .{});
+ }
+};
+
+fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) InnerError!*zir.Inst {
+ var cond_kind: CondKind = .bool;
+ if (if_node.payload) |_| cond_kind = .{ .optional = null };
+ if (if_node.@"else") |else_node| {
+ if (else_node.payload) |payload| {
+ cond_kind = .{ .err_union = null };
+ }
+ }
+ var block_scope: Scope.GenZIR = .{
+ .parent = scope,
+ .decl = scope.decl().?,
+ .arena = scope.arena(),
+ .instructions = .{},
+ };
+ defer block_scope.instructions.deinit(mod.gpa);
+
+ const tree = scope.tree();
+ const if_src = tree.token_locs[if_node.if_token].start;
+ const cond = try cond_kind.cond(mod, &block_scope, if_src, if_node.condition);
+
+ const condbr = try addZIRInstSpecial(mod, &block_scope.base, if_src, zir.Inst.CondBr, .{
+ .condition = cond,
+ .then_body = undefined, // populated below
+ .else_body = undefined, // populated below
+ }, .{});
+
+ const block = try addZIRInstBlock(mod, scope, if_src, .block, .{
+ .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
+ });
+
+ const then_src = tree.token_locs[if_node.body.lastToken()].start;
+ var then_scope: Scope.GenZIR = .{
+ .parent = scope,
+ .decl = block_scope.decl,
+ .arena = block_scope.arena,
+ .instructions = .{},
+ };
+ defer then_scope.instructions.deinit(mod.gpa);
+
+ // declare payload to the then_scope
+ const then_sub_scope = try cond_kind.thenSubScope(mod, &then_scope, then_src, if_node.payload);
+
+ // Most result location types can be forwarded directly; however
+ // if we need to write to a pointer which has an inferred type,
+ // proper type inference requires peer type resolution on the if's
+ // branches.
+ const branch_rl: ResultLoc = switch (rl) {
+ .discard, .none, .ty, .ptr, .ref => rl,
+ .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
+ };
+
+ const then_result = try expr(mod, then_sub_scope, branch_rl, if_node.body);
+ if (!then_result.tag.isNoReturn()) {
+ _ = try addZIRInst(mod, then_sub_scope, then_src, zir.Inst.Break, .{
+ .block = block,
+ .operand = then_result,
+ }, .{});
+ }
+ condbr.positionals.then_body = .{
+ .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),
+ };
+
+ var else_scope: Scope.GenZIR = .{
+ .parent = scope,
+ .decl = block_scope.decl,
+ .arena = block_scope.arena,
+ .instructions = .{},
+ };
+ defer else_scope.instructions.deinit(mod.gpa);
+
+ if (if_node.@"else") |else_node| {
+ const else_src = tree.token_locs[else_node.body.lastToken()].start;
+ // declare payload to the then_scope
+ const else_sub_scope = try cond_kind.elseSubScope(mod, &else_scope, else_src, else_node.payload);
+
+ const else_result = try expr(mod, else_sub_scope, branch_rl, else_node.body);
+ if (!else_result.tag.isNoReturn()) {
+ _ = try addZIRInst(mod, else_sub_scope, else_src, zir.Inst.Break, .{
+ .block = block,
+ .operand = else_result,
+ }, .{});
+ }
+ } else {
+ // TODO Optimization opportunity: we can avoid an allocation and a memcpy here
+ // by directly allocating the body for this one instruction.
+ const else_src = tree.token_locs[if_node.lastToken()].start;
+ _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.BreakVoid, .{
+ .block = block,
+ }, .{});
+ }
+ condbr.positionals.else_body = .{
+ .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
+ };
+
+ return &block.base;
+}
+
+fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.While) InnerError!*zir.Inst {
+ var cond_kind: CondKind = .bool;
+ if (while_node.payload) |_| cond_kind = .{ .optional = null };
+ if (while_node.@"else") |else_node| {
+ if (else_node.payload) |payload| {
+ cond_kind = .{ .err_union = null };
+ }
+ }
+
+ if (while_node.label) |tok|
+ return mod.failTok(scope, tok, "TODO labeled while", .{});
+
+ if (while_node.inline_token) |tok|
+ return mod.failTok(scope, tok, "TODO inline while", .{});
+
+ var expr_scope: Scope.GenZIR = .{
+ .parent = scope,
+ .decl = scope.decl().?,
+ .arena = scope.arena(),
+ .instructions = .{},
+ };
+ defer expr_scope.instructions.deinit(mod.gpa);
+
+ var loop_scope: Scope.GenZIR = .{
+ .parent = &expr_scope.base,
+ .decl = expr_scope.decl,
+ .arena = expr_scope.arena,
+ .instructions = .{},
+ };
+ defer loop_scope.instructions.deinit(mod.gpa);
+
+ var continue_scope: Scope.GenZIR = .{
+ .parent = &loop_scope.base,
+ .decl = loop_scope.decl,
+ .arena = loop_scope.arena,
+ .instructions = .{},
+ };
+ defer continue_scope.instructions.deinit(mod.gpa);
+
+ const tree = scope.tree();
+ const while_src = tree.token_locs[while_node.while_token].start;
+ const void_type = try addZIRInstConst(mod, scope, while_src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.void_type),
+ });
+ const cond = try cond_kind.cond(mod, &continue_scope, while_src, while_node.condition);
+
+ const condbr = try addZIRInstSpecial(mod, &continue_scope.base, while_src, zir.Inst.CondBr, .{
+ .condition = cond,
+ .then_body = undefined, // populated below
+ .else_body = undefined, // populated below
+ }, .{});
+ const cond_block = try addZIRInstBlock(mod, &loop_scope.base, while_src, .block, .{
+ .instructions = try loop_scope.arena.dupe(*zir.Inst, continue_scope.instructions.items),
+ });
+ // TODO avoid emitting the continue expr when there
+ // are no jumps to it. This happens when the last statement of a while body is noreturn
+ // and there are no `continue` statements.
+ // The "repeat" at the end of a loop body is implied.
+ if (while_node.continue_expr) |cont_expr| {
+ _ = try expr(mod, &loop_scope.base, .{ .ty = void_type }, cont_expr);
+ }
+ const loop = try addZIRInstLoop(mod, &expr_scope.base, while_src, .{
+ .instructions = try expr_scope.arena.dupe(*zir.Inst, loop_scope.instructions.items),
+ });
+ const while_block = try addZIRInstBlock(mod, scope, while_src, .block, .{
+ .instructions = try expr_scope.arena.dupe(*zir.Inst, expr_scope.instructions.items),
+ });
+
+ const then_src = tree.token_locs[while_node.body.lastToken()].start;
+ var then_scope: Scope.GenZIR = .{
+ .parent = &continue_scope.base,
+ .decl = continue_scope.decl,
+ .arena = continue_scope.arena,
+ .instructions = .{},
+ };
+ defer then_scope.instructions.deinit(mod.gpa);
+
+ // declare payload to the then_scope
+ const then_sub_scope = try cond_kind.thenSubScope(mod, &then_scope, then_src, while_node.payload);
+
+ // Most result location types can be forwarded directly; however
+ // if we need to write to a pointer which has an inferred type,
+ // proper type inference requires peer type resolution on the while's
+ // branches.
+ const branch_rl: ResultLoc = switch (rl) {
+ .discard, .none, .ty, .ptr, .ref => rl,
+ .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = while_block },
+ };
+
+ const then_result = try expr(mod, then_sub_scope, branch_rl, while_node.body);
+ if (!then_result.tag.isNoReturn()) {
+ _ = try addZIRInst(mod, then_sub_scope, then_src, zir.Inst.Break, .{
+ .block = cond_block,
+ .operand = then_result,
+ }, .{});
+ }
+ condbr.positionals.then_body = .{
+ .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),
+ };
+
+ var else_scope: Scope.GenZIR = .{
+ .parent = &continue_scope.base,
+ .decl = continue_scope.decl,
+ .arena = continue_scope.arena,
+ .instructions = .{},
+ };
+ defer else_scope.instructions.deinit(mod.gpa);
+
+ if (while_node.@"else") |else_node| {
+ const else_src = tree.token_locs[else_node.body.lastToken()].start;
+ // declare payload to the then_scope
+ const else_sub_scope = try cond_kind.elseSubScope(mod, &else_scope, else_src, else_node.payload);
+
+ const else_result = try expr(mod, else_sub_scope, branch_rl, else_node.body);
+ if (!else_result.tag.isNoReturn()) {
+ _ = try addZIRInst(mod, else_sub_scope, else_src, zir.Inst.Break, .{
+ .block = while_block,
+ .operand = else_result,
+ }, .{});
+ }
+ } else {
+ const else_src = tree.token_locs[while_node.lastToken()].start;
+ _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.BreakVoid, .{
+ .block = while_block,
+ }, .{});
+ }
+ condbr.positionals.else_body = .{
+ .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
+ };
+ return &while_block.base;
+}
+
+fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For) InnerError!*zir.Inst {
+ if (for_node.label) |tok|
+ return mod.failTok(scope, tok, "TODO labeled for", .{});
+
+ if (for_node.inline_token) |tok|
+ return mod.failTok(scope, tok, "TODO inline for", .{});
+
+ var for_scope: Scope.GenZIR = .{
+ .parent = scope,
+ .decl = scope.decl().?,
+ .arena = scope.arena(),
+ .instructions = .{},
+ };
+ defer for_scope.instructions.deinit(mod.gpa);
+
+ // setup variables and constants
+ const tree = scope.tree();
+ const for_src = tree.token_locs[for_node.for_token].start;
+ const index_ptr = blk: {
+ const usize_type = try addZIRInstConst(mod, &for_scope.base, for_src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.usize_type),
+ });
+ const index_ptr = try addZIRUnOp(mod, &for_scope.base, for_src, .alloc, usize_type);
+ // initialize to zero
+ const zero = try addZIRInstConst(mod, &for_scope.base, for_src, .{
+ .ty = Type.initTag(.usize),
+ .val = Value.initTag(.zero),
+ });
+ _ = try addZIRBinOp(mod, &for_scope.base, for_src, .store, index_ptr, zero);
+ break :blk index_ptr;
+ };
+ const array_ptr = try expr(mod, &for_scope.base, .ref, for_node.array_expr);
+ _ = try addZIRUnOp(mod, &for_scope.base, for_node.array_expr.firstToken(), .ensure_indexable, array_ptr);
+ const cond_src = tree.token_locs[for_node.array_expr.firstToken()].start;
+ const len_ptr = try addZIRInst(mod, &for_scope.base, cond_src, zir.Inst.FieldPtr, .{
+ .object_ptr = array_ptr,
+ .field_name = try addZIRInst(mod, &for_scope.base, cond_src, zir.Inst.Str, .{ .bytes = "len" }, .{}),
+ }, .{});
+
+ var loop_scope: Scope.GenZIR = .{
+ .parent = &for_scope.base,
+ .decl = for_scope.decl,
+ .arena = for_scope.arena,
+ .instructions = .{},
+ };
+ defer loop_scope.instructions.deinit(mod.gpa);
+
+ var cond_scope: Scope.GenZIR = .{
+ .parent = &loop_scope.base,
+ .decl = loop_scope.decl,
+ .arena = loop_scope.arena,
+ .instructions = .{},
+ };
+ defer cond_scope.instructions.deinit(mod.gpa);
+
+ // check condition i < array_expr.len
+ const index = try addZIRUnOp(mod, &cond_scope.base, cond_src, .deref, index_ptr);
+ const len = try addZIRUnOp(mod, &cond_scope.base, cond_src, .deref, len_ptr);
+ const cond = try addZIRBinOp(mod, &cond_scope.base, cond_src, .cmp_lt, index, len);
+
+ const condbr = try addZIRInstSpecial(mod, &cond_scope.base, for_src, zir.Inst.CondBr, .{
+ .condition = cond,
+ .then_body = undefined, // populated below
+ .else_body = undefined, // populated below
+ }, .{});
+ const cond_block = try addZIRInstBlock(mod, &loop_scope.base, for_src, .block, .{
+ .instructions = try loop_scope.arena.dupe(*zir.Inst, cond_scope.instructions.items),
+ });
+
+ // increment index variable
+ const one = try addZIRInstConst(mod, &loop_scope.base, for_src, .{
+ .ty = Type.initTag(.usize),
+ .val = Value.initTag(.one),
+ });
+ const index_2 = try addZIRUnOp(mod, &loop_scope.base, cond_src, .deref, index_ptr);
+ const index_plus_one = try addZIRBinOp(mod, &loop_scope.base, for_src, .add, index_2, one);
+ _ = try addZIRBinOp(mod, &loop_scope.base, for_src, .store, index_ptr, index_plus_one);
+
+ // looping stuff
+ const loop = try addZIRInstLoop(mod, &for_scope.base, for_src, .{
+ .instructions = try for_scope.arena.dupe(*zir.Inst, loop_scope.instructions.items),
+ });
+ const for_block = try addZIRInstBlock(mod, scope, for_src, .block, .{
+ .instructions = try for_scope.arena.dupe(*zir.Inst, for_scope.instructions.items),
+ });
+
+ // while body
+ const then_src = tree.token_locs[for_node.body.lastToken()].start;
+ var then_scope: Scope.GenZIR = .{
+ .parent = &cond_scope.base,
+ .decl = cond_scope.decl,
+ .arena = cond_scope.arena,
+ .instructions = .{},
+ };
+ defer then_scope.instructions.deinit(mod.gpa);
+
+ // Most result location types can be forwarded directly; however
+ // if we need to write to a pointer which has an inferred type,
+ // proper type inference requires peer type resolution on the while's
+ // branches.
+ const branch_rl: ResultLoc = switch (rl) {
+ .discard, .none, .ty, .ptr, .ref => rl,
+ .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = for_block },
+ };
+
+ var index_scope: Scope.LocalPtr = undefined;
+ const then_sub_scope = blk: {
+ const payload = for_node.payload.castTag(.PointerIndexPayload).?;
+ const is_ptr = payload.ptr_token != null;
+ const value_name = tree.tokenSlice(payload.value_symbol.firstToken());
+ if (!mem.eql(u8, value_name, "_")) {
+ return mod.failNode(&then_scope.base, payload.value_symbol, "TODO implement for value payload", .{});
+ } else if (is_ptr) {
+ return mod.failTok(&then_scope.base, payload.ptr_token.?, "pointer modifier invalid on discard", .{});
+ }
+
+ const index_symbol_node = payload.index_symbol orelse
+ break :blk &then_scope.base;
+
+ const index_name = tree.tokenSlice(index_symbol_node.firstToken());
+ if (mem.eql(u8, index_name, "_")) {
+ break :blk &then_scope.base;
+ }
+ // TODO make this const without an extra copy?
+ index_scope = .{
+ .parent = &then_scope.base,
+ .gen_zir = &then_scope,
+ .name = index_name,
+ .ptr = index_ptr,
+ };
+ break :blk &index_scope.base;
+ };
+
+ const then_result = try expr(mod, then_sub_scope, branch_rl, for_node.body);
+ if (!then_result.tag.isNoReturn()) {
+ _ = try addZIRInst(mod, then_sub_scope, then_src, zir.Inst.Break, .{
+ .block = cond_block,
+ .operand = then_result,
+ }, .{});
+ }
+ condbr.positionals.then_body = .{
+ .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),
+ };
+
+ // else branch
+ var else_scope: Scope.GenZIR = .{
+ .parent = &cond_scope.base,
+ .decl = cond_scope.decl,
+ .arena = cond_scope.arena,
+ .instructions = .{},
+ };
+ defer else_scope.instructions.deinit(mod.gpa);
+
+ if (for_node.@"else") |else_node| {
+ const else_src = tree.token_locs[else_node.body.lastToken()].start;
+ const else_result = try expr(mod, &else_scope.base, branch_rl, else_node.body);
+ if (!else_result.tag.isNoReturn()) {
+ _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.Break, .{
+ .block = for_block,
+ .operand = else_result,
+ }, .{});
+ }
+ } else {
+ const else_src = tree.token_locs[for_node.lastToken()].start;
+ _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.BreakVoid, .{
+ .block = for_block,
+ }, .{});
+ }
+ condbr.positionals.else_body = .{
+ .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
+ };
+ return &for_block.base;
+}
+
+fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[cfe.ltoken].start;
+ if (cfe.getRHS()) |rhs_node| {
+ if (nodeMayNeedMemoryLocation(rhs_node)) {
+ const ret_ptr = try addZIRNoOp(mod, scope, src, .ret_ptr);
+ const operand = try expr(mod, scope, .{ .ptr = ret_ptr }, rhs_node);
+ return addZIRUnOp(mod, scope, src, .@"return", operand);
+ } else {
+ const fn_ret_ty = try addZIRNoOp(mod, scope, src, .ret_type);
+ const operand = try expr(mod, scope, .{ .ty = fn_ret_ty }, rhs_node);
+ return addZIRUnOp(mod, scope, src, .@"return", operand);
+ }
+ } else {
+ return addZIRNoOp(mod, scope, src, .returnvoid);
+ }
+}
+
+fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneToken) InnerError!*zir.Inst {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const tree = scope.tree();
+ const ident_name = try identifierTokenString(mod, scope, ident.token);
+ const src = tree.token_locs[ident.token].start;
+ if (mem.eql(u8, ident_name, "_")) {
+ return mod.failNode(scope, &ident.base, "TODO implement '_' identifier", .{});
+ }
+
+ if (getSimplePrimitiveValue(ident_name)) |typed_value| {
+ const result = try addZIRInstConst(mod, scope, src, typed_value);
+ return rlWrap(mod, scope, rl, result);
+ }
+
+ if (ident_name.len >= 2) integer: {
+ const first_c = ident_name[0];
+ if (first_c == 'i' or first_c == 'u') {
+ const is_signed = first_c == 'i';
+ const bit_count = std.fmt.parseInt(u16, ident_name[1..], 10) catch |err| switch (err) {
+ error.Overflow => return mod.failNode(
+ scope,
+ &ident.base,
+ "primitive integer type '{}' exceeds maximum bit width of 65535",
+ .{ident_name},
+ ),
+ error.InvalidCharacter => break :integer,
+ };
+ const val = switch (bit_count) {
+ 8 => if (is_signed) Value.initTag(.i8_type) else Value.initTag(.u8_type),
+ 16 => if (is_signed) Value.initTag(.i16_type) else Value.initTag(.u16_type),
+ 32 => if (is_signed) Value.initTag(.i32_type) else Value.initTag(.u32_type),
+ 64 => if (is_signed) Value.initTag(.i64_type) else Value.initTag(.u64_type),
+ else => {
+ const int_type_payload = try scope.arena().create(Value.Payload.IntType);
+ int_type_payload.* = .{ .signed = is_signed, .bits = bit_count };
+ const result = try addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initPayload(&int_type_payload.base),
+ });
+ return rlWrap(mod, scope, rl, result);
+ },
+ };
+ const result = try addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = val,
+ });
+ return rlWrap(mod, scope, rl, result);
+ }
+ }
+
+ // Local variables, including function parameters.
+ {
+ var s = scope;
+ while (true) switch (s.tag) {
+ .local_val => {
+ const local_val = s.cast(Scope.LocalVal).?;
+ if (mem.eql(u8, local_val.name, ident_name)) {
+ return rlWrap(mod, scope, rl, local_val.inst);
+ }
+ s = local_val.parent;
+ },
+ .local_ptr => {
+ const local_ptr = s.cast(Scope.LocalPtr).?;
+ if (mem.eql(u8, local_ptr.name, ident_name)) {
+ return rlWrapPtr(mod, scope, rl, local_ptr.ptr);
+ }
+ s = local_ptr.parent;
+ },
+ .gen_zir => s = s.cast(Scope.GenZIR).?.parent,
+ else => break,
+ };
+ }
+
+ if (mod.lookupDeclName(scope, ident_name)) |decl| {
+ return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{}));
+ }
+
+ return mod.failNode(scope, &ident.base, "use of undeclared identifier '{}'", .{ident_name});
+}
+
+fn stringLiteral(mod: *Module, scope: *Scope, str_lit: *ast.Node.OneToken) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const unparsed_bytes = tree.tokenSlice(str_lit.token);
+ const arena = scope.arena();
+
+ var bad_index: usize = undefined;
+ const bytes = std.zig.parseStringLiteral(arena, unparsed_bytes, &bad_index) catch |err| switch (err) {
+ error.InvalidCharacter => {
+ const bad_byte = unparsed_bytes[bad_index];
+ const src = tree.token_locs[str_lit.token].start;
+ return mod.fail(scope, src + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte});
+ },
+ else => |e| return e,
+ };
+
+ const src = tree.token_locs[str_lit.token].start;
+ return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{});
+}
+
+fn multilineStrLiteral(mod: *Module, scope: *Scope, node: *ast.Node.MultilineStringLiteral) !*zir.Inst {
+ const tree = scope.tree();
+ const lines = node.linesConst();
+ const src = tree.token_locs[lines[0]].start;
+
+ // line lengths and new lines
+ var len = lines.len - 1;
+ for (lines) |line| {
+ // 2 for the '//' + 1 for '\n'
+ len += tree.tokenSlice(line).len - 3;
+ }
+
+ const bytes = try scope.arena().alloc(u8, len);
+ var i: usize = 0;
+ for (lines) |line, line_i| {
+ if (line_i != 0) {
+ bytes[i] = '\n';
+ i += 1;
+ }
+ const slice = tree.tokenSlice(line);
+ mem.copy(u8, bytes[i..], slice[2 .. slice.len - 1]);
+ i += slice.len - 3;
+ }
+
+ return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{});
+}
+
+fn charLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) !*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.token].start;
+ const slice = tree.tokenSlice(node.token);
+
+ var bad_index: usize = undefined;
+ const value = std.zig.parseCharLiteral(slice, &bad_index) catch |err| switch (err) {
+ error.InvalidCharacter => {
+ const bad_byte = slice[bad_index];
+ return mod.fail(scope, src + bad_index, "invalid character: '{c}'\n", .{bad_byte});
+ },
+ };
+
+ const int_payload = try scope.arena().create(Value.Payload.Int_u64);
+ int_payload.* = .{ .int = value };
+ return addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.comptime_int),
+ .val = Value.initPayload(&int_payload.base),
+ });
+}
+
+fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.OneToken) InnerError!*zir.Inst {
+ const arena = scope.arena();
+ const tree = scope.tree();
+ const prefixed_bytes = tree.tokenSlice(int_lit.token);
+ const base = if (mem.startsWith(u8, prefixed_bytes, "0x"))
+ 16
+ else if (mem.startsWith(u8, prefixed_bytes, "0o"))
+ 8
+ else if (mem.startsWith(u8, prefixed_bytes, "0b"))
+ 2
+ else
+ @as(u8, 10);
+
+ const bytes = if (base == 10)
+ prefixed_bytes
+ else
+ prefixed_bytes[2..];
+
+ if (std.fmt.parseInt(u64, bytes, base)) |small_int| {
+ const int_payload = try arena.create(Value.Payload.Int_u64);
+ int_payload.* = .{ .int = small_int };
+ const src = tree.token_locs[int_lit.token].start;
+ return addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.comptime_int),
+ .val = Value.initPayload(&int_payload.base),
+ });
+ } else |err| {
+ return mod.failTok(scope, int_lit.token, "TODO implement int literals that don't fit in a u64", .{});
+ }
+}
+
+fn floatLiteral(mod: *Module, scope: *Scope, float_lit: *ast.Node.OneToken) InnerError!*zir.Inst {
+ const arena = scope.arena();
+ const tree = scope.tree();
+ const bytes = tree.tokenSlice(float_lit.token);
+ if (bytes.len > 2 and bytes[1] == 'x') {
+ return mod.failTok(scope, float_lit.token, "TODO hex floats", .{});
+ }
+
+ const val = std.fmt.parseFloat(f128, bytes) catch |e| switch (e) {
+ error.InvalidCharacter => unreachable, // validated by tokenizer
+ };
+ const float_payload = try arena.create(Value.Payload.Float_128);
+ float_payload.* = .{ .val = val };
+ const src = tree.token_locs[float_lit.token].start;
+ return addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.comptime_float),
+ .val = Value.initPayload(&float_payload.base),
+ });
+}
+
+fn undefLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
+ const arena = scope.arena();
+ const tree = scope.tree();
+ const src = tree.token_locs[node.token].start;
+ return addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.@"undefined"),
+ .val = Value.initTag(.undef),
+ });
+}
+
+fn boolLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
+ const arena = scope.arena();
+ const tree = scope.tree();
+ const src = tree.token_locs[node.token].start;
+ return addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.bool),
+ .val = switch (tree.token_ids[node.token]) {
+ .Keyword_true => Value.initTag(.bool_true),
+ .Keyword_false => Value.initTag(.bool_false),
+ else => unreachable,
+ },
+ });
+}
+
+fn nullLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
+ const arena = scope.arena();
+ const tree = scope.tree();
+ const src = tree.token_locs[node.token].start;
+ return addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.@"null"),
+ .val = Value.initTag(.null_value),
+ });
+}
+
+fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zir.Inst {
+ if (asm_node.outputs.len != 0) {
+ return mod.failNode(scope, &asm_node.base, "TODO implement asm with an output", .{});
+ }
+ const arena = scope.arena();
+ const tree = scope.tree();
+
+ const inputs = try arena.alloc(*zir.Inst, asm_node.inputs.len);
+ const args = try arena.alloc(*zir.Inst, asm_node.inputs.len);
+
+ const src = tree.token_locs[asm_node.asm_token].start;
+
+ const str_type = try addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.const_slice_u8_type),
+ });
+ const str_type_rl: ResultLoc = .{ .ty = str_type };
+
+ for (asm_node.inputs) |input, i| {
+ // TODO semantically analyze constraints
+ inputs[i] = try expr(mod, scope, str_type_rl, input.constraint);
+ args[i] = try expr(mod, scope, .none, input.expr);
+ }
+
+ const return_type = try addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.void_type),
+ });
+ const asm_inst = try addZIRInst(mod, scope, src, zir.Inst.Asm, .{
+ .asm_source = try expr(mod, scope, str_type_rl, asm_node.template),
+ .return_type = return_type,
+ }, .{
+ .@"volatile" = asm_node.volatile_token != null,
+ //.clobbers = TODO handle clobbers
+ .inputs = inputs,
+ .args = args,
+ });
+ return asm_inst;
+}
+
+fn ensureBuiltinParamCount(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall, count: u32) !void {
+ if (call.params_len == count)
+ return;
+
+ const s = if (count == 1) "" else "s";
+ return mod.failTok(scope, call.builtin_token, "expected {} parameter{}, found {}", .{ count, s, call.params_len });
+}
+
+fn simpleCast(
+ mod: *Module,
+ scope: *Scope,
+ rl: ResultLoc,
+ call: *ast.Node.BuiltinCall,
+ inst_tag: zir.Inst.Tag,
+) InnerError!*zir.Inst {
+ try ensureBuiltinParamCount(mod, scope, call, 2);
+ const tree = scope.tree();
+ const src = tree.token_locs[call.builtin_token].start;
+ const params = call.params();
+ const dest_type = try typeExpr(mod, scope, params[0]);
+ const rhs = try expr(mod, scope, .none, params[1]);
+ const result = try addZIRBinOp(mod, scope, src, inst_tag, dest_type, rhs);
+ return rlWrap(mod, scope, rl, result);
+}
+
+fn ptrToInt(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
+ try ensureBuiltinParamCount(mod, scope, call, 1);
+ const operand = try expr(mod, scope, .none, call.params()[0]);
+ const tree = scope.tree();
+ const src = tree.token_locs[call.builtin_token].start;
+ return addZIRUnOp(mod, scope, src, .ptrtoint, operand);
+}
+
+fn as(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
+ try ensureBuiltinParamCount(mod, scope, call, 2);
+ const tree = scope.tree();
+ const src = tree.token_locs[call.builtin_token].start;
+ const params = call.params();
+ const dest_type = try typeExpr(mod, scope, params[0]);
+ switch (rl) {
+ .none => return try expr(mod, scope, .{ .ty = dest_type }, params[1]),
+ .discard => {
+ const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]);
+ _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
+ return result;
+ },
+ .ref => {
+ const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]);
+ return addZIRUnOp(mod, scope, result.src, .ref, result);
+ },
+ .ty => |result_ty| {
+ const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]);
+ return addZIRBinOp(mod, scope, src, .as, result_ty, result);
+ },
+ .ptr => |result_ptr| {
+ const casted_result_ptr = try addZIRBinOp(mod, scope, src, .coerce_result_ptr, dest_type, result_ptr);
+ return expr(mod, scope, .{ .ptr = casted_result_ptr }, params[1]);
+ },
+ .bitcasted_ptr => |bitcasted_ptr| {
+ // TODO here we should be able to resolve the inference; we now have a type for the result.
+ return mod.failTok(scope, call.builtin_token, "TODO implement @as with result location @bitCast", .{});
+ },
+ .inferred_ptr => |result_alloc| {
+ // TODO here we should be able to resolve the inference; we now have a type for the result.
+ return mod.failTok(scope, call.builtin_token, "TODO implement @as with inferred-type result location pointer", .{});
+ },
+ .block_ptr => |block_ptr| {
+ const casted_block_ptr = try addZIRInst(mod, scope, src, zir.Inst.CoerceResultBlockPtr, .{
+ .dest_type = dest_type,
+ .block = block_ptr,
+ }, .{});
+ return expr(mod, scope, .{ .ptr = casted_block_ptr }, params[1]);
+ },
+ }
+}
+
+fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
+ try ensureBuiltinParamCount(mod, scope, call, 2);
+ const tree = scope.tree();
+ const src = tree.token_locs[call.builtin_token].start;
+ const params = call.params();
+ const dest_type = try typeExpr(mod, scope, params[0]);
+ switch (rl) {
+ .none => {
+ const operand = try expr(mod, scope, .none, params[1]);
+ return addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand);
+ },
+ .discard => {
+ const operand = try expr(mod, scope, .none, params[1]);
+ const result = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand);
+ _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
+ return result;
+ },
+ .ref => {
+ const operand = try expr(mod, scope, .ref, params[1]);
+ const result = try addZIRBinOp(mod, scope, src, .bitcast_ref, dest_type, operand);
+ return result;
+ },
+ .ty => |result_ty| {
+ const result = try expr(mod, scope, .none, params[1]);
+ const bitcasted = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, result);
+ return addZIRBinOp(mod, scope, src, .as, result_ty, bitcasted);
+ },
+ .ptr => |result_ptr| {
+ const casted_result_ptr = try addZIRUnOp(mod, scope, src, .bitcast_result_ptr, result_ptr);
+ return expr(mod, scope, .{ .bitcasted_ptr = casted_result_ptr.castTag(.bitcast_result_ptr).? }, params[1]);
+ },
+ .bitcasted_ptr => |bitcasted_ptr| {
+ return mod.failTok(scope, call.builtin_token, "TODO implement @bitCast with result location another @bitCast", .{});
+ },
+ .block_ptr => |block_ptr| {
+ return mod.failTok(scope, call.builtin_token, "TODO implement @bitCast with result location inferred peer types", .{});
+ },
+ .inferred_ptr => |result_alloc| {
+ // TODO here we should be able to resolve the inference; we now have a type for the result.
+ return mod.failTok(scope, call.builtin_token, "TODO implement @bitCast with inferred-type result location pointer", .{});
+ },
+ }
+}
+
+fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const builtin_name = tree.tokenSlice(call.builtin_token);
+
+ // We handle the different builtins manually because they have different semantics depending
+ // on the function. For example, `@as` and others participate in result location semantics,
+ // and `@cImport` creates a special scope that collects a .c source code text buffer.
+ // Also, some builtins have a variable number of parameters.
+
+ if (mem.eql(u8, builtin_name, "@ptrToInt")) {
+ return rlWrap(mod, scope, rl, try ptrToInt(mod, scope, call));
+ } else if (mem.eql(u8, builtin_name, "@as")) {
+ return as(mod, scope, rl, call);
+ } else if (mem.eql(u8, builtin_name, "@floatCast")) {
+ return simpleCast(mod, scope, rl, call, .floatcast);
+ } else if (mem.eql(u8, builtin_name, "@intCast")) {
+ return simpleCast(mod, scope, rl, call, .intcast);
+ } else if (mem.eql(u8, builtin_name, "@bitCast")) {
+ return bitCast(mod, scope, rl, call);
+ } else if (mem.eql(u8, builtin_name, "@breakpoint")) {
+ const src = tree.token_locs[call.builtin_token].start;
+ return rlWrap(mod, scope, rl, try addZIRNoOp(mod, scope, src, .breakpoint));
+ } else {
+ return mod.failTok(scope, call.builtin_token, "invalid builtin function: '{}'", .{builtin_name});
+ }
+}
+
+fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const lhs = try expr(mod, scope, .none, node.lhs);
+
+ const param_nodes = node.params();
+ const args = try scope.getGenZIR().arena.alloc(*zir.Inst, param_nodes.len);
+ for (param_nodes) |param_node, i| {
+ const param_src = tree.token_locs[param_node.firstToken()].start;
+ const param_type = try addZIRInst(mod, scope, param_src, zir.Inst.ParamType, .{
+ .func = lhs,
+ .arg_index = i,
+ }, .{});
+ args[i] = try expr(mod, scope, .{ .ty = param_type }, param_node);
+ }
+
+ const src = tree.token_locs[node.lhs.firstToken()].start;
+ const result = try addZIRInst(mod, scope, src, zir.Inst.Call, .{
+ .func = lhs,
+ .args = args,
+ }, .{});
+ // TODO function call with result location
+ return rlWrap(mod, scope, rl, result);
+}
+
+fn unreach(mod: *Module, scope: *Scope, unreach_node: *ast.Node.OneToken) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[unreach_node.token].start;
+ return addZIRNoOp(mod, scope, src, .@"unreachable");
+}
+
+fn getSimplePrimitiveValue(name: []const u8) ?TypedValue {
+ const simple_types = std.ComptimeStringMap(Value.Tag, .{
+ .{ "u8", .u8_type },
+ .{ "i8", .i8_type },
+ .{ "isize", .isize_type },
+ .{ "usize", .usize_type },
+ .{ "c_short", .c_short_type },
+ .{ "c_ushort", .c_ushort_type },
+ .{ "c_int", .c_int_type },
+ .{ "c_uint", .c_uint_type },
+ .{ "c_long", .c_long_type },
+ .{ "c_ulong", .c_ulong_type },
+ .{ "c_longlong", .c_longlong_type },
+ .{ "c_ulonglong", .c_ulonglong_type },
+ .{ "c_longdouble", .c_longdouble_type },
+ .{ "f16", .f16_type },
+ .{ "f32", .f32_type },
+ .{ "f64", .f64_type },
+ .{ "f128", .f128_type },
+ .{ "c_void", .c_void_type },
+ .{ "bool", .bool_type },
+ .{ "void", .void_type },
+ .{ "type", .type_type },
+ .{ "anyerror", .anyerror_type },
+ .{ "comptime_int", .comptime_int_type },
+ .{ "comptime_float", .comptime_float_type },
+ .{ "noreturn", .noreturn_type },
+ });
+ if (simple_types.get(name)) |tag| {
+ return TypedValue{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(tag),
+ };
+ }
+ return null;
+}
+
+fn nodeMayNeedMemoryLocation(start_node: *ast.Node) bool {
+ var node = start_node;
+ while (true) {
+ switch (node.tag) {
+ .Root,
+ .Use,
+ .TestDecl,
+ .DocComment,
+ .SwitchCase,
+ .SwitchElse,
+ .Else,
+ .Payload,
+ .PointerPayload,
+ .PointerIndexPayload,
+ .ContainerField,
+ .ErrorTag,
+ .FieldInitializer,
+ => unreachable,
+
+ .Return,
+ .Break,
+ .Continue,
+ .BitNot,
+ .BoolNot,
+ .VarDecl,
+ .Defer,
+ .AddressOf,
+ .OptionalType,
+ .Negation,
+ .NegationWrap,
+ .Resume,
+ .ArrayType,
+ .ArrayTypeSentinel,
+ .PtrType,
+ .SliceType,
+ .Suspend,
+ .AnyType,
+ .ErrorType,
+ .FnProto,
+ .AnyFrameType,
+ .IntegerLiteral,
+ .FloatLiteral,
+ .EnumLiteral,
+ .StringLiteral,
+ .MultilineStringLiteral,
+ .CharLiteral,
+ .BoolLiteral,
+ .NullLiteral,
+ .UndefinedLiteral,
+ .Unreachable,
+ .Identifier,
+ .ErrorSetDecl,
+ .ContainerDecl,
+ .Asm,
+ .Add,
+ .AddWrap,
+ .ArrayCat,
+ .ArrayMult,
+ .Assign,
+ .AssignBitAnd,
+ .AssignBitOr,
+ .AssignBitShiftLeft,
+ .AssignBitShiftRight,
+ .AssignBitXor,
+ .AssignDiv,
+ .AssignSub,
+ .AssignSubWrap,
+ .AssignMod,
+ .AssignAdd,
+ .AssignAddWrap,
+ .AssignMul,
+ .AssignMulWrap,
+ .BangEqual,
+ .BitAnd,
+ .BitOr,
+ .BitShiftLeft,
+ .BitShiftRight,
+ .BitXor,
+ .BoolAnd,
+ .BoolOr,
+ .Div,
+ .EqualEqual,
+ .ErrorUnion,
+ .GreaterOrEqual,
+ .GreaterThan,
+ .LessOrEqual,
+ .LessThan,
+ .MergeErrorSets,
+ .Mod,
+ .Mul,
+ .MulWrap,
+ .Range,
+ .Period,
+ .Sub,
+ .SubWrap,
+ .Slice,
+ .Deref,
+ .ArrayAccess,
+ .Block,
+ => return false,
+
+ // Forward the question to a sub-expression.
+ .GroupedExpression => node = node.castTag(.GroupedExpression).?.expr,
+ .Try => node = node.castTag(.Try).?.rhs,
+ .Await => node = node.castTag(.Await).?.rhs,
+ .Catch => node = node.castTag(.Catch).?.rhs,
+ .OrElse => node = node.castTag(.OrElse).?.rhs,
+ .Comptime => node = node.castTag(.Comptime).?.expr,
+ .Nosuspend => node = node.castTag(.Nosuspend).?.expr,
+ .UnwrapOptional => node = node.castTag(.UnwrapOptional).?.lhs,
+
+ // True because these are exactly the expressions we need memory locations for.
+ .ArrayInitializer,
+ .ArrayInitializerDot,
+ .StructInitializer,
+ .StructInitializerDot,
+ => return true,
+
+ // True because depending on comptime conditions, sub-expressions
+ // may be the kind that need memory locations.
+ .While,
+ .For,
+ .Switch,
+ .Call,
+ .BuiltinCall, // TODO some of these can return false
+ .LabeledBlock,
+ => return true,
+
+ // Depending on AST properties, they may need memory locations.
+ .If => return node.castTag(.If).?.@"else" != null,
+ }
+ }
+}
+
+/// Applies `rl` semantics to `inst`. Expressions which do not do their own handling of
+/// result locations must call this function on their result.
+/// As an example, if the `ResultLoc` is `ptr`, it will write the result to the pointer.
+/// If the `ResultLoc` is `ty`, it will coerce the result to the type.
+fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerError!*zir.Inst {
+ switch (rl) {
+ .none => return result,
+ .discard => {
+ // Emit a compile error for discarding error values.
+ _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
+ return result;
+ },
+ .ref => {
+ // We need a pointer but we have a value.
+ return addZIRUnOp(mod, scope, result.src, .ref, result);
+ },
+ .ty => |ty_inst| return addZIRBinOp(mod, scope, result.src, .as, ty_inst, result),
+ .ptr => |ptr_inst| {
+ const casted_result = try addZIRInst(mod, scope, result.src, zir.Inst.CoerceToPtrElem, .{
+ .ptr = ptr_inst,
+ .value = result,
+ }, .{});
+ _ = try addZIRBinOp(mod, scope, result.src, .store, ptr_inst, casted_result);
+ return casted_result;
+ },
+ .bitcasted_ptr => |bitcasted_ptr| {
+ return mod.fail(scope, result.src, "TODO implement rlWrap .bitcasted_ptr", .{});
+ },
+ .inferred_ptr => |alloc| {
+ return mod.fail(scope, result.src, "TODO implement rlWrap .inferred_ptr", .{});
+ },
+ .block_ptr => |block_ptr| {
+ return mod.fail(scope, result.src, "TODO implement rlWrap .block_ptr", .{});
+ },
+ }
+}
+
+fn rlWrapVoid(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node, result: void) InnerError!*zir.Inst {
+ const src = scope.tree().token_locs[node.firstToken()].start;
+ const void_inst = try addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.void),
+ .val = Value.initTag(.void_value),
+ });
+ return rlWrap(mod, scope, rl, void_inst);
+}
+
+fn rlWrapPtr(mod: *Module, scope: *Scope, rl: ResultLoc, ptr: *zir.Inst) InnerError!*zir.Inst {
+ if (rl == .ref) return ptr;
+
+ return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, ptr.src, .deref, ptr));
+}
+
+pub fn addZIRInstSpecial(
+ mod: *Module,
+ scope: *Scope,
+ src: usize,
+ comptime T: type,
+ positionals: std.meta.fieldInfo(T, "positionals").field_type,
+ kw_args: std.meta.fieldInfo(T, "kw_args").field_type,
+) !*T {
+ const gen_zir = scope.getGenZIR();
+ try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
+ const inst = try gen_zir.arena.create(T);
+ inst.* = .{
+ .base = .{
+ .tag = T.base_tag,
+ .src = src,
+ },
+ .positionals = positionals,
+ .kw_args = kw_args,
+ };
+ gen_zir.instructions.appendAssumeCapacity(&inst.base);
+ return inst;
+}
+
+pub fn addZIRNoOpT(mod: *Module, scope: *Scope, src: usize, tag: zir.Inst.Tag) !*zir.Inst.NoOp {
+ const gen_zir = scope.getGenZIR();
+ try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
+ const inst = try gen_zir.arena.create(zir.Inst.NoOp);
+ inst.* = .{
+ .base = .{
+ .tag = tag,
+ .src = src,
+ },
+ .positionals = .{},
+ .kw_args = .{},
+ };
+ gen_zir.instructions.appendAssumeCapacity(&inst.base);
+ return inst;
+}
+
+pub fn addZIRNoOp(mod: *Module, scope: *Scope, src: usize, tag: zir.Inst.Tag) !*zir.Inst {
+ const inst = try addZIRNoOpT(mod, scope, src, tag);
+ return &inst.base;
+}
+
+pub fn addZIRUnOp(
+ mod: *Module,
+ scope: *Scope,
+ src: usize,
+ tag: zir.Inst.Tag,
+ operand: *zir.Inst,
+) !*zir.Inst {
+ const gen_zir = scope.getGenZIR();
+ try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
+ const inst = try gen_zir.arena.create(zir.Inst.UnOp);
+ inst.* = .{
+ .base = .{
+ .tag = tag,
+ .src = src,
+ },
+ .positionals = .{
+ .operand = operand,
+ },
+ .kw_args = .{},
+ };
+ gen_zir.instructions.appendAssumeCapacity(&inst.base);
+ return &inst.base;
+}
+
+pub fn addZIRBinOp(
+ mod: *Module,
+ scope: *Scope,
+ src: usize,
+ tag: zir.Inst.Tag,
+ lhs: *zir.Inst,
+ rhs: *zir.Inst,
+) !*zir.Inst {
+ const gen_zir = scope.getGenZIR();
+ try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
+ const inst = try gen_zir.arena.create(zir.Inst.BinOp);
+ inst.* = .{
+ .base = .{
+ .tag = tag,
+ .src = src,
+ },
+ .positionals = .{
+ .lhs = lhs,
+ .rhs = rhs,
+ },
+ .kw_args = .{},
+ };
+ gen_zir.instructions.appendAssumeCapacity(&inst.base);
+ return &inst.base;
+}
+
+pub fn addZIRInstBlock(
+ mod: *Module,
+ scope: *Scope,
+ src: usize,
+ tag: zir.Inst.Tag,
+ body: zir.Module.Body,
+) !*zir.Inst.Block {
+ const gen_zir = scope.getGenZIR();
+ try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
+ const inst = try gen_zir.arena.create(zir.Inst.Block);
+ inst.* = .{
+ .base = .{
+ .tag = tag,
+ .src = src,
+ },
+ .positionals = .{
+ .body = body,
+ },
+ .kw_args = .{},
+ };
+ gen_zir.instructions.appendAssumeCapacity(&inst.base);
+ return inst;
+}
+
+pub fn addZIRInst(
+ mod: *Module,
+ scope: *Scope,
+ src: usize,
+ comptime T: type,
+ positionals: std.meta.fieldInfo(T, "positionals").field_type,
+ kw_args: std.meta.fieldInfo(T, "kw_args").field_type,
+) !*zir.Inst {
+ const inst_special = try addZIRInstSpecial(mod, scope, src, T, positionals, kw_args);
+ return &inst_special.base;
+}
+
+/// TODO The existence of this function is a workaround for a bug in stage1.
+pub fn addZIRInstConst(mod: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*zir.Inst {
+ const P = std.meta.fieldInfo(zir.Inst.Const, "positionals").field_type;
+ return addZIRInst(mod, scope, src, zir.Inst.Const, P{ .typed_value = typed_value }, .{});
+}
+
+/// TODO The existence of this function is a workaround for a bug in stage1.
+pub fn addZIRInstLoop(mod: *Module, scope: *Scope, src: usize, body: zir.Module.Body) !*zir.Inst.Loop {
+ const P = std.meta.fieldInfo(zir.Inst.Loop, "positionals").field_type;
+ return addZIRInstSpecial(mod, scope, src, zir.Inst.Loop, P{ .body = body }, .{});
+}
diff --git a/src/bigfloat.cpp b/src/bigfloat.cpp
deleted file mode 100644
index a2a3a3b69cbbfbc91b80d85db97d1d7b256058c3..0000000000000000000000000000000000000000
--- a/src/bigfloat.cpp
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (c) 2017 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#include "bigfloat.hpp"
-#include "bigint.hpp"
-#include "buffer.hpp"
-#include "softfloat.hpp"
-#include "parse_f128.h"
-#include
-#include
-#include
-
-
-void bigfloat_init_128(BigFloat *dest, float128_t x) {
- dest->value = x;
-}
-
-void bigfloat_init_16(BigFloat *dest, float16_t x) {
- f16_to_f128M(x, &dest->value);
-}
-
-void bigfloat_init_32(BigFloat *dest, float x) {
- float32_t f32_val;
- memcpy(&f32_val, &x, sizeof(float));
- f32_to_f128M(f32_val, &dest->value);
-}
-
-void bigfloat_init_64(BigFloat *dest, double x) {
- float64_t f64_val;
- memcpy(&f64_val, &x, sizeof(double));
- f64_to_f128M(f64_val, &dest->value);
-}
-
-void bigfloat_init_bigfloat(BigFloat *dest, const BigFloat *x) {
- memcpy(&dest->value, &x->value, sizeof(float128_t));
-}
-
-void bigfloat_init_bigint(BigFloat *dest, const BigInt *op) {
- ui32_to_f128M(0, &dest->value);
- if (op->digit_count == 0)
- return;
-
- float128_t base;
- ui64_to_f128M(UINT64_MAX, &base);
- const uint64_t *digits = bigint_ptr(op);
-
- for (size_t i = op->digit_count - 1;;) {
- float128_t digit_f128;
- ui64_to_f128M(digits[i], &digit_f128);
-
- f128M_mulAdd(&dest->value, &base, &digit_f128, &dest->value);
-
- if (i == 0) {
- if (op->is_negative) {
- float128_t zero_f128;
- ui32_to_f128M(0, &zero_f128);
- f128M_sub(&zero_f128, &dest->value, &dest->value);
- }
- return;
- }
- i -= 1;
- }
-}
-
-Error bigfloat_init_buf(BigFloat *dest, const uint8_t *buf_ptr, size_t buf_len) {
- char *str_begin = (char *)buf_ptr;
- char *str_end;
-
- errno = 0;
- dest->value = parse_f128(str_begin, &str_end);
- if (errno) {
- return ErrorOverflow;
- }
-
- assert(str_end <= ((char*)buf_ptr) + buf_len);
- return ErrorNone;
-}
-
-void bigfloat_add(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {
- f128M_add(&op1->value, &op2->value, &dest->value);
-}
-
-void bigfloat_negate(BigFloat *dest, const BigFloat *op) {
- float128_t zero_f128;
- ui32_to_f128M(0, &zero_f128);
- f128M_sub(&zero_f128, &op->value, &dest->value);
-}
-
-void bigfloat_sub(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {
- f128M_sub(&op1->value, &op2->value, &dest->value);
-}
-
-void bigfloat_mul(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {
- f128M_mul(&op1->value, &op2->value, &dest->value);
-}
-
-void bigfloat_div(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {
- f128M_div(&op1->value, &op2->value, &dest->value);
-}
-
-void bigfloat_div_trunc(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {
- f128M_div(&op1->value, &op2->value, &dest->value);
- f128M_roundToInt(&dest->value, softfloat_round_minMag, false, &dest->value);
-}
-
-void bigfloat_div_floor(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {
- f128M_div(&op1->value, &op2->value, &dest->value);
- f128M_roundToInt(&dest->value, softfloat_round_min, false, &dest->value);
-}
-
-void bigfloat_rem(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {
- f128M_rem(&op1->value, &op2->value, &dest->value);
-}
-
-void bigfloat_mod(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {
- f128M_rem(&op1->value, &op2->value, &dest->value);
- f128M_add(&dest->value, &op2->value, &dest->value);
- f128M_rem(&dest->value, &op2->value, &dest->value);
-}
-
-void bigfloat_append_buf(Buf *buf, const BigFloat *op) {
- const size_t extra_len = 100;
- size_t old_len = buf_len(buf);
- buf_resize(buf, old_len + extra_len);
-
- // TODO actually print f128
- float64_t f64_value = f128M_to_f64(&op->value);
- double double_value;
- memcpy(&double_value, &f64_value, sizeof(double));
-
- int len = snprintf(buf_ptr(buf) + old_len, extra_len, "%f", double_value);
- assert(len > 0);
- buf_resize(buf, old_len + len);
-}
-
-Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2) {
- if (f128M_lt(&op1->value, &op2->value)) {
- return CmpLT;
- } else if (f128M_eq(&op1->value, &op2->value)) {
- return CmpEQ;
- } else {
- return CmpGT;
- }
-}
-
-float16_t bigfloat_to_f16(const BigFloat *bigfloat) {
- return f128M_to_f16(&bigfloat->value);
-}
-
-float bigfloat_to_f32(const BigFloat *bigfloat) {
- float32_t f32_value = f128M_to_f32(&bigfloat->value);
- float result;
- memcpy(&result, &f32_value, sizeof(float));
- return result;
-}
-
-double bigfloat_to_f64(const BigFloat *bigfloat) {
- float64_t f64_value = f128M_to_f64(&bigfloat->value);
- double result;
- memcpy(&result, &f64_value, sizeof(double));
- return result;
-}
-
-float128_t bigfloat_to_f128(const BigFloat *bigfloat) {
- return bigfloat->value;
-}
-
-Cmp bigfloat_cmp_zero(const BigFloat *bigfloat) {
- float128_t zero_float;
- ui32_to_f128M(0, &zero_float);
- if (f128M_lt(&bigfloat->value, &zero_float)) {
- return CmpLT;
- } else if (f128M_eq(&bigfloat->value, &zero_float)) {
- return CmpEQ;
- } else {
- return CmpGT;
- }
-}
-
-bool bigfloat_has_fraction(const BigFloat *bigfloat) {
- float128_t floored;
- f128M_roundToInt(&bigfloat->value, softfloat_round_minMag, false, &floored);
- return !f128M_eq(&floored, &bigfloat->value);
-}
-
-void bigfloat_sqrt(BigFloat *dest, const BigFloat *op) {
- f128M_sqrt(&op->value, &dest->value);
-}
-
-bool bigfloat_is_nan(const BigFloat *op) {
- return f128M_isSignalingNaN(&op->value);
-}
diff --git a/src/bigfloat.hpp b/src/bigfloat.hpp
deleted file mode 100644
index 3ed6624fdcbff7d57629792599320558db71542a..0000000000000000000000000000000000000000
--- a/src/bigfloat.hpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (c) 2017 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#ifndef ZIG_BIGFLOAT_HPP
-#define ZIG_BIGFLOAT_HPP
-
-#include "bigint.hpp"
-#include "error.hpp"
-#include
-#include
-
-#include "softfloat_types.h"
-
-
-struct BigFloat {
- float128_t value;
-};
-
-struct Buf;
-
-void bigfloat_init_16(BigFloat *dest, float16_t x);
-void bigfloat_init_32(BigFloat *dest, float x);
-void bigfloat_init_64(BigFloat *dest, double x);
-void bigfloat_init_128(BigFloat *dest, float128_t x);
-void bigfloat_init_bigfloat(BigFloat *dest, const BigFloat *x);
-void bigfloat_init_bigint(BigFloat *dest, const BigInt *op);
-Error bigfloat_init_buf(BigFloat *dest, const uint8_t *buf_ptr, size_t buf_len);
-
-float16_t bigfloat_to_f16(const BigFloat *bigfloat);
-float bigfloat_to_f32(const BigFloat *bigfloat);
-double bigfloat_to_f64(const BigFloat *bigfloat);
-float128_t bigfloat_to_f128(const BigFloat *bigfloat);
-
-void bigfloat_add(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
-void bigfloat_negate(BigFloat *dest, const BigFloat *op);
-void bigfloat_sub(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
-void bigfloat_mul(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
-void bigfloat_div(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
-void bigfloat_div_trunc(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
-void bigfloat_div_floor(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
-void bigfloat_rem(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
-void bigfloat_mod(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
-void bigfloat_sqrt(BigFloat *dest, const BigFloat *op);
-void bigfloat_append_buf(Buf *buf, const BigFloat *op);
-Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2);
-
-bool bigfloat_is_nan(const BigFloat *op);
-
-// convenience functions
-Cmp bigfloat_cmp_zero(const BigFloat *bigfloat);
-bool bigfloat_has_fraction(const BigFloat *bigfloat);
-
-#endif
diff --git a/src/bigint.cpp b/src/bigint.cpp
deleted file mode 100644
index 79a05e95a52a862c8728be21318642cf2fecda45..0000000000000000000000000000000000000000
--- a/src/bigint.cpp
+++ /dev/null
@@ -1,1786 +0,0 @@
-/*
- * Copyright (c) 2017 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#include "bigfloat.hpp"
-#include "bigint.hpp"
-#include "buffer.hpp"
-#include "list.hpp"
-#include "os.hpp"
-#include "softfloat.hpp"
-
-#include
-#include
-
-static uint64_t bigint_as_unsigned(const BigInt *bigint);
-
-static void bigint_normalize(BigInt *dest) {
- const uint64_t *digits = bigint_ptr(dest);
-
- size_t last_nonzero_digit = SIZE_MAX;
- for (size_t i = 0; i < dest->digit_count; i += 1) {
- uint64_t digit = digits[i];
- if (digit != 0) {
- last_nonzero_digit = i;
- }
- }
- if (last_nonzero_digit == SIZE_MAX) {
- dest->is_negative = false;
- dest->digit_count = 0;
- } else {
- dest->digit_count = last_nonzero_digit + 1;
- if (last_nonzero_digit == 0) {
- dest->data.digit = digits[0];
- }
- }
-}
-
-static uint8_t digit_to_char(uint8_t digit, bool uppercase) {
- if (digit <= 9) {
- return digit + '0';
- } else if (digit <= 35) {
- return (digit - 10) + (uppercase ? 'A' : 'a');
- } else {
- zig_unreachable();
- }
-}
-
-size_t bigint_bits_needed(const BigInt *op) {
- size_t full_bits = op->digit_count * 64;
- size_t leading_zero_count = bigint_clz(op, full_bits);
- size_t bits_needed = full_bits - leading_zero_count;
- return bits_needed + op->is_negative;
-}
-
-static void to_twos_complement(BigInt *dest, const BigInt *op, size_t bit_count) {
- if (bit_count == 0 || op->digit_count == 0) {
- bigint_init_unsigned(dest, 0);
- return;
- }
- if (op->is_negative) {
- BigInt negated = {0};
- bigint_negate(&negated, op);
-
- BigInt inverted = {0};
- bigint_not(&inverted, &negated, bit_count, false);
-
- BigInt one = {0};
- bigint_init_unsigned(&one, 1);
-
- bigint_add(dest, &inverted, &one);
- return;
- }
-
- dest->is_negative = false;
- const uint64_t *op_digits = bigint_ptr(op);
- if (op->digit_count == 1) {
- dest->data.digit = op_digits[0];
- if (bit_count < 64) {
- dest->data.digit &= (1ULL << bit_count) - 1;
- }
- dest->digit_count = 1;
- bigint_normalize(dest);
- return;
- }
- size_t digits_to_copy = bit_count / 64;
- size_t leftover_bits = bit_count % 64;
- dest->digit_count = digits_to_copy + ((leftover_bits == 0) ? 0 : 1);
- if (dest->digit_count == 1 && leftover_bits == 0) {
- dest->data.digit = op_digits[0];
- if (dest->data.digit == 0) dest->digit_count = 0;
- return;
- }
- dest->data.digits = heap::c_allocator.allocate_nonzero(dest->digit_count);
- for (size_t i = 0; i < digits_to_copy; i += 1) {
- uint64_t digit = (i < op->digit_count) ? op_digits[i] : 0;
- dest->data.digits[i] = digit;
- }
- if (leftover_bits != 0) {
- uint64_t digit = (digits_to_copy < op->digit_count) ? op_digits[digits_to_copy] : 0;
- dest->data.digits[digits_to_copy] = digit & ((1ULL << leftover_bits) - 1);
- }
- bigint_normalize(dest);
-}
-
-static bool bit_at_index(const BigInt *bi, size_t index) {
- size_t digit_index = index / 64;
- if (digit_index >= bi->digit_count)
- return false;
- size_t digit_bit_index = index % 64;
- const uint64_t *digits = bigint_ptr(bi);
- uint64_t digit = digits[digit_index];
- return ((digit >> digit_bit_index) & 0x1) == 0x1;
-}
-
-static void from_twos_complement(BigInt *dest, const BigInt *src, size_t bit_count, bool is_signed) {
- assert(!src->is_negative);
-
- if (bit_count == 0 || src->digit_count == 0) {
- bigint_init_unsigned(dest, 0);
- return;
- }
-
- if (is_signed && bit_at_index(src, bit_count - 1)) {
- BigInt negative_one = {0};
- bigint_init_signed(&negative_one, -1);
-
- BigInt minus_one = {0};
- bigint_add(&minus_one, src, &negative_one);
-
- BigInt inverted = {0};
- bigint_not(&inverted, &minus_one, bit_count, false);
-
- bigint_negate(dest, &inverted);
- return;
-
- }
-
- bigint_init_bigint(dest, src);
-}
-
-void bigint_init_unsigned(BigInt *dest, uint64_t x) {
- if (x == 0) {
- dest->digit_count = 0;
- dest->is_negative = false;
- return;
- }
- dest->digit_count = 1;
- dest->data.digit = x;
- dest->is_negative = false;
-}
-
-void bigint_init_signed(BigInt *dest, int64_t x) {
- if (x >= 0) {
- return bigint_init_unsigned(dest, x);
- }
- dest->is_negative = true;
- dest->digit_count = 1;
- dest->data.digit = ((uint64_t)(-(x + 1))) + 1;
-}
-
-void bigint_init_data(BigInt *dest, const uint64_t *digits, size_t digit_count, bool is_negative) {
- if (digit_count == 0) {
- return bigint_init_unsigned(dest, 0);
- } else if (digit_count == 1) {
- dest->digit_count = 1;
- dest->data.digit = digits[0];
- dest->is_negative = is_negative;
- bigint_normalize(dest);
- return;
- }
-
- dest->digit_count = digit_count;
- dest->is_negative = is_negative;
- dest->data.digits = heap::c_allocator.allocate_nonzero(digit_count);
- memcpy(dest->data.digits, digits, sizeof(uint64_t) * digit_count);
-
- bigint_normalize(dest);
-}
-
-void bigint_init_bigint(BigInt *dest, const BigInt *src) {
- if (src->digit_count == 0) {
- return bigint_init_unsigned(dest, 0);
- } else if (src->digit_count == 1) {
- dest->digit_count = 1;
- dest->data.digit = src->data.digit;
- dest->is_negative = src->is_negative;
- return;
- }
- dest->is_negative = src->is_negative;
- dest->digit_count = src->digit_count;
- dest->data.digits = heap::c_allocator.allocate_nonzero(dest->digit_count);
- memcpy(dest->data.digits, src->data.digits, sizeof(uint64_t) * dest->digit_count);
-}
-
-void bigint_deinit(BigInt *bi) {
- if (bi->digit_count > 1)
- heap::c_allocator.deallocate(bi->data.digits, bi->digit_count);
-}
-
-void bigint_init_bigfloat(BigInt *dest, const BigFloat *op) {
- float128_t zero;
- ui32_to_f128M(0, &zero);
-
- dest->is_negative = f128M_lt(&op->value, &zero);
- float128_t abs_val;
- if (dest->is_negative) {
- f128M_sub(&zero, &op->value, &abs_val);
- } else {
- memcpy(&abs_val, &op->value, sizeof(float128_t));
- }
-
- float128_t max_u64;
- ui64_to_f128M(UINT64_MAX, &max_u64);
- if (f128M_le(&abs_val, &max_u64)) {
- dest->digit_count = 1;
- dest->data.digit = f128M_to_ui64(&op->value, softfloat_round_minMag, false);
- bigint_normalize(dest);
- return;
- }
-
- float128_t amt;
- f128M_div(&abs_val, &max_u64, &amt);
- float128_t remainder;
- f128M_rem(&abs_val, &max_u64, &remainder);
-
- dest->digit_count = 2;
- dest->data.digits = heap::c_allocator.allocate_nonzero(dest->digit_count);
- dest->data.digits[0] = f128M_to_ui64(&remainder, softfloat_round_minMag, false);
- dest->data.digits[1] = f128M_to_ui64(&amt, softfloat_round_minMag, false);
- bigint_normalize(dest);
-}
-
-bool bigint_fits_in_bits(const BigInt *bn, size_t bit_count, bool is_signed) {
- assert(bn->digit_count != 1 || bn->data.digit != 0);
- if (bit_count == 0) {
- return bigint_cmp_zero(bn) == CmpEQ;
- }
- if (bn->digit_count == 0) {
- return true;
- }
-
- if (!is_signed) {
- if(bn->is_negative) return false;
- size_t full_bits = bn->digit_count * 64;
- size_t leading_zero_count = bigint_clz(bn, full_bits);
- return bit_count >= full_bits - leading_zero_count;
- }
-
- BigInt one = {0};
- bigint_init_unsigned(&one, 1);
-
- BigInt shl_amt = {0};
- bigint_init_unsigned(&shl_amt, bit_count - 1);
-
- BigInt max_value_plus_one = {0};
- bigint_shl(&max_value_plus_one, &one, &shl_amt);
-
- BigInt max_value = {0};
- bigint_sub(&max_value, &max_value_plus_one, &one);
-
- BigInt min_value = {0};
- bigint_negate(&min_value, &max_value_plus_one);
-
- Cmp min_cmp = bigint_cmp(bn, &min_value);
- Cmp max_cmp = bigint_cmp(bn, &max_value);
-
- return (min_cmp == CmpGT || min_cmp == CmpEQ) && (max_cmp == CmpLT || max_cmp == CmpEQ);
-}
-
-void bigint_write_twos_complement(const BigInt *big_int, uint8_t *buf, size_t bit_count, bool is_big_endian) {
- if (bit_count == 0)
- return;
-
- BigInt twos_comp = {0};
- to_twos_complement(&twos_comp, big_int, bit_count);
-
- const uint64_t *twos_comp_digits = bigint_ptr(&twos_comp);
-
- size_t bits_in_last_digit = bit_count % 64;
- if (bits_in_last_digit == 0) bits_in_last_digit = 64;
- size_t bytes_in_last_digit = (bits_in_last_digit + 7) / 8;
- size_t unwritten_byte_count = 8 - bytes_in_last_digit;
-
- if (is_big_endian) {
- size_t last_digit_index = (bit_count - 1) / 64;
- size_t digit_index = last_digit_index;
- size_t buf_index = 0;
- for (;;) {
- uint64_t x = (digit_index < twos_comp.digit_count) ? twos_comp_digits[digit_index] : 0;
-
- for (size_t byte_index = 7;;) {
- uint8_t byte = x & 0xff;
- if (digit_index == last_digit_index) {
- buf[buf_index + byte_index - unwritten_byte_count] = byte;
- if (byte_index == unwritten_byte_count) break;
- } else {
- buf[buf_index + byte_index] = byte;
- }
-
- if (byte_index == 0) break;
- byte_index -= 1;
- x >>= 8;
- }
-
- if (digit_index == 0) break;
- digit_index -= 1;
- if (digit_index == last_digit_index) {
- buf_index += bytes_in_last_digit;
- } else {
- buf_index += 8;
- }
- }
- } else {
- size_t digit_count = (bit_count + 63) / 64;
- size_t buf_index = 0;
- for (size_t digit_index = 0; digit_index < digit_count; digit_index += 1) {
- uint64_t x = (digit_index < twos_comp.digit_count) ? twos_comp_digits[digit_index] : 0;
-
- for (size_t byte_index = 0;
- byte_index < 8 && (digit_index + 1 < digit_count || byte_index < bytes_in_last_digit);
- byte_index += 1)
- {
- uint8_t byte = x & 0xff;
- buf[buf_index] = byte;
- buf_index += 1;
- x >>= 8;
- }
- }
- }
-}
-
-
-void bigint_read_twos_complement(BigInt *dest, const uint8_t *buf, size_t bit_count, bool is_big_endian,
- bool is_signed)
-{
- if (bit_count == 0) {
- bigint_init_unsigned(dest, 0);
- return;
- }
-
- dest->digit_count = (bit_count + 63) / 64;
- uint64_t *digits;
- if (dest->digit_count == 1) {
- digits = &dest->data.digit;
- } else {
- digits = heap::c_allocator.allocate_nonzero(dest->digit_count);
- dest->data.digits = digits;
- }
-
- size_t bits_in_last_digit = bit_count % 64;
- if (bits_in_last_digit == 0) {
- bits_in_last_digit = 64;
- }
- size_t bytes_in_last_digit = (bits_in_last_digit + 7) / 8;
- size_t unread_byte_count = 8 - bytes_in_last_digit;
-
- if (is_big_endian) {
- size_t buf_index = 0;
- uint64_t digit = 0;
- for (size_t byte_index = unread_byte_count; byte_index < 8; byte_index += 1) {
- uint8_t byte = buf[buf_index];
- buf_index += 1;
- digit <<= 8;
- digit |= byte;
- }
- digits[dest->digit_count - 1] = digit;
- for (size_t digit_index = 1; digit_index < dest->digit_count; digit_index += 1) {
- digit = 0;
- for (size_t byte_index = 0; byte_index < 8; byte_index += 1) {
- uint8_t byte = buf[buf_index];
- buf_index += 1;
- digit <<= 8;
- digit |= byte;
- }
- digits[dest->digit_count - 1 - digit_index] = digit;
- }
- } else {
- size_t buf_index = 0;
- for (size_t digit_index = 0; digit_index < dest->digit_count; digit_index += 1) {
- uint64_t digit = 0;
- size_t end_byte_index = (digit_index == dest->digit_count - 1) ? bytes_in_last_digit : 8;
- for (size_t byte_index = 0; byte_index < end_byte_index; byte_index += 1) {
- uint64_t byte = buf[buf_index];
- buf_index += 1;
-
- digit |= byte << (8 * byte_index);
- }
- digits[digit_index] = digit;
- }
- }
-
- if (is_signed) {
- bigint_normalize(dest);
- BigInt tmp = {0};
- bigint_init_bigint(&tmp, dest);
- from_twos_complement(dest, &tmp, bit_count, true);
- } else {
- dest->is_negative = false;
- bigint_normalize(dest);
- }
-}
-
-#if defined(_MSC_VER)
-static bool add_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) {
- *result = op1 + op2;
- return *result < op1 || *result < op2;
-}
-
-static bool sub_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) {
- *result = op1 - op2;
- return *result > op1;
-}
-
-bool mul_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) {
- *result = op1 * op2;
-
- if (op1 == 0 || op2 == 0)
- return false;
-
- if (op1 > UINT64_MAX / op2)
- return true;
-
- if (op2 > UINT64_MAX / op1)
- return true;
-
- return false;
-}
-#else
-static bool add_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) {
- return __builtin_uaddll_overflow((unsigned long long)op1, (unsigned long long)op2,
- (unsigned long long *)result);
-}
-
-static bool sub_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) {
- return __builtin_usubll_overflow((unsigned long long)op1, (unsigned long long)op2,
- (unsigned long long *)result);
-}
-
-bool mul_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) {
- return __builtin_umulll_overflow((unsigned long long)op1, (unsigned long long)op2,
- (unsigned long long *)result);
-}
-#endif
-
-void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2) {
- if (op1->digit_count == 0) {
- return bigint_init_bigint(dest, op2);
- }
- if (op2->digit_count == 0) {
- return bigint_init_bigint(dest, op1);
- }
- if (op1->is_negative == op2->is_negative) {
- dest->is_negative = op1->is_negative;
-
- const uint64_t *op1_digits = bigint_ptr(op1);
- const uint64_t *op2_digits = bigint_ptr(op2);
- bool overflow = add_u64_overflow(op1_digits[0], op2_digits[0], &dest->data.digit);
- if (overflow == 0 && op1->digit_count == 1 && op2->digit_count == 1) {
- dest->digit_count = 1;
- bigint_normalize(dest);
- return;
- }
- size_t i = 1;
- uint64_t first_digit = dest->data.digit;
- dest->data.digits = heap::c_allocator.allocate_nonzero(max(op1->digit_count, op2->digit_count) + 1);
- dest->data.digits[0] = first_digit;
-
- for (;;) {
- bool found_digit = false;
- uint64_t x = overflow;
- overflow = 0;
-
- if (i < op1->digit_count) {
- found_digit = true;
- uint64_t digit = op1_digits[i];
- overflow += add_u64_overflow(x, digit, &x);
- }
-
- if (i < op2->digit_count) {
- found_digit = true;
- uint64_t digit = op2_digits[i];
- overflow += add_u64_overflow(x, digit, &x);
- }
-
- dest->data.digits[i] = x;
- i += 1;
-
- if (!found_digit) {
- dest->digit_count = i;
- bigint_normalize(dest);
- return;
- }
- }
- }
- const BigInt *op_pos;
- const BigInt *op_neg;
- if (op1->is_negative) {
- op_neg = op1;
- op_pos = op2;
- } else {
- op_pos = op1;
- op_neg = op2;
- }
-
- BigInt op_neg_abs = {0};
- bigint_negate(&op_neg_abs, op_neg);
- const BigInt *bigger_op;
- const BigInt *smaller_op;
- switch (bigint_cmp(op_pos, &op_neg_abs)) {
- case CmpEQ:
- bigint_init_unsigned(dest, 0);
- return;
- case CmpLT:
- bigger_op = &op_neg_abs;
- smaller_op = op_pos;
- dest->is_negative = true;
- break;
- case CmpGT:
- bigger_op = op_pos;
- smaller_op = &op_neg_abs;
- dest->is_negative = false;
- break;
- }
- const uint64_t *bigger_op_digits = bigint_ptr(bigger_op);
- const uint64_t *smaller_op_digits = bigint_ptr(smaller_op);
- uint64_t overflow = sub_u64_overflow(bigger_op_digits[0], smaller_op_digits[0], &dest->data.digit);
- if (overflow == 0 && bigger_op->digit_count == 1 && smaller_op->digit_count == 1) {
- dest->digit_count = 1;
- bigint_normalize(dest);
- return;
- }
- uint64_t first_digit = dest->data.digit;
- dest->data.digits = heap::c_allocator.allocate_nonzero(bigger_op->digit_count);
- dest->data.digits[0] = first_digit;
- size_t i = 1;
-
- for (;;) {
- bool found_digit = false;
- uint64_t x = bigger_op_digits[i];
- uint64_t prev_overflow = overflow;
- overflow = 0;
-
- if (i < smaller_op->digit_count) {
- found_digit = true;
- uint64_t digit = smaller_op_digits[i];
- overflow += sub_u64_overflow(x, digit, &x);
- }
- if (sub_u64_overflow(x, prev_overflow, &x)) {
- found_digit = true;
- overflow += 1;
- }
- dest->data.digits[i] = x;
- i += 1;
-
- if (!found_digit || i >= bigger_op->digit_count)
- break;
- }
- assert(overflow == 0);
- dest->digit_count = i;
- bigint_normalize(dest);
-}
-
-void bigint_add_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) {
- BigInt unwrapped = {0};
- bigint_add(&unwrapped, op1, op2);
- bigint_truncate(dest, &unwrapped, bit_count, is_signed);
-}
-
-void bigint_sub(BigInt *dest, const BigInt *op1, const BigInt *op2) {
- BigInt op2_negated = {0};
- bigint_negate(&op2_negated, op2);
- return bigint_add(dest, op1, &op2_negated);
-}
-
-void bigint_sub_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) {
- BigInt op2_negated = {0};
- bigint_negate(&op2_negated, op2);
- return bigint_add_wrap(dest, op1, &op2_negated, bit_count, is_signed);
-}
-
-static void mul_overflow(uint64_t op1, uint64_t op2, uint64_t *lo, uint64_t *hi) {
- uint64_t u1 = (op1 & 0xffffffff);
- uint64_t v1 = (op2 & 0xffffffff);
- uint64_t t = (u1 * v1);
- uint64_t w3 = (t & 0xffffffff);
- uint64_t k = (t >> 32);
-
- op1 >>= 32;
- t = (op1 * v1) + k;
- k = (t & 0xffffffff);
- uint64_t w1 = (t >> 32);
-
- op2 >>= 32;
- t = (u1 * op2) + k;
- k = (t >> 32);
-
- *hi = (op1 * op2) + w1 + k;
- *lo = (t << 32) + w3;
-}
-
-static void mul_scalar(BigInt *dest, const BigInt *op, uint64_t scalar) {
- bigint_init_unsigned(dest, 0);
-
- BigInt bi_64;
- bigint_init_unsigned(&bi_64, 64);
-
- const uint64_t *op_digits = bigint_ptr(op);
- size_t i = op->digit_count - 1;
-
- for (;;) {
- BigInt shifted;
- bigint_shl(&shifted, dest, &bi_64);
-
- uint64_t result_scalar;
- uint64_t carry_scalar;
- mul_overflow(scalar, op_digits[i], &result_scalar, &carry_scalar);
-
- BigInt result;
- bigint_init_unsigned(&result, result_scalar);
-
- BigInt carry;
- bigint_init_unsigned(&carry, carry_scalar);
-
- BigInt carry_shifted;
- bigint_shl(&carry_shifted, &carry, &bi_64);
-
- BigInt tmp;
- bigint_add(&tmp, &shifted, &carry_shifted);
-
- bigint_add(dest, &tmp, &result);
-
- if (i == 0) {
- break;
- }
- i -= 1;
- }
-}
-
-void bigint_mul(BigInt *dest, const BigInt *op1, const BigInt *op2) {
- if (op1->digit_count == 0 || op2->digit_count == 0) {
- return bigint_init_unsigned(dest, 0);
- }
- const uint64_t *op1_digits = bigint_ptr(op1);
- const uint64_t *op2_digits = bigint_ptr(op2);
-
- uint64_t carry;
- mul_overflow(op1_digits[0], op2_digits[0], &dest->data.digit, &carry);
- if (carry == 0 && op1->digit_count == 1 && op2->digit_count == 1) {
- dest->is_negative = (op1->is_negative != op2->is_negative);
- dest->digit_count = 1;
- bigint_normalize(dest);
- return;
- }
-
- bigint_init_unsigned(dest, 0);
-
- BigInt bi_64;
- bigint_init_unsigned(&bi_64, 64);
-
- size_t i = op2->digit_count - 1;
- for (;;) {
- BigInt shifted;
- bigint_shl(&shifted, dest, &bi_64);
-
- BigInt scalar_result;
- mul_scalar(&scalar_result, op1, op2_digits[i]);
-
- bigint_add(dest, &scalar_result, &shifted);
-
- if (i == 0) {
- break;
- }
- i -= 1;
- }
-
- dest->is_negative = (op1->is_negative != op2->is_negative);
- bigint_normalize(dest);
-}
-
-void bigint_mul_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) {
- BigInt unwrapped = {0};
- bigint_mul(&unwrapped, op1, op2);
- bigint_truncate(dest, &unwrapped, bit_count, is_signed);
-}
-
-enum ZeroBehavior {
- /// \brief The returned value is undefined.
- ZB_Undefined,
- /// \brief The returned value is numeric_limits::max()
- ZB_Max,
- /// \brief The returned value is numeric_limits::digits
- ZB_Width
-};
-
-template struct LeadingZerosCounter {
- static std::size_t count(T Val, ZeroBehavior) {
- if (!Val)
- return std::numeric_limits::digits;
-
- // Bisection method.
- std::size_t ZeroBits = 0;
- for (T Shift = std::numeric_limits::digits >> 1; Shift; Shift >>= 1) {
- T Tmp = Val >> Shift;
- if (Tmp)
- Val = Tmp;
- else
- ZeroBits |= Shift;
- }
- return ZeroBits;
- }
-};
-
-#if __GNUC__ >= 4 || defined(_MSC_VER)
-template struct LeadingZerosCounter {
- static std::size_t count(T Val, ZeroBehavior ZB) {
- if (ZB != ZB_Undefined && Val == 0)
- return 32;
-
-#if defined(_MSC_VER)
- unsigned long Index;
- _BitScanReverse(&Index, Val);
- return Index ^ 31;
-#else
- return __builtin_clz(Val);
-#endif
- }
-};
-
-#if !defined(_MSC_VER) || defined(_M_X64)
-template struct LeadingZerosCounter {
- static std::size_t count(T Val, ZeroBehavior ZB) {
- if (ZB != ZB_Undefined && Val == 0)
- return 64;
-
-#if defined(_MSC_VER)
- unsigned long Index;
- _BitScanReverse64(&Index, Val);
- return Index ^ 63;
-#else
- return __builtin_clzll(Val);
-#endif
- }
-};
-#endif
-#endif
-
-/// \brief Count number of 0's from the most significant bit to the least
-/// stopping at the first 1.
-///
-/// Only unsigned integral types are allowed.
-///
-/// \param ZB the behavior on an input of 0. Only ZB_Width and ZB_Undefined are
-/// valid arguments.
-template
-std::size_t countLeadingZeros(T Val, ZeroBehavior ZB = ZB_Width) {
- static_assert(std::numeric_limits::is_integer &&
- !std::numeric_limits::is_signed,
- "Only unsigned integral types are allowed.");
- return LeadingZerosCounter::count(Val, ZB);
-}
-
-/// Make a 64-bit integer from a high / low pair of 32-bit integers.
-constexpr inline uint64_t Make_64(uint32_t High, uint32_t Low) {
- return ((uint64_t)High << 32) | (uint64_t)Low;
-}
-
-/// Return the high 32 bits of a 64 bit value.
-constexpr inline uint32_t Hi_32(uint64_t Value) {
- return static_cast(Value >> 32);
-}
-
-/// Return the low 32 bits of a 64 bit value.
-constexpr inline uint32_t Lo_32(uint64_t Value) {
- return static_cast(Value);
-}
-
-/// Implementation of Knuth's Algorithm D (Division of nonnegative integers)
-/// from "Art of Computer Programming, Volume 2", section 4.3.1, p. 272. The
-/// variables here have the same names as in the algorithm. Comments explain
-/// the algorithm and any deviation from it.
-static void KnuthDiv(uint32_t *u, uint32_t *v, uint32_t *q, uint32_t* r,
- unsigned m, unsigned n)
-{
- assert(u && "Must provide dividend");
- assert(v && "Must provide divisor");
- assert(q && "Must provide quotient");
- assert(u != v && u != q && v != q && "Must use different memory");
- assert(n>1 && "n must be > 1");
-
- // b denotes the base of the number system. In our case b is 2^32.
- const uint64_t b = uint64_t(1) << 32;
-
- // D1. [Normalize.] Set d = b / (v[n-1] + 1) and multiply all the digits of
- // u and v by d. Note that we have taken Knuth's advice here to use a power
- // of 2 value for d such that d * v[n-1] >= b/2 (b is the base). A power of
- // 2 allows us to shift instead of multiply and it is easy to determine the
- // shift amount from the leading zeros. We are basically normalizing the u
- // and v so that its high bits are shifted to the top of v's range without
- // overflow. Note that this can require an extra word in u so that u must
- // be of length m+n+1.
- unsigned shift = countLeadingZeros(v[n-1]);
- uint32_t v_carry = 0;
- uint32_t u_carry = 0;
- if (shift) {
- for (unsigned i = 0; i < m+n; ++i) {
- uint32_t u_tmp = u[i] >> (32 - shift);
- u[i] = (u[i] << shift) | u_carry;
- u_carry = u_tmp;
- }
- for (unsigned i = 0; i < n; ++i) {
- uint32_t v_tmp = v[i] >> (32 - shift);
- v[i] = (v[i] << shift) | v_carry;
- v_carry = v_tmp;
- }
- }
- u[m+n] = u_carry;
-
- // D2. [Initialize j.] Set j to m. This is the loop counter over the places.
- int j = m;
- do {
- // D3. [Calculate q'.].
- // Set qp = (u[j+n]*b + u[j+n-1]) / v[n-1]. (qp=qprime=q')
- // Set rp = (u[j+n]*b + u[j+n-1]) % v[n-1]. (rp=rprime=r')
- // Now test if qp == b or qp*v[n-2] > b*rp + u[j+n-2]; if so, decrease
- // qp by 1, increase rp by v[n-1], and repeat this test if rp < b. The test
- // on v[n-2] determines at high speed most of the cases in which the trial
- // value qp is one too large, and it eliminates all cases where qp is two
- // too large.
- uint64_t dividend = Make_64(u[j+n], u[j+n-1]);
- uint64_t qp = dividend / v[n-1];
- uint64_t rp = dividend % v[n-1];
- if (qp == b || qp*v[n-2] > b*rp + u[j+n-2]) {
- qp--;
- rp += v[n-1];
- if (rp < b && (qp == b || qp*v[n-2] > b*rp + u[j+n-2]))
- qp--;
- }
-
- // D4. [Multiply and subtract.] Replace (u[j+n]u[j+n-1]...u[j]) with
- // (u[j+n]u[j+n-1]..u[j]) - qp * (v[n-1]...v[1]v[0]). This computation
- // consists of a simple multiplication by a one-place number, combined with
- // a subtraction.
- // The digits (u[j+n]...u[j]) should be kept positive; if the result of
- // this step is actually negative, (u[j+n]...u[j]) should be left as the
- // true value plus b**(n+1), namely as the b's complement of
- // the true value, and a "borrow" to the left should be remembered.
- int64_t borrow = 0;
- for (unsigned i = 0; i < n; ++i) {
- uint64_t p = uint64_t(qp) * uint64_t(v[i]);
- int64_t subres = int64_t(u[j+i]) - borrow - Lo_32(p);
- u[j+i] = Lo_32(subres);
- borrow = Hi_32(p) - Hi_32(subres);
- }
- bool isNeg = u[j+n] < borrow;
- u[j+n] -= Lo_32(borrow);
-
- // D5. [Test remainder.] Set q[j] = qp. If the result of step D4 was
- // negative, go to step D6; otherwise go on to step D7.
- q[j] = Lo_32(qp);
- if (isNeg) {
- // D6. [Add back]. The probability that this step is necessary is very
- // small, on the order of only 2/b. Make sure that test data accounts for
- // this possibility. Decrease q[j] by 1
- q[j]--;
- // and add (0v[n-1]...v[1]v[0]) to (u[j+n]u[j+n-1]...u[j+1]u[j]).
- // A carry will occur to the left of u[j+n], and it should be ignored
- // since it cancels with the borrow that occurred in D4.
- bool carry = false;
- for (unsigned i = 0; i < n; i++) {
- uint32_t limit = std::min(u[j+i],v[i]);
- u[j+i] += v[i] + carry;
- carry = u[j+i] < limit || (carry && u[j+i] == limit);
- }
- u[j+n] += carry;
- }
-
- // D7. [Loop on j.] Decrease j by one. Now if j >= 0, go back to D3.
- } while (--j >= 0);
-
- // D8. [Unnormalize]. Now q[...] is the desired quotient, and the desired
- // remainder may be obtained by dividing u[...] by d. If r is non-null we
- // compute the remainder (urem uses this).
- if (r) {
- // The value d is expressed by the "shift" value above since we avoided
- // multiplication by d by using a shift left. So, all we have to do is
- // shift right here.
- if (shift) {
- uint32_t carry = 0;
- for (int i = n-1; i >= 0; i--) {
- r[i] = (u[i] >> shift) | carry;
- carry = u[i] << (32 - shift);
- }
- } else {
- for (int i = n-1; i >= 0; i--) {
- r[i] = u[i];
- }
- }
- }
-}
-
-// Implementation ported from LLVM/lib/Support/APInt.cpp
-static void bigint_unsigned_division(const BigInt *op1, const BigInt *op2, BigInt *Quotient, BigInt *Remainder) {
- Cmp cmp = bigint_cmp(op1, op2);
- if (cmp == CmpLT) {
- if (Quotient != nullptr) {
- bigint_init_unsigned(Quotient, 0);
- }
- if (Remainder != nullptr) {
- bigint_init_bigint(Remainder, op1);
- }
- return;
- }
- if (cmp == CmpEQ) {
- if (Quotient != nullptr) {
- bigint_init_unsigned(Quotient, 1);
- }
- if (Remainder != nullptr) {
- bigint_init_unsigned(Remainder, 0);
- }
- return;
- }
-
- const uint64_t *LHS = bigint_ptr(op1);
- const uint64_t *RHS = bigint_ptr(op2);
- unsigned lhsWords = op1->digit_count;
- unsigned rhsWords = op2->digit_count;
-
- // First, compose the values into an array of 32-bit words instead of
- // 64-bit words. This is a necessity of both the "short division" algorithm
- // and the Knuth "classical algorithm" which requires there to be native
- // operations for +, -, and * on an m bit value with an m*2 bit result. We
- // can't use 64-bit operands here because we don't have native results of
- // 128-bits. Furthermore, casting the 64-bit values to 32-bit values won't
- // work on large-endian machines.
- unsigned n = rhsWords * 2;
- unsigned m = (lhsWords * 2) - n;
-
- // Allocate space for the temporary values we need either on the stack, if
- // it will fit, or on the heap if it won't.
- uint32_t SPACE[128];
- uint32_t *U = nullptr;
- uint32_t *V = nullptr;
- uint32_t *Q = nullptr;
- uint32_t *R = nullptr;
- if ((Remainder?4:3)*n+2*m+1 <= 128) {
- U = &SPACE[0];
- V = &SPACE[m+n+1];
- Q = &SPACE[(m+n+1) + n];
- if (Remainder)
- R = &SPACE[(m+n+1) + n + (m+n)];
- } else {
- U = new uint32_t[m + n + 1];
- V = new uint32_t[n];
- Q = new uint32_t[m+n];
- if (Remainder)
- R = new uint32_t[n];
- }
-
- // Initialize the dividend
- memset(U, 0, (m+n+1)*sizeof(uint32_t));
- for (unsigned i = 0; i < lhsWords; ++i) {
- uint64_t tmp = LHS[i];
- U[i * 2] = Lo_32(tmp);
- U[i * 2 + 1] = Hi_32(tmp);
- }
- U[m+n] = 0; // this extra word is for "spill" in the Knuth algorithm.
-
- // Initialize the divisor
- memset(V, 0, (n)*sizeof(uint32_t));
- for (unsigned i = 0; i < rhsWords; ++i) {
- uint64_t tmp = RHS[i];
- V[i * 2] = Lo_32(tmp);
- V[i * 2 + 1] = Hi_32(tmp);
- }
-
- // initialize the quotient and remainder
- memset(Q, 0, (m+n) * sizeof(uint32_t));
- if (Remainder)
- memset(R, 0, n * sizeof(uint32_t));
-
- // Now, adjust m and n for the Knuth division. n is the number of words in
- // the divisor. m is the number of words by which the dividend exceeds the
- // divisor (i.e. m+n is the length of the dividend). These sizes must not
- // contain any zero words or the Knuth algorithm fails.
- for (unsigned i = n; i > 0 && V[i-1] == 0; i--) {
- n--;
- m++;
- }
- for (unsigned i = m+n; i > 0 && U[i-1] == 0; i--)
- m--;
-
- // If we're left with only a single word for the divisor, Knuth doesn't work
- // so we implement the short division algorithm here. This is much simpler
- // and faster because we are certain that we can divide a 64-bit quantity
- // by a 32-bit quantity at hardware speed and short division is simply a
- // series of such operations. This is just like doing short division but we
- // are using base 2^32 instead of base 10.
- assert(n != 0 && "Divide by zero?");
- if (n == 1) {
- uint32_t divisor = V[0];
- uint32_t remainder = 0;
- for (int i = m; i >= 0; i--) {
- uint64_t partial_dividend = Make_64(remainder, U[i]);
- if (partial_dividend == 0) {
- Q[i] = 0;
- remainder = 0;
- } else if (partial_dividend < divisor) {
- Q[i] = 0;
- remainder = Lo_32(partial_dividend);
- } else if (partial_dividend == divisor) {
- Q[i] = 1;
- remainder = 0;
- } else {
- Q[i] = Lo_32(partial_dividend / divisor);
- remainder = Lo_32(partial_dividend - (Q[i] * divisor));
- }
- }
- if (R)
- R[0] = remainder;
- } else {
- // Now we're ready to invoke the Knuth classical divide algorithm. In this
- // case n > 1.
- KnuthDiv(U, V, Q, R, m, n);
- }
-
- // If the caller wants the quotient
- if (Quotient) {
- Quotient->is_negative = false;
- Quotient->digit_count = lhsWords;
- if (lhsWords == 1) {
- Quotient->data.digit = Make_64(Q[1], Q[0]);
- } else {
- Quotient->data.digits = heap::c_allocator.allocate(lhsWords);
- for (size_t i = 0; i < lhsWords; i += 1) {
- Quotient->data.digits[i] = Make_64(Q[i*2+1], Q[i*2]);
- }
- }
- }
-
- // If the caller wants the remainder
- if (Remainder) {
- Remainder->is_negative = false;
- Remainder->digit_count = rhsWords;
- if (rhsWords == 1) {
- Remainder->data.digit = Make_64(R[1], R[0]);
- } else {
- Remainder->data.digits = heap::c_allocator.allocate(rhsWords);
- for (size_t i = 0; i < rhsWords; i += 1) {
- Remainder->data.digits[i] = Make_64(R[i*2+1], R[i*2]);
- }
- }
- }
-}
-
-void bigint_div_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2) {
- assert(op2->digit_count != 0); // division by zero
- if (op1->digit_count == 0) {
- bigint_init_unsigned(dest, 0);
- return;
- }
- const uint64_t *op1_digits = bigint_ptr(op1);
- const uint64_t *op2_digits = bigint_ptr(op2);
- if (op1->digit_count == 1 && op2->digit_count == 1) {
- dest->data.digit = op1_digits[0] / op2_digits[0];
- dest->digit_count = 1;
- dest->is_negative = op1->is_negative != op2->is_negative;
- bigint_normalize(dest);
- return;
- }
- if (op2->digit_count == 1 && op2_digits[0] == 1) {
- // X / 1 == X
- bigint_init_bigint(dest, op1);
- dest->is_negative = op1->is_negative != op2->is_negative;
- bigint_normalize(dest);
- return;
- }
-
- const BigInt *op1_positive;
- BigInt op1_positive_data;
- if (op1->is_negative) {
- bigint_negate(&op1_positive_data, op1);
- op1_positive = &op1_positive_data;
- } else {
- op1_positive = op1;
- }
-
- const BigInt *op2_positive;
- BigInt op2_positive_data;
- if (op2->is_negative) {
- bigint_negate(&op2_positive_data, op2);
- op2_positive = &op2_positive_data;
- } else {
- op2_positive = op2;
- }
-
- bigint_unsigned_division(op1_positive, op2_positive, dest, nullptr);
- dest->is_negative = op1->is_negative != op2->is_negative;
- bigint_normalize(dest);
-}
-
-void bigint_div_floor(BigInt *dest, const BigInt *op1, const BigInt *op2) {
- if (op1->is_negative != op2->is_negative) {
- bigint_div_trunc(dest, op1, op2);
- BigInt mult_again = {0};
- bigint_mul(&mult_again, dest, op2);
- mult_again.is_negative = op1->is_negative;
- if (bigint_cmp(&mult_again, op1) != CmpEQ) {
- BigInt tmp = {0};
- bigint_init_bigint(&tmp, dest);
- BigInt neg_one = {0};
- bigint_init_signed(&neg_one, -1);
- bigint_add(dest, &tmp, &neg_one);
- }
- bigint_normalize(dest);
- } else {
- bigint_div_trunc(dest, op1, op2);
- }
-}
-
-void bigint_rem(BigInt *dest, const BigInt *op1, const BigInt *op2) {
- assert(op2->digit_count != 0); // division by zero
- if (op1->digit_count == 0) {
- bigint_init_unsigned(dest, 0);
- return;
- }
- const uint64_t *op1_digits = bigint_ptr(op1);
- const uint64_t *op2_digits = bigint_ptr(op2);
-
- if (op1->digit_count == 1 && op2->digit_count == 1) {
- dest->data.digit = op1_digits[0] % op2_digits[0];
- dest->digit_count = 1;
- dest->is_negative = op1->is_negative;
- bigint_normalize(dest);
- return;
- }
- if (op2->digit_count == 2 && op2_digits[0] == 0 && op2_digits[1] == 1) {
- // special case this divisor
- bigint_init_unsigned(dest, op1_digits[0]);
- dest->is_negative = op1->is_negative;
- bigint_normalize(dest);
- return;
- }
-
- if (op2->digit_count == 1 && op2_digits[0] == 1) {
- // X % 1 == 0
- bigint_init_unsigned(dest, 0);
- return;
- }
-
- const BigInt *op1_positive;
- BigInt op1_positive_data;
- if (op1->is_negative) {
- bigint_negate(&op1_positive_data, op1);
- op1_positive = &op1_positive_data;
- } else {
- op1_positive = op1;
- }
-
- const BigInt *op2_positive;
- BigInt op2_positive_data;
- if (op2->is_negative) {
- bigint_negate(&op2_positive_data, op2);
- op2_positive = &op2_positive_data;
- } else {
- op2_positive = op2;
- }
-
- bigint_unsigned_division(op1_positive, op2_positive, nullptr, dest);
- dest->is_negative = op1->is_negative;
- bigint_normalize(dest);
-}
-
-void bigint_mod(BigInt *dest, const BigInt *op1, const BigInt *op2) {
- if (op1->is_negative) {
- BigInt first_rem;
- bigint_rem(&first_rem, op1, op2);
- first_rem.is_negative = !op2->is_negative;
- BigInt op2_minus_rem;
- bigint_add(&op2_minus_rem, op2, &first_rem);
- bigint_rem(dest, &op2_minus_rem, op2);
- dest->is_negative = false;
- } else {
- bigint_rem(dest, op1, op2);
- dest->is_negative = false;
- }
-}
-
-void bigint_or(BigInt *dest, const BigInt *op1, const BigInt *op2) {
- if (op1->digit_count == 0) {
- return bigint_init_bigint(dest, op2);
- }
- if (op2->digit_count == 0) {
- return bigint_init_bigint(dest, op1);
- }
- if (op1->is_negative || op2->is_negative) {
- size_t big_bit_count = max(bigint_bits_needed(op1), bigint_bits_needed(op2));
-
- BigInt twos_comp_op1 = {0};
- to_twos_complement(&twos_comp_op1, op1, big_bit_count);
-
- BigInt twos_comp_op2 = {0};
- to_twos_complement(&twos_comp_op2, op2, big_bit_count);
-
- BigInt twos_comp_dest = {0};
- bigint_or(&twos_comp_dest, &twos_comp_op1, &twos_comp_op2);
-
- from_twos_complement(dest, &twos_comp_dest, big_bit_count, true);
- } else {
- dest->is_negative = false;
- const uint64_t *op1_digits = bigint_ptr(op1);
- const uint64_t *op2_digits = bigint_ptr(op2);
- if (op1->digit_count == 1 && op2->digit_count == 1) {
- dest->digit_count = 1;
- dest->data.digit = op1_digits[0] | op2_digits[0];
- bigint_normalize(dest);
- return;
- }
- dest->digit_count = max(op1->digit_count, op2->digit_count);
- dest->data.digits = heap::c_allocator.allocate_nonzero(dest->digit_count);
- for (size_t i = 0; i < dest->digit_count; i += 1) {
- uint64_t digit = 0;
- if (i < op1->digit_count) {
- digit |= op1_digits[i];
- }
- if (i < op2->digit_count) {
- digit |= op2_digits[i];
- }
- dest->data.digits[i] = digit;
- }
- bigint_normalize(dest);
- }
-}
-
-void bigint_and(BigInt *dest, const BigInt *op1, const BigInt *op2) {
- if (op1->digit_count == 0 || op2->digit_count == 0) {
- return bigint_init_unsigned(dest, 0);
- }
- if (op1->is_negative || op2->is_negative) {
- size_t big_bit_count = max(bigint_bits_needed(op1), bigint_bits_needed(op2));
-
- BigInt twos_comp_op1 = {0};
- to_twos_complement(&twos_comp_op1, op1, big_bit_count);
-
- BigInt twos_comp_op2 = {0};
- to_twos_complement(&twos_comp_op2, op2, big_bit_count);
-
- BigInt twos_comp_dest = {0};
- bigint_and(&twos_comp_dest, &twos_comp_op1, &twos_comp_op2);
-
- from_twos_complement(dest, &twos_comp_dest, big_bit_count, true);
- } else {
- dest->is_negative = false;
- const uint64_t *op1_digits = bigint_ptr(op1);
- const uint64_t *op2_digits = bigint_ptr(op2);
- if (op1->digit_count == 1 && op2->digit_count == 1) {
- dest->digit_count = 1;
- dest->data.digit = op1_digits[0] & op2_digits[0];
- bigint_normalize(dest);
- return;
- }
-
- dest->digit_count = max(op1->digit_count, op2->digit_count);
- dest->data.digits = heap::c_allocator.allocate_nonzero(dest->digit_count);
-
- size_t i = 0;
- for (; i < op1->digit_count && i < op2->digit_count; i += 1) {
- dest->data.digits[i] = op1_digits[i] & op2_digits[i];
- }
- for (; i < dest->digit_count; i += 1) {
- dest->data.digits[i] = 0;
- }
- bigint_normalize(dest);
- }
-}
-
-void bigint_xor(BigInt *dest, const BigInt *op1, const BigInt *op2) {
- if (op1->digit_count == 0) {
- return bigint_init_bigint(dest, op2);
- }
- if (op2->digit_count == 0) {
- return bigint_init_bigint(dest, op1);
- }
- if (op1->is_negative || op2->is_negative) {
- size_t big_bit_count = max(bigint_bits_needed(op1), bigint_bits_needed(op2));
-
- BigInt twos_comp_op1 = {0};
- to_twos_complement(&twos_comp_op1, op1, big_bit_count);
-
- BigInt twos_comp_op2 = {0};
- to_twos_complement(&twos_comp_op2, op2, big_bit_count);
-
- BigInt twos_comp_dest = {0};
- bigint_xor(&twos_comp_dest, &twos_comp_op1, &twos_comp_op2);
-
- from_twos_complement(dest, &twos_comp_dest, big_bit_count, true);
- } else {
- dest->is_negative = false;
- const uint64_t *op1_digits = bigint_ptr(op1);
- const uint64_t *op2_digits = bigint_ptr(op2);
-
- assert(op1->digit_count > 0 && op2->digit_count > 0);
- if (op1->digit_count == 1 && op2->digit_count == 1) {
- dest->digit_count = 1;
- dest->data.digit = op1_digits[0] ^ op2_digits[0];
- bigint_normalize(dest);
- return;
- }
- dest->digit_count = max(op1->digit_count, op2->digit_count);
- dest->data.digits = heap::c_allocator.allocate_nonzero(dest->digit_count);
- size_t i = 0;
- for (; i < op1->digit_count && i < op2->digit_count; i += 1) {
- dest->data.digits[i] = op1_digits[i] ^ op2_digits[i];
- }
- for (; i < dest->digit_count; i += 1) {
- if (i < op1->digit_count) {
- dest->data.digits[i] = op1_digits[i];
- } else if (i < op2->digit_count) {
- dest->data.digits[i] = op2_digits[i];
- } else {
- zig_unreachable();
- }
- }
- bigint_normalize(dest);
- }
-}
-
-void bigint_shl(BigInt *dest, const BigInt *op1, const BigInt *op2) {
- assert(!op2->is_negative);
-
- if (op2->digit_count == 0) {
- bigint_init_bigint(dest, op1);
- return;
- }
-
- if (op1->digit_count == 0) {
- bigint_init_unsigned(dest, 0);
- return;
- }
-
- if (op2->digit_count != 1) {
- zig_panic("TODO shift left by amount greater than 64 bit integer");
- }
-
- const uint64_t *op1_digits = bigint_ptr(op1);
- uint64_t shift_amt = bigint_as_unsigned(op2);
-
- if (op1->digit_count == 1 && shift_amt < 64) {
- dest->data.digit = op1_digits[0] << shift_amt;
- if (dest->data.digit > op1_digits[0]) {
- dest->digit_count = 1;
- dest->is_negative = op1->is_negative;
- return;
- }
- }
-
- uint64_t digit_shift_count = shift_amt / 64;
- uint64_t leftover_shift_count = shift_amt % 64;
-
- dest->data.digits = heap::c_allocator.allocate(op1->digit_count + digit_shift_count + 1);
- dest->digit_count = digit_shift_count;
- uint64_t carry = 0;
- for (size_t i = 0; i < op1->digit_count; i += 1) {
- uint64_t digit = op1_digits[i];
- dest->data.digits[dest->digit_count] = carry | (digit << leftover_shift_count);
- dest->digit_count += 1;
- if (leftover_shift_count > 0) {
- carry = digit >> (64 - leftover_shift_count);
- } else {
- carry = 0;
- }
- }
- dest->data.digits[dest->digit_count] = carry;
- dest->digit_count += 1;
- dest->is_negative = op1->is_negative;
- bigint_normalize(dest);
-}
-
-void bigint_shl_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) {
- BigInt unwrapped = {0};
- bigint_shl(&unwrapped, op1, op2);
- bigint_truncate(dest, &unwrapped, bit_count, is_signed);
-}
-
-void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2) {
- assert(!op2->is_negative);
-
- if (op1->digit_count == 0) {
- return bigint_init_unsigned(dest, 0);
- }
-
- if (op2->digit_count == 0) {
- return bigint_init_bigint(dest, op1);
- }
-
- if (op2->digit_count != 1) {
- zig_panic("TODO shift right by amount greater than 64 bit integer");
- }
-
- const uint64_t *op1_digits = bigint_ptr(op1);
- uint64_t shift_amt = bigint_as_unsigned(op2);
-
- if (op1->digit_count == 1) {
- dest->data.digit = (shift_amt < 64) ? op1_digits[0] >> shift_amt : 0;
- dest->digit_count = 1;
- dest->is_negative = op1->is_negative;
- bigint_normalize(dest);
- return;
- }
-
- size_t digit_shift_count = shift_amt / 64;
- size_t leftover_shift_count = shift_amt % 64;
-
- if (digit_shift_count >= op1->digit_count) {
- return bigint_init_unsigned(dest, 0);
- }
-
- dest->digit_count = op1->digit_count - digit_shift_count;
- uint64_t *digits;
- if (dest->digit_count == 1) {
- digits = &dest->data.digit;
- } else {
- digits = heap::c_allocator.allocate(dest->digit_count);
- dest->data.digits = digits;
- }
-
- uint64_t carry = 0;
- for (size_t op_digit_index = op1->digit_count - 1;;) {
- uint64_t digit = op1_digits[op_digit_index];
- size_t dest_digit_index = op_digit_index - digit_shift_count;
- digits[dest_digit_index] = carry | (digit >> leftover_shift_count);
- carry = (leftover_shift_count != 0) ? (digit << (64 - leftover_shift_count)) : 0;
-
- if (dest_digit_index == 0) { break; }
- op_digit_index -= 1;
- }
- dest->is_negative = op1->is_negative;
- bigint_normalize(dest);
-}
-
-void bigint_negate(BigInt *dest, const BigInt *op) {
- bigint_init_bigint(dest, op);
- dest->is_negative = !dest->is_negative;
- bigint_normalize(dest);
-}
-
-void bigint_negate_wrap(BigInt *dest, const BigInt *op, size_t bit_count) {
- BigInt zero;
- bigint_init_unsigned(&zero, 0);
- bigint_sub_wrap(dest, &zero, op, bit_count, true);
-}
-
-void bigint_not(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed) {
- if (bit_count == 0) {
- bigint_init_unsigned(dest, 0);
- return;
- }
-
- if (is_signed) {
- BigInt twos_comp = {0};
- to_twos_complement(&twos_comp, op, bit_count);
-
- BigInt inverted = {0};
- bigint_not(&inverted, &twos_comp, bit_count, false);
-
- from_twos_complement(dest, &inverted, bit_count, true);
- return;
- }
-
- assert(!op->is_negative);
-
- dest->is_negative = false;
- const uint64_t *op_digits = bigint_ptr(op);
- if (bit_count <= 64) {
- dest->digit_count = 1;
- if (op->digit_count == 0) {
- if (bit_count == 64) {
- dest->data.digit = UINT64_MAX;
- } else {
- dest->data.digit = (1ULL << bit_count) - 1;
- }
- } else if (op->digit_count == 1) {
- dest->data.digit = ~op_digits[0];
- if (bit_count != 64) {
- uint64_t mask = (1ULL << bit_count) - 1;
- dest->data.digit &= mask;
- }
- }
- bigint_normalize(dest);
- return;
- }
- dest->digit_count = (bit_count + 63) / 64;
- assert(dest->digit_count >= op->digit_count);
- dest->data.digits = heap::c_allocator.allocate_nonzero(dest->digit_count);
- size_t i = 0;
- for (; i < op->digit_count; i += 1) {
- dest->data.digits[i] = ~op_digits[i];
- }
- for (; i < dest->digit_count; i += 1) {
- dest->data.digits[i] = 0xffffffffffffffffULL;
- }
- size_t digit_index = dest->digit_count - 1;
- size_t digit_bit_index = bit_count % 64;
- if (digit_bit_index != 0) {
- uint64_t mask = (1ULL << digit_bit_index) - 1;
- dest->data.digits[digit_index] &= mask;
- }
- bigint_normalize(dest);
-}
-
-void bigint_truncate(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed) {
- BigInt twos_comp;
- to_twos_complement(&twos_comp, op, bit_count);
- from_twos_complement(dest, &twos_comp, bit_count, is_signed);
-}
-
-Cmp bigint_cmp(const BigInt *op1, const BigInt *op2) {
- if (op1->is_negative && !op2->is_negative) {
- return CmpLT;
- } else if (!op1->is_negative && op2->is_negative) {
- return CmpGT;
- } else if (op1->digit_count > op2->digit_count) {
- return op1->is_negative ? CmpLT : CmpGT;
- } else if (op2->digit_count > op1->digit_count) {
- return op1->is_negative ? CmpGT : CmpLT;
- } else if (op1->digit_count == 0) {
- return CmpEQ;
- }
- const uint64_t *op1_digits = bigint_ptr(op1);
- const uint64_t *op2_digits = bigint_ptr(op2);
- for (size_t i = op1->digit_count - 1; ;) {
- uint64_t op1_digit = op1_digits[i];
- uint64_t op2_digit = op2_digits[i];
-
- if (op1_digit > op2_digit) {
- return op1->is_negative ? CmpLT : CmpGT;
- }
- if (op1_digit < op2_digit) {
- return op1->is_negative ? CmpGT : CmpLT;
- }
-
- if (i == 0) {
- return CmpEQ;
- }
- i -= 1;
- }
-}
-
-void bigint_append_buf(Buf *buf, const BigInt *op, uint64_t base) {
- if (op->digit_count == 0) {
- buf_append_char(buf, '0');
- return;
- }
- if (op->is_negative) {
- buf_append_char(buf, '-');
- }
- if (op->digit_count == 1 && base == 10) {
- buf_appendf(buf, "%" ZIG_PRI_u64, op->data.digit);
- return;
- }
- if (op->digit_count == 1 && base == 16) {
- buf_appendf(buf, "%" ZIG_PRI_x64, op->data.digit);
- return;
- }
- size_t first_digit_index = buf_len(buf);
-
- BigInt digit_bi = {0};
- BigInt a1 = {0};
- BigInt a2 = {0};
-
- BigInt *a = &a1;
- BigInt *other_a = &a2;
- bigint_init_bigint(a, op);
-
- BigInt base_bi = {0};
- bigint_init_unsigned(&base_bi, base);
-
- for (;;) {
- bigint_rem(&digit_bi, a, &base_bi);
- uint8_t digit = bigint_as_unsigned(&digit_bi);
- buf_append_char(buf, digit_to_char(digit, false));
- bigint_div_trunc(other_a, a, &base_bi);
- {
- BigInt *tmp = a;
- a = other_a;
- other_a = tmp;
- }
- if (bigint_cmp_zero(a) == CmpEQ) {
- break;
- }
- }
-
- // reverse
- for (size_t i = first_digit_index; i < buf_len(buf) / 2; i += 1) {
- size_t other_i = buf_len(buf) + first_digit_index - i - 1;
- uint8_t tmp = buf_ptr(buf)[i];
- buf_ptr(buf)[i] = buf_ptr(buf)[other_i];
- buf_ptr(buf)[other_i] = tmp;
- }
-}
-
-size_t bigint_popcount_unsigned(const BigInt *bi) {
- assert(!bi->is_negative);
- if (bi->digit_count == 0)
- return 0;
-
- size_t count = 0;
- size_t bit_count = bi->digit_count * 64;
- for (size_t i = 0; i < bit_count; i += 1) {
- if (bit_at_index(bi, i))
- count += 1;
- }
- return count;
-}
-
-size_t bigint_popcount_signed(const BigInt *bi, size_t bit_count) {
- if (bit_count == 0)
- return 0;
- if (bi->digit_count == 0)
- return 0;
-
- BigInt twos_comp = {0};
- to_twos_complement(&twos_comp, bi, bit_count);
-
- size_t count = 0;
- for (size_t i = 0; i < bit_count; i += 1) {
- if (bit_at_index(&twos_comp, i))
- count += 1;
- }
- return count;
-}
-
-size_t bigint_ctz(const BigInt *bi, size_t bit_count) {
- if (bit_count == 0)
- return 0;
- if (bi->digit_count == 0)
- return bit_count;
-
- BigInt twos_comp = {0};
- to_twos_complement(&twos_comp, bi, bit_count);
-
- size_t count = 0;
- for (size_t i = 0; i < bit_count; i += 1) {
- if (bit_at_index(&twos_comp, i))
- return count;
- count += 1;
- }
- return count;
-}
-
-size_t bigint_clz(const BigInt *bi, size_t bit_count) {
- if (bi->is_negative || bit_count == 0)
- return 0;
- if (bi->digit_count == 0)
- return bit_count;
-
- size_t count = 0;
- for (size_t i = bit_count - 1;;) {
- if (bit_at_index(bi, i))
- return count;
- count += 1;
-
- if (i == 0) break;
- i -= 1;
- }
- return count;
-}
-
-static uint64_t bigint_as_unsigned(const BigInt *bigint) {
- assert(!bigint->is_negative);
- if (bigint->digit_count == 0) {
- return 0;
- } else if (bigint->digit_count == 1) {
- return bigint->data.digit;
- } else {
- zig_unreachable();
- }
-}
-
-uint64_t bigint_as_u64(const BigInt *bigint)
-{
- return bigint_as_unsigned(bigint);
-}
-
-uint32_t bigint_as_u32(const BigInt *bigint) {
- uint64_t value64 = bigint_as_unsigned(bigint);
- uint32_t value32 = (uint32_t)value64;
- assert (value64 == value32);
- return value32;
-}
-
-size_t bigint_as_usize(const BigInt *bigint) {
- uint64_t value64 = bigint_as_unsigned(bigint);
- size_t valueUsize = (size_t)value64;
- assert (value64 == valueUsize);
- return valueUsize;
-}
-
-int64_t bigint_as_signed(const BigInt *bigint) {
- if (bigint->digit_count == 0) {
- return 0;
- } else if (bigint->digit_count == 1) {
- if (bigint->is_negative) {
- if (bigint->data.digit <= 9223372036854775808ULL) {
- return (-((int64_t)(bigint->data.digit - 1))) - 1;
- } else {
- zig_unreachable();
- }
- } else {
- return bigint->data.digit;
- }
- } else {
- zig_unreachable();
- }
-}
-
-Cmp bigint_cmp_zero(const BigInt *op) {
- if (op->digit_count == 0) {
- return CmpEQ;
- }
- return op->is_negative ? CmpLT : CmpGT;
-}
-
-uint32_t bigint_hash(BigInt x) {
- if (x.digit_count == 0) {
- return 0;
- } else {
- return bigint_ptr(&x)[0];
- }
-}
-
-bool bigint_eql(BigInt a, BigInt b) {
- return bigint_cmp(&a, &b) == CmpEQ;
-}
-
-void bigint_incr(BigInt *x) {
- if (x->digit_count == 0) {
- bigint_init_unsigned(x, 1);
- return;
- }
-
- if (x->digit_count == 1) {
- if (x->is_negative && x->data.digit != 0) {
- x->data.digit -= 1;
- return;
- } else if (!x->is_negative && x->data.digit != UINT64_MAX) {
- x->data.digit += 1;
- return;
- }
- }
-
- BigInt copy;
- bigint_init_bigint(©, x);
-
- BigInt one;
- bigint_init_unsigned(&one, 1);
-
- bigint_add(x, ©, &one);
-}
-
-void bigint_decr(BigInt *x) {
- if (x->digit_count == 0) {
- bigint_init_signed(x, -1);
- return;
- }
-
- if (x->digit_count == 1) {
- if (x->is_negative && x->data.digit != UINT64_MAX) {
- x->data.digit += 1;
- return;
- } else if (!x->is_negative && x->data.digit != 0) {
- x->data.digit -= 1;
- return;
- }
- }
-
- BigInt copy;
- bigint_init_bigint(©, x);
-
- BigInt neg_one;
- bigint_init_signed(&neg_one, -1);
-
- bigint_add(x, ©, &neg_one);
-}
diff --git a/src/bigint.hpp b/src/bigint.hpp
deleted file mode 100644
index 044ea6642370e69e92ac3e291ed81c14d12dc360..0000000000000000000000000000000000000000
--- a/src/bigint.hpp
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (c) 2017 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#ifndef ZIG_BIGINT_HPP
-#define ZIG_BIGINT_HPP
-
-#include
-#include
-
-struct BigInt {
- size_t digit_count;
- union {
- uint64_t digit;
- uint64_t *digits; // Least significant digit first
- } data;
- bool is_negative;
-};
-
-struct Buf;
-struct BigFloat;
-
-enum Cmp {
- CmpLT,
- CmpGT,
- CmpEQ,
-};
-
-void bigint_init_unsigned(BigInt *dest, uint64_t x);
-void bigint_init_signed(BigInt *dest, int64_t x);
-void bigint_init_bigint(BigInt *dest, const BigInt *src);
-void bigint_init_bigfloat(BigInt *dest, const BigFloat *op);
-void bigint_init_data(BigInt *dest, const uint64_t *digits, size_t digit_count, bool is_negative);
-void bigint_deinit(BigInt *bi);
-
-// panics if number won't fit
-uint64_t bigint_as_u64(const BigInt *bigint);
-uint32_t bigint_as_u32(const BigInt *bigint);
-size_t bigint_as_usize(const BigInt *bigint);
-
-int64_t bigint_as_signed(const BigInt *bigint);
-
-static inline const uint64_t *bigint_ptr(const BigInt *bigint) {
- if (bigint->digit_count == 1) {
- return &bigint->data.digit;
- } else {
- return bigint->data.digits;
- }
-}
-
-bool bigint_fits_in_bits(const BigInt *bn, size_t bit_count, bool is_signed);
-void bigint_write_twos_complement(const BigInt *big_int, uint8_t *buf, size_t bit_count, bool is_big_endian);
-void bigint_read_twos_complement(BigInt *dest, const uint8_t *buf, size_t bit_count, bool is_big_endian,
- bool is_signed);
-void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2);
-void bigint_add_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed);
-void bigint_sub(BigInt *dest, const BigInt *op1, const BigInt *op2);
-void bigint_sub_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed);
-void bigint_mul(BigInt *dest, const BigInt *op1, const BigInt *op2);
-void bigint_mul_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed);
-void bigint_div_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2);
-void bigint_div_floor(BigInt *dest, const BigInt *op1, const BigInt *op2);
-void bigint_rem(BigInt *dest, const BigInt *op1, const BigInt *op2);
-void bigint_mod(BigInt *dest, const BigInt *op1, const BigInt *op2);
-
-void bigint_or(BigInt *dest, const BigInt *op1, const BigInt *op2);
-void bigint_and(BigInt *dest, const BigInt *op1, const BigInt *op2);
-void bigint_xor(BigInt *dest, const BigInt *op1, const BigInt *op2);
-
-void bigint_shl(BigInt *dest, const BigInt *op1, const BigInt *op2);
-void bigint_shl_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed);
-void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2);
-
-void bigint_negate(BigInt *dest, const BigInt *op);
-void bigint_negate_wrap(BigInt *dest, const BigInt *op, size_t bit_count);
-void bigint_not(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed);
-void bigint_truncate(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed);
-
-Cmp bigint_cmp(const BigInt *op1, const BigInt *op2);
-
-void bigint_append_buf(Buf *buf, const BigInt *op, uint64_t base);
-
-size_t bigint_ctz(const BigInt *bi, size_t bit_count);
-size_t bigint_clz(const BigInt *bi, size_t bit_count);
-size_t bigint_popcount_signed(const BigInt *bi, size_t bit_count);
-size_t bigint_popcount_unsigned(const BigInt *bi);
-
-size_t bigint_bits_needed(const BigInt *op);
-
-
-// convenience functions
-Cmp bigint_cmp_zero(const BigInt *op);
-
-void bigint_incr(BigInt *value);
-void bigint_decr(BigInt *value);
-
-bool mul_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result);
-
-uint32_t bigint_hash(BigInt x);
-bool bigint_eql(BigInt a, BigInt b);
-
-#endif
diff --git a/src/buffer.cpp b/src/buffer.cpp
deleted file mode 100644
index 86435e0f1496fa19f080bf442cd37ba9e348701c..0000000000000000000000000000000000000000
--- a/src/buffer.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (c) 2016 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#include "buffer.hpp"
-#include
-#include
-#include
-
-Buf *buf_vprintf(const char *format, va_list ap) {
- va_list ap2;
- va_copy(ap2, ap);
-
- int len1 = vsnprintf(nullptr, 0, format, ap);
- assert(len1 >= 0);
-
- size_t required_size = len1 + 1;
-
- Buf *buf = buf_alloc_fixed(len1);
-
- int len2 = vsnprintf(buf_ptr(buf), required_size, format, ap2);
- assert(len2 == len1);
-
- va_end(ap2);
-
- return buf;
-}
-
-Buf *buf_sprintf(const char *format, ...) {
- va_list ap;
- va_start(ap, format);
- Buf *result = buf_vprintf(format, ap);
- va_end(ap);
- return result;
-}
-
-void buf_appendf(Buf *buf, const char *format, ...) {
- assert(buf->list.length);
- va_list ap, ap2;
- va_start(ap, format);
- va_copy(ap2, ap);
-
- int len1 = vsnprintf(nullptr, 0, format, ap);
- assert(len1 >= 0);
-
- size_t required_size = len1 + 1;
-
- size_t orig_len = buf_len(buf);
-
- buf_resize(buf, orig_len + len1);
-
- int len2 = vsnprintf(buf_ptr(buf) + orig_len, required_size, format, ap2);
- assert(len2 == len1);
-
- va_end(ap2);
- va_end(ap);
-}
-
-// these functions are not static inline so they can be better used as template parameters
-bool buf_eql_buf(Buf *buf, Buf *other) {
- return buf_eql_mem(buf, buf_ptr(other), buf_len(other));
-}
-
-uint32_t buf_hash(Buf *buf) {
- assert(buf->list.length);
- size_t interval = buf->list.length / 256;
- if (interval == 0)
- interval = 1;
- // FNV 32-bit hash
- uint32_t h = 2166136261;
- for (size_t i = 0; i < buf_len(buf); i += interval) {
- h = h ^ ((uint8_t)buf->list.at(i));
- h = h * 16777619;
- }
- return h;
-}
diff --git a/src/buffer.hpp b/src/buffer.hpp
deleted file mode 100644
index 8876316589e76c8a069609d0a375c73acaf4a322..0000000000000000000000000000000000000000
--- a/src/buffer.hpp
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (c) 2015 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#ifndef ZIG_BUFFER_HPP
-#define ZIG_BUFFER_HPP
-
-#include "list.hpp"
-
-#include
-#include
-#include
-
-#define BUF_INIT {{0}}
-
-// Note, you must call one of the alloc, init, or resize functions to have an
-// initialized buffer. The assertions should help with this.
-struct Buf {
- ZigList list;
-};
-
-Buf *buf_sprintf(const char *format, ...)
- ATTRIBUTE_PRINTF(1, 2);
-Buf *buf_vprintf(const char *format, va_list ap);
-
-static inline size_t buf_len(Buf *buf) {
- assert(buf);
- assert(buf->list.length);
- return buf->list.length - 1;
-}
-
-static inline char *buf_ptr(Buf *buf) {
- assert(buf);
- assert(buf->list.length);
- return buf->list.items;
-}
-
-static inline const char *buf_ptr(const Buf *buf) {
- assert(buf);
- assert(buf->list.length);
- return buf->list.items;
-}
-
-static inline void buf_resize(Buf *buf, size_t new_len) {
- buf->list.resize(new_len + 1);
- buf->list.at(buf_len(buf)) = 0;
-}
-
-static inline Buf *buf_alloc_fixed(size_t size) {
- Buf *buf = heap::c_allocator.create();
- buf_resize(buf, size);
- return buf;
-}
-
-static inline Buf *buf_alloc(void) {
- return buf_alloc_fixed(0);
-}
-
-static inline void buf_deinit(Buf *buf) {
- buf->list.deinit();
-}
-
-static inline void buf_destroy(Buf *buf) {
- buf_deinit(buf);
- heap::c_allocator.destroy(buf);
-}
-
-static inline void buf_init_from_mem(Buf *buf, const char *ptr, size_t len) {
- assert(len != SIZE_MAX);
- buf->list.resize(len + 1);
- memcpy(buf_ptr(buf), ptr, len);
- buf->list.at(buf_len(buf)) = 0;
-}
-
-static inline void buf_init_from_str(Buf *buf, const char *str) {
- buf_init_from_mem(buf, str, strlen(str));
-}
-
-static inline void buf_init_from_buf(Buf *buf, Buf *other) {
- buf_init_from_mem(buf, buf_ptr(other), buf_len(other));
-}
-
-static inline Buf *buf_create_from_mem(const char *ptr, size_t len) {
- assert(len != SIZE_MAX);
- Buf *buf = heap::c_allocator.create();
- buf_init_from_mem(buf, ptr, len);
- return buf;
-}
-
-static inline Buf *buf_create_from_slice(Slice slice) {
- return buf_create_from_mem((const char *)slice.ptr, slice.len);
-}
-
-static inline Buf *buf_create_from_str(const char *str) {
- return buf_create_from_mem(str, strlen(str));
-}
-
-static inline Buf *buf_create_from_buf(Buf *buf) {
- return buf_create_from_mem(buf_ptr(buf), buf_len(buf));
-}
-
-static inline Buf *buf_slice(Buf *in_buf, size_t start, size_t end) {
- assert(in_buf->list.length);
- assert(start != SIZE_MAX);
- assert(end != SIZE_MAX);
- assert(start < buf_len(in_buf));
- assert(end <= buf_len(in_buf));
- Buf *out_buf = heap::c_allocator.create();
- out_buf->list.resize(end - start + 1);
- memcpy(buf_ptr(out_buf), buf_ptr(in_buf) + start, end - start);
- out_buf->list.at(buf_len(out_buf)) = 0;
- return out_buf;
-}
-
-static inline void buf_append_mem(Buf *buf, const char *mem, size_t mem_len) {
- assert(buf->list.length);
- assert(mem_len != SIZE_MAX);
- size_t old_len = buf_len(buf);
- buf_resize(buf, old_len + mem_len);
- memcpy(buf_ptr(buf) + old_len, mem, mem_len);
- buf->list.at(buf_len(buf)) = 0;
-}
-
-static inline void buf_append_str(Buf *buf, const char *str) {
- assert(buf->list.length);
- buf_append_mem(buf, str, strlen(str));
-}
-
-static inline void buf_append_buf(Buf *buf, Buf *append_buf) {
- assert(buf->list.length);
- buf_append_mem(buf, buf_ptr(append_buf), buf_len(append_buf));
-}
-
-static inline void buf_append_char(Buf *buf, uint8_t c) {
- assert(buf->list.length);
- buf_append_mem(buf, (const char *)&c, 1);
-}
-
-void buf_appendf(Buf *buf, const char *format, ...)
- ATTRIBUTE_PRINTF(2, 3);
-
-static inline bool buf_eql_mem(Buf *buf, const char *mem, size_t mem_len) {
- assert(buf->list.length);
- return mem_eql_mem(buf_ptr(buf), buf_len(buf), mem, mem_len);
-}
-
-static inline bool buf_eql_mem_ignore_case(Buf *buf, const char *mem, size_t mem_len) {
- assert(buf->list.length);
- return mem_eql_mem_ignore_case(buf_ptr(buf), buf_len(buf), mem, mem_len);
-}
-
-static inline bool buf_eql_str(Buf *buf, const char *str) {
- assert(buf->list.length);
- return buf_eql_mem(buf, str, strlen(str));
-}
-
-static inline bool buf_eql_str_ignore_case(Buf *buf, const char *str) {
- assert(buf->list.length);
- return buf_eql_mem_ignore_case(buf, str, strlen(str));
-}
-
-static inline bool buf_starts_with_mem(Buf *buf, const char *mem, size_t mem_len) {
- if (buf_len(buf) < mem_len) {
- return false;
- }
- return memcmp(buf_ptr(buf), mem, mem_len) == 0;
-}
-
-static inline bool buf_starts_with_buf(Buf *buf, Buf *sub) {
- return buf_starts_with_mem(buf, buf_ptr(sub), buf_len(sub));
-}
-
-static inline bool buf_starts_with_str(Buf *buf, const char *str) {
- return buf_starts_with_mem(buf, str, strlen(str));
-}
-
-static inline bool buf_ends_with_mem(Buf *buf, const char *mem, size_t mem_len) {
- return mem_ends_with_mem(buf_ptr(buf), buf_len(buf), mem, mem_len);
-}
-
-static inline bool buf_ends_with_str(Buf *buf, const char *str) {
- return buf_ends_with_mem(buf, str, strlen(str));
-}
-
-bool buf_eql_buf(Buf *buf, Buf *other);
-uint32_t buf_hash(Buf *buf);
-
-static inline void buf_upcase(Buf *buf) {
- for (size_t i = 0; i < buf_len(buf); i += 1) {
- buf_ptr(buf)[i] = (char)toupper(buf_ptr(buf)[i]);
- }
-}
-
-static inline Slice]