| ... | ... | @@ -10,6 +10,7 @@ const log = std.log.scoped(.module); |
| 10 | 10 | const BigIntConst = std.math.big.int.Const; |
| 11 | 11 | const BigIntMutable = std.math.big.int.Mutable; |
| 12 | 12 | const Target = std.Target; |
| 13 | const target_util = @import("target.zig"); |
| 13 | 14 | const Package = @import("Package.zig"); |
| 14 | 15 | const link = @import("link.zig"); |
| 15 | 16 | const ir = @import("ir.zig"); |
| ... | ... | @@ -26,6 +27,8 @@ const build_options = @import("build_options"); |
| 26 | 27 | |
| 27 | 28 | /// General-purpose allocator. Used for both temporary and long-term storage. |
| 28 | 29 | gpa: *Allocator, |
| 30 | /// Arena-allocated memory used during initialization. Should be untouched until deinit. |
| 31 | arena_state: std.heap.ArenaAllocator.State, |
| 29 | 32 | /// Pointer to externally managed resource. `null` if there is no zig file being compiled. |
| 30 | 33 | root_pkg: ?*Package, |
| 31 | 34 | /// Module owns this resource. |
| ... | ... | @@ -85,6 +88,12 @@ deletion_set: std.ArrayListUnmanaged(*Decl) = .{}, |
| 85 | 88 | root_name: []u8, |
| 86 | 89 | keep_source_files_loaded: bool, |
| 87 | 90 | use_clang: bool, |
| 91 | sanitize_c: bool, |
| 92 | /// When this is `true` it means invoking clang as a sub-process is expected to inherit |
| 93 | /// stdin, stdout, stderr, and if it returns non success, to forward the exit code. |
| 94 | /// Otherwise we attempt to parse the error messages and expose them via the Module API. |
| 95 | /// This is `true` for `zig cc`, `zig c++`, and `zig translate-c`. |
| 96 | clang_passthrough_mode: bool, |
| 88 | 97 | |
| 89 | 98 | /// Error tags and their values, tag names are duped with mod.gpa. |
| 90 | 99 | global_error_set: std.StringHashMapUnmanaged(u16) = .{}, |
| ... | ... | @@ -92,6 +101,12 @@ global_error_set: std.StringHashMapUnmanaged(u16) = .{}, |
| 92 | 101 | c_source_files: []const []const u8, |
| 93 | 102 | clang_argv: []const []const u8, |
| 94 | 103 | cache: std.cache_hash.CacheHash, |
| 104 | /// Path to own executable for invoking `zig clang`. |
| 105 | self_exe_path: ?[]const u8, |
| 106 | zig_lib_dir: []const u8, |
| 107 | zig_cache_dir_path: []const u8, |
| 108 | libc_include_dir_list: []const []const u8, |
| 109 | rand: *std.rand.Random, |
| 95 | 110 | |
| 96 | 111 | pub const InnerError = error{ OutOfMemory, AnalysisFail }; |
| 97 | 112 | |
| ... | ... | @@ -913,10 +928,12 @@ pub const AllErrors = struct { |
| 913 | 928 | }; |
| 914 | 929 | |
| 915 | 930 | pub const InitOptions = struct { |
| 916 | | target: std.Target, |
| 931 | zig_lib_dir: []const u8, |
| 932 | target: Target, |
| 917 | 933 | root_name: []const u8, |
| 918 | 934 | root_pkg: ?*Package, |
| 919 | 935 | output_mode: std.builtin.OutputMode, |
| 936 | rand: *std.rand.Random, |
| 920 | 937 | bin_file_dir: ?std.fs.Dir = null, |
| 921 | 938 | bin_file_path: []const u8, |
| 922 | 939 | emit_h: ?[]const u8 = null, |
| ... | ... | @@ -932,8 +949,8 @@ pub const InitOptions = struct { |
| 932 | 949 | framework_dirs: []const []const u8 = &[0][]const u8{}, |
| 933 | 950 | frameworks: []const []const u8 = &[0][]const u8{}, |
| 934 | 951 | system_libs: []const []const u8 = &[0][]const u8{}, |
| 935 | | have_libc: bool = false, |
| 936 | | have_libcpp: bool = false, |
| 952 | link_libc: bool = false, |
| 953 | link_libcpp: bool = false, |
| 937 | 954 | want_pic: ?bool = null, |
| 938 | 955 | want_sanitize_c: ?bool = null, |
| 939 | 956 | use_llvm: ?bool = null, |
| ... | ... | @@ -943,170 +960,232 @@ pub const InitOptions = struct { |
| 943 | 960 | strip: bool = false, |
| 944 | 961 | linker_script: ?[]const u8 = null, |
| 945 | 962 | version_script: ?[]const u8 = null, |
| 946 | | disable_c_depfile: bool = false, |
| 947 | 963 | override_soname: ?[]const u8 = null, |
| 948 | 964 | linker_optimization: ?[]const u8 = null, |
| 949 | 965 | linker_gc_sections: ?bool = null, |
| 966 | function_sections: ?bool = null, |
| 950 | 967 | linker_allow_shlib_undefined: ?bool = null, |
| 951 | 968 | linker_bind_global_refs_locally: ?bool = null, |
| 969 | disable_c_depfile: bool = false, |
| 952 | 970 | linker_z_nodelete: bool = false, |
| 953 | 971 | linker_z_defs: bool = false, |
| 972 | clang_passthrough_mode: bool = false, |
| 954 | 973 | stack_size_override: u64 = 0, |
| 974 | self_exe_path: ?[]const u8 = null, |
| 955 | 975 | }; |
| 956 | 976 | |
| 957 | | pub fn init(gpa: *Allocator, options: InitOptions) !Module { |
| 958 | | const root_name = try gpa.dupe(u8, options.root_name); |
| 959 | | errdefer gpa.free(root_name); |
| 960 | | |
| 961 | | const ofmt = options.object_format orelse options.target.getObjectFormat(); |
| 962 | | |
| 963 | | // Make a decision on whether to use LLD or our own linker. |
| 964 | | const use_lld = if (options.use_lld) |explicit| explicit else blk: { |
| 965 | | if (!build_options.have_llvm) |
| 977 | pub fn create(gpa: *Allocator, options: InitOptions) !*Module { |
| 978 | const mod: *Module = mod: { |
| 979 | // For allocations that have the same lifetime as Module. This arena is used only during this |
| 980 | // initialization and then is freed in deinit(). |
| 981 | var arena_allocator = std.heap.ArenaAllocator.init(gpa); |
| 982 | errdefer arena_allocator.deinit(); |
| 983 | const arena = &arena_allocator.allocator; |
| 984 | |
| 985 | // We put the `Module` itself in the arena. Freeing the arena will free the module. |
| 986 | // It's initialized later after we prepare the initialization options. |
| 987 | const mod = try arena.create(Module); |
| 988 | const root_name = try arena.dupe(u8, options.root_name); |
| 989 | |
| 990 | const ofmt = options.object_format orelse options.target.getObjectFormat(); |
| 991 | |
| 992 | // Make a decision on whether to use LLD or our own linker. |
| 993 | const use_lld = if (options.use_lld) |explicit| explicit else blk: { |
| 994 | if (!build_options.have_llvm) |
| 995 | break :blk false; |
| 996 | |
| 997 | if (ofmt == .c) |
| 998 | break :blk false; |
| 999 | |
| 1000 | // Our linker can't handle objects or most advanced options yet. |
| 1001 | if (options.link_objects.len != 0 or |
| 1002 | options.c_source_files.len != 0 or |
| 1003 | options.frameworks.len != 0 or |
| 1004 | options.system_libs.len != 0 or |
| 1005 | options.link_libc or options.link_libcpp or |
| 1006 | options.linker_script != null or options.version_script != null) |
| 1007 | { |
| 1008 | break :blk true; |
| 1009 | } |
| 966 | 1010 | break :blk false; |
| 1011 | }; |
| 967 | 1012 | |
| 968 | | if (ofmt == .c) |
| 1013 | // Make a decision on whether to use LLVM or our own backend. |
| 1014 | const use_llvm = if (options.use_llvm) |explicit| explicit else blk: { |
| 1015 | // We would want to prefer LLVM for release builds when it is available, however |
| 1016 | // we don't have an LLVM backend yet :) |
| 1017 | // We would also want to prefer LLVM for architectures that we don't have self-hosted support for too. |
| 969 | 1018 | break :blk false; |
| 1019 | }; |
| 970 | 1020 | |
| 971 | | // Our linker can't handle objects or most advanced options yet. |
| 972 | | if (options.link_objects.len != 0 or |
| 973 | | options.c_source_files.len != 0 or |
| 974 | | options.frameworks.len != 0 or |
| 975 | | options.system_libs.len != 0 or |
| 976 | | options.have_libc or options.have_libcpp or |
| 977 | | options.linker_script != null or options.version_script != null) |
| 978 | | { |
| 979 | | break :blk true; |
| 980 | | } |
| 981 | | break :blk false; |
| 982 | | }; |
| 983 | | |
| 984 | | // Make a decision on whether to use LLVM or our own backend. |
| 985 | | const use_llvm = if (options.use_llvm) |explicit| explicit else blk: { |
| 986 | | // We would want to prefer LLVM for release builds when it is available, however |
| 987 | | // we don't have an LLVM backend yet :) |
| 988 | | // We would also want to prefer LLVM for architectures that we don't have self-hosted support for too. |
| 989 | | break :blk false; |
| 990 | | }; |
| 991 | | |
| 992 | | const bin_file_dir = options.bin_file_dir orelse std.fs.cwd(); |
| 993 | | const bin_file = try link.File.openPath(gpa, bin_file_dir, options.bin_file_path, .{ |
| 994 | | .root_name = root_name, |
| 995 | | .root_pkg = options.root_pkg, |
| 996 | | .target = options.target, |
| 997 | | .output_mode = options.output_mode, |
| 998 | | .link_mode = options.link_mode orelse .Static, |
| 999 | | .object_format = ofmt, |
| 1000 | | .optimize_mode = options.optimize_mode, |
| 1001 | | .use_lld = use_lld, |
| 1002 | | .use_llvm = use_llvm, |
| 1003 | | .objects = options.link_objects, |
| 1004 | | .frameworks = options.frameworks, |
| 1005 | | .framework_dirs = options.framework_dirs, |
| 1006 | | .system_libs = options.system_libs, |
| 1007 | | .lib_dirs = options.lib_dirs, |
| 1008 | | .rpath_list = options.rpath_list, |
| 1009 | | .strip = options.strip, |
| 1010 | | }); |
| 1011 | | errdefer bin_file.destroy(); |
| 1012 | | |
| 1013 | | const root_scope = blk: { |
| 1014 | | if (options.root_pkg) |root_pkg| { |
| 1015 | | if (mem.endsWith(u8, root_pkg.root_src_path, ".zig")) { |
| 1016 | | const root_scope = try gpa.create(Scope.File); |
| 1017 | | root_scope.* = .{ |
| 1018 | | .sub_file_path = root_pkg.root_src_path, |
| 1019 | | .source = .{ .unloaded = {} }, |
| 1020 | | .contents = .{ .not_available = {} }, |
| 1021 | | .status = .never_loaded, |
| 1022 | | .root_container = .{ |
| 1023 | | .file_scope = root_scope, |
| 1021 | const bin_file_dir = options.bin_file_dir orelse std.fs.cwd(); |
| 1022 | const bin_file = try link.File.openPath(gpa, bin_file_dir, options.bin_file_path, .{ |
| 1023 | .root_name = root_name, |
| 1024 | .root_pkg = options.root_pkg, |
| 1025 | .target = options.target, |
| 1026 | .output_mode = options.output_mode, |
| 1027 | .link_mode = options.link_mode orelse .Static, |
| 1028 | .object_format = ofmt, |
| 1029 | .optimize_mode = options.optimize_mode, |
| 1030 | .use_lld = use_lld, |
| 1031 | .use_llvm = use_llvm, |
| 1032 | .link_libc = options.link_libc, |
| 1033 | .link_libcpp = options.link_libcpp, |
| 1034 | .objects = options.link_objects, |
| 1035 | .frameworks = options.frameworks, |
| 1036 | .framework_dirs = options.framework_dirs, |
| 1037 | .system_libs = options.system_libs, |
| 1038 | .lib_dirs = options.lib_dirs, |
| 1039 | .rpath_list = options.rpath_list, |
| 1040 | .strip = options.strip, |
| 1041 | .function_sections = options.function_sections orelse false, |
| 1042 | }); |
| 1043 | errdefer bin_file.destroy(); |
| 1044 | |
| 1045 | // We arena-allocate the root scope so there is no free needed. |
| 1046 | const root_scope = blk: { |
| 1047 | if (options.root_pkg) |root_pkg| { |
| 1048 | if (mem.endsWith(u8, root_pkg.root_src_path, ".zig")) { |
| 1049 | const root_scope = try gpa.create(Scope.File); |
| 1050 | root_scope.* = .{ |
| 1051 | .sub_file_path = root_pkg.root_src_path, |
| 1052 | .source = .{ .unloaded = {} }, |
| 1053 | .contents = .{ .not_available = {} }, |
| 1054 | .status = .never_loaded, |
| 1055 | .root_container = .{ |
| 1056 | .file_scope = root_scope, |
| 1057 | .decls = .{}, |
| 1058 | }, |
| 1059 | }; |
| 1060 | break :blk &root_scope.base; |
| 1061 | } else if (mem.endsWith(u8, root_pkg.root_src_path, ".zir")) { |
| 1062 | const root_scope = try gpa.create(Scope.ZIRModule); |
| 1063 | root_scope.* = .{ |
| 1064 | .sub_file_path = root_pkg.root_src_path, |
| 1065 | .source = .{ .unloaded = {} }, |
| 1066 | .contents = .{ .not_available = {} }, |
| 1067 | .status = .never_loaded, |
| 1024 | 1068 | .decls = .{}, |
| 1025 | | }, |
| 1026 | | }; |
| 1027 | | break :blk &root_scope.base; |
| 1028 | | } else if (mem.endsWith(u8, root_pkg.root_src_path, ".zir")) { |
| 1029 | | const root_scope = try gpa.create(Scope.ZIRModule); |
| 1030 | | root_scope.* = .{ |
| 1031 | | .sub_file_path = root_pkg.root_src_path, |
| 1032 | | .source = .{ .unloaded = {} }, |
| 1033 | | .contents = .{ .not_available = {} }, |
| 1034 | | .status = .never_loaded, |
| 1035 | | .decls = .{}, |
| 1036 | | }; |
| 1037 | | break :blk &root_scope.base; |
| 1069 | }; |
| 1070 | break :blk &root_scope.base; |
| 1071 | } else { |
| 1072 | unreachable; |
| 1073 | } |
| 1038 | 1074 | } else { |
| 1039 | | unreachable; |
| 1075 | const root_scope = try gpa.create(Scope.None); |
| 1076 | root_scope.* = .{}; |
| 1077 | break :blk &root_scope.base; |
| 1040 | 1078 | } |
| 1041 | | } else { |
| 1042 | | const root_scope = try gpa.create(Scope.None); |
| 1043 | | root_scope.* = .{}; |
| 1044 | | break :blk &root_scope.base; |
| 1045 | | } |
| 1046 | | }; |
| 1079 | }; |
| 1047 | 1080 | |
| 1048 | | // We put everything into the cache hash except for the root source file, because we want to |
| 1049 | | // find the same binary and incrementally update it even if the file contents changed. |
| 1050 | | // TODO Look into storing this information in memory rather than on disk and solving |
| 1051 | | // serialization/deserialization of *all* incremental compilation state in a more generic way. |
| 1052 | | const cache_dir = if (options.root_pkg) |root_pkg| root_pkg.root_src_dir else std.fs.cwd(); |
| 1053 | | var cache = try std.cache_hash.CacheHash.init(gpa, cache_dir, "zig-cache"); |
| 1054 | | errdefer cache.release(); |
| 1055 | | |
| 1056 | | // Now we will prepare hash state initializations to avoid redundantly computing hashes. |
| 1057 | | // First we add common things between things that apply to zig source and all c source files. |
| 1058 | | cache.addBytes(build_options.version); |
| 1059 | | cache.add(options.optimize_mode); |
| 1060 | | cache.add(options.target.cpu.arch); |
| 1061 | | cache.addBytes(options.target.cpu.model.name); |
| 1062 | | cache.add(options.target.cpu.features.ints); |
| 1063 | | cache.add(options.target.os.tag); |
| 1064 | | switch (options.target.os.tag) { |
| 1065 | | .linux => { |
| 1066 | | cache.add(options.target.os.version_range.linux.range.min); |
| 1067 | | cache.add(options.target.os.version_range.linux.range.max); |
| 1068 | | cache.add(options.target.os.version_range.linux.glibc); |
| 1069 | | }, |
| 1070 | | .windows => { |
| 1071 | | cache.add(options.target.os.version_range.windows.min); |
| 1072 | | cache.add(options.target.os.version_range.windows.max); |
| 1073 | | }, |
| 1074 | | .freebsd, |
| 1075 | | .macosx, |
| 1076 | | .ios, |
| 1077 | | .tvos, |
| 1078 | | .watchos, |
| 1079 | | .netbsd, |
| 1080 | | .openbsd, |
| 1081 | | .dragonfly, |
| 1082 | | => { |
| 1083 | | cache.add(options.target.os.version_range.semver.min); |
| 1084 | | cache.add(options.target.os.version_range.semver.max); |
| 1085 | | }, |
| 1086 | | else => {}, |
| 1087 | | } |
| 1088 | | cache.add(options.target.abi); |
| 1089 | | cache.add(ofmt); |
| 1090 | | // TODO PIC (see detect_pic from codegen.cpp) |
| 1091 | | cache.add(bin_file.options.link_mode); |
| 1092 | | cache.add(options.strip); |
| 1093 | | |
| 1094 | | // Make a decision on whether to use Clang for translate-c and compiling C files. |
| 1095 | | const use_clang = if (options.use_clang) |explicit| explicit else blk: { |
| 1096 | | if (build_options.have_llvm) { |
| 1097 | | // Can't use it if we don't have it! |
| 1098 | | break :blk false; |
| 1081 | // We put everything into the cache hash except for the root source file, because we want to |
| 1082 | // find the same binary and incrementally update it even if the file contents changed. |
| 1083 | // TODO Look into storing this information in memory rather than on disk and solving |
| 1084 | // serialization/deserialization of *all* incremental compilation state in a more generic way. |
| 1085 | const cache_parent_dir = if (options.root_pkg) |root_pkg| root_pkg.root_src_dir else std.fs.cwd(); |
| 1086 | var cache_dir = try cache_parent_dir.makeOpenPath("zig-cache", .{}); |
| 1087 | defer cache_dir.close(); |
| 1088 | |
| 1089 | try cache_dir.makePath("tmp"); |
| 1090 | try cache_dir.makePath("o"); |
| 1091 | // We need this string because of sending paths to clang as a child process. |
| 1092 | const zig_cache_dir_path = if (options.root_pkg) |root_pkg| |
| 1093 | try std.fmt.allocPrint(arena, "{}" ++ std.fs.path.sep_str ++ "zig-cache", .{root_pkg.root_src_dir_path}) |
| 1094 | else |
| 1095 | "zig-cache"; |
| 1096 | |
| 1097 | var cache = try std.cache_hash.CacheHash.init(gpa, cache_dir, "h"); |
| 1098 | errdefer cache.release(); |
| 1099 | |
| 1100 | // Now we will prepare hash state initializations to avoid redundantly computing hashes. |
| 1101 | // First we add common things between things that apply to zig source and all c source files. |
| 1102 | cache.addBytes(build_options.version); |
| 1103 | cache.add(options.optimize_mode); |
| 1104 | cache.add(options.target.cpu.arch); |
| 1105 | cache.addBytes(options.target.cpu.model.name); |
| 1106 | cache.add(options.target.cpu.features.ints); |
| 1107 | cache.add(options.target.os.tag); |
| 1108 | switch (options.target.os.tag) { |
| 1109 | .linux => { |
| 1110 | cache.add(options.target.os.version_range.linux.range.min); |
| 1111 | cache.add(options.target.os.version_range.linux.range.max); |
| 1112 | cache.add(options.target.os.version_range.linux.glibc); |
| 1113 | }, |
| 1114 | .windows => { |
| 1115 | cache.add(options.target.os.version_range.windows.min); |
| 1116 | cache.add(options.target.os.version_range.windows.max); |
| 1117 | }, |
| 1118 | .freebsd, |
| 1119 | .macosx, |
| 1120 | .ios, |
| 1121 | .tvos, |
| 1122 | .watchos, |
| 1123 | .netbsd, |
| 1124 | .openbsd, |
| 1125 | .dragonfly, |
| 1126 | => { |
| 1127 | cache.add(options.target.os.version_range.semver.min); |
| 1128 | cache.add(options.target.os.version_range.semver.max); |
| 1129 | }, |
| 1130 | else => {}, |
| 1099 | 1131 | } |
| 1100 | | // It's not planned to do our own translate-c or C compilation. |
| 1101 | | break :blk true; |
| 1132 | cache.add(options.target.abi); |
| 1133 | cache.add(ofmt); |
| 1134 | // TODO PIC (see detect_pic from codegen.cpp) |
| 1135 | cache.add(bin_file.options.link_mode); |
| 1136 | cache.add(options.strip); |
| 1137 | |
| 1138 | // Make a decision on whether to use Clang for translate-c and compiling C files. |
| 1139 | const use_clang = if (options.use_clang) |explicit| explicit else blk: { |
| 1140 | if (build_options.have_llvm) { |
| 1141 | // Can't use it if we don't have it! |
| 1142 | break :blk false; |
| 1143 | } |
| 1144 | // It's not planned to do our own translate-c or C compilation. |
| 1145 | break :blk true; |
| 1146 | }; |
| 1147 | |
| 1148 | const libc_include_dir_list = try detectLibCIncludeDirs( |
| 1149 | arena, |
| 1150 | options.zig_lib_dir, |
| 1151 | options.target, |
| 1152 | options.link_libc, |
| 1153 | ); |
| 1154 | |
| 1155 | const sanitize_c: bool = options.want_sanitize_c orelse switch (options.optimize_mode) { |
| 1156 | .Debug, .ReleaseSafe => true, |
| 1157 | .ReleaseSmall, .ReleaseFast => false, |
| 1158 | }; |
| 1159 | |
| 1160 | mod.* = .{ |
| 1161 | .gpa = gpa, |
| 1162 | .arena_state = arena_allocator.state, |
| 1163 | .zig_lib_dir = options.zig_lib_dir, |
| 1164 | .zig_cache_dir_path = zig_cache_dir_path, |
| 1165 | .root_name = root_name, |
| 1166 | .root_pkg = options.root_pkg, |
| 1167 | .root_scope = root_scope, |
| 1168 | .bin_file_dir = bin_file_dir, |
| 1169 | .bin_file_path = options.bin_file_path, |
| 1170 | .bin_file = bin_file, |
| 1171 | .work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa), |
| 1172 | .keep_source_files_loaded = options.keep_source_files_loaded, |
| 1173 | .use_clang = use_clang, |
| 1174 | .clang_argv = options.clang_argv, |
| 1175 | .c_source_files = options.c_source_files, |
| 1176 | .cache = cache, |
| 1177 | .self_exe_path = options.self_exe_path, |
| 1178 | .libc_include_dir_list = libc_include_dir_list, |
| 1179 | .sanitize_c = sanitize_c, |
| 1180 | .rand = options.rand, |
| 1181 | .clang_passthrough_mode = options.clang_passthrough_mode, |
| 1182 | }; |
| 1183 | break :mod mod; |
| 1102 | 1184 | }; |
| 1103 | | var c_object_table = std.AutoArrayHashMapUnmanaged(*CObject, void){}; |
| 1104 | | errdefer { |
| 1105 | | for (c_object_table.items()) |entry| entry.key.destroy(gpa); |
| 1106 | | c_object_table.deinit(gpa); |
| 1107 | | } |
| 1185 | errdefer mod.destroy(); |
| 1186 | |
| 1108 | 1187 | // Add a `CObject` for each `c_source_files`. |
| 1109 | | try c_object_table.ensureCapacity(gpa, options.c_source_files.len); |
| 1188 | try mod.c_object_table.ensureCapacity(gpa, options.c_source_files.len); |
| 1110 | 1189 | for (options.c_source_files) |c_source_file| { |
| 1111 | 1190 | var local_arena = std.heap.ArenaAllocator.init(gpa); |
| 1112 | 1191 | errdefer local_arena.deinit(); |
| ... | ... | @@ -1120,31 +1199,15 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module { |
| 1120 | 1199 | .extra_flags = &[0][]const u8{}, |
| 1121 | 1200 | .arena = local_arena.state, |
| 1122 | 1201 | }; |
| 1123 | | c_object_table.putAssumeCapacityNoClobber(c_object, {}); |
| 1124 | | } |
| 1125 | | |
| 1126 | | return Module{ |
| 1127 | | .gpa = gpa, |
| 1128 | | .root_name = root_name, |
| 1129 | | .root_pkg = options.root_pkg, |
| 1130 | | .root_scope = root_scope, |
| 1131 | | .bin_file_dir = bin_file_dir, |
| 1132 | | .bin_file_path = options.bin_file_path, |
| 1133 | | .bin_file = bin_file, |
| 1134 | | .work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa), |
| 1135 | | .keep_source_files_loaded = options.keep_source_files_loaded, |
| 1136 | | .use_clang = use_clang, |
| 1137 | | .clang_argv = options.clang_argv, |
| 1138 | | .c_source_files = options.c_source_files, |
| 1139 | | .cache = cache, |
| 1140 | | .c_object_table = c_object_table, |
| 1141 | | }; |
| 1202 | mod.c_object_table.putAssumeCapacityNoClobber(c_object, {}); |
| 1203 | } |
| 1204 | |
| 1205 | return mod; |
| 1142 | 1206 | } |
| 1143 | 1207 | |
| 1144 | | pub fn deinit(self: *Module) void { |
| 1208 | pub fn destroy(self: *Module) void { |
| 1145 | 1209 | self.bin_file.destroy(); |
| 1146 | 1210 | const gpa = self.gpa; |
| 1147 | | self.gpa.free(self.root_name); |
| 1148 | 1211 | self.deletion_set.deinit(gpa); |
| 1149 | 1212 | self.work_queue.deinit(); |
| 1150 | 1213 | |
| ... | ... | @@ -1198,7 +1261,9 @@ pub fn deinit(self: *Module) void { |
| 1198 | 1261 | } |
| 1199 | 1262 | self.global_error_set.deinit(gpa); |
| 1200 | 1263 | self.cache.release(); |
| 1201 | | self.* = undefined; |
| 1264 | |
| 1265 | // This destroys `self`. |
| 1266 | self.arena_state.promote(gpa).deinit(); |
| 1202 | 1267 | } |
| 1203 | 1268 | |
| 1204 | 1269 | fn freeExportList(gpa: *Allocator, export_list: []*Export) void { |
| ... | ... | @@ -1209,7 +1274,7 @@ fn freeExportList(gpa: *Allocator, export_list: []*Export) void { |
| 1209 | 1274 | gpa.free(export_list); |
| 1210 | 1275 | } |
| 1211 | 1276 | |
| 1212 | | pub fn target(self: Module) std.Target { |
| 1277 | pub fn getTarget(self: Module) Target { |
| 1213 | 1278 | return self.bin_file.options.target; |
| 1214 | 1279 | } |
| 1215 | 1280 | |
| ... | ... | @@ -1440,29 +1505,335 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void { |
| 1440 | 1505 | c_object.status = .{ .new = {} }; |
| 1441 | 1506 | }, |
| 1442 | 1507 | } |
| 1443 | | if (!build_options.have_llvm) { |
| 1444 | | try self.failed_c_objects.ensureCapacity(self.gpa, self.failed_c_objects.items().len + 1); |
| 1445 | | self.failed_c_objects.putAssumeCapacityNoClobber(c_object, try ErrorMsg.create( |
| 1446 | | self.gpa, |
| 1447 | | 0, |
| 1448 | | "clang not available: compiler not built with LLVM extensions enabled", |
| 1449 | | .{}, |
| 1450 | | )); |
| 1451 | | c_object.status = .{ .failure = "" }; |
| 1452 | | continue; |
| 1453 | | } |
| 1454 | | try self.failed_c_objects.ensureCapacity(self.gpa, self.failed_c_objects.items().len + 1); |
| 1455 | | self.failed_c_objects.putAssumeCapacityNoClobber(c_object, try ErrorMsg.create( |
| 1456 | | self.gpa, |
| 1457 | | 0, |
| 1458 | | "TODO: implement invoking clang to compile C source files", |
| 1459 | | .{}, |
| 1460 | | )); |
| 1461 | | c_object.status = .{ .failure = "" }; |
| 1508 | self.buildCObject(c_object) catch |err| switch (err) { |
| 1509 | error.AnalysisFail => continue, |
| 1510 | else => { |
| 1511 | try self.failed_c_objects.ensureCapacity(self.gpa, self.failed_c_objects.items().len + 1); |
| 1512 | self.failed_c_objects.putAssumeCapacityNoClobber(c_object, try ErrorMsg.create( |
| 1513 | self.gpa, |
| 1514 | 0, |
| 1515 | "unable to build C object: {}", |
| 1516 | .{@errorName(err)}, |
| 1517 | )); |
| 1518 | c_object.status = .{ .failure = "" }; |
| 1519 | }, |
| 1520 | }; |
| 1462 | 1521 | }, |
| 1463 | 1522 | }; |
| 1464 | 1523 | } |
| 1465 | 1524 | |
| 1525 | fn buildCObject(mod: *Module, c_object: *CObject) !void { |
| 1526 | const tracy = trace(@src()); |
| 1527 | defer tracy.end(); |
| 1528 | |
| 1529 | if (!build_options.have_llvm) { |
| 1530 | return mod.failCObj(c_object, "clang not available: compiler not built with LLVM extensions enabled", .{}); |
| 1531 | } |
| 1532 | const self_exe_path = mod.self_exe_path orelse |
| 1533 | return mod.failCObj(c_object, "clang compilation disabled", .{}); |
| 1534 | |
| 1535 | var arena_allocator = std.heap.ArenaAllocator.init(mod.gpa); |
| 1536 | defer arena_allocator.deinit(); |
| 1537 | const arena = &arena_allocator.allocator; |
| 1538 | |
| 1539 | var argv = std.ArrayList([]const u8).init(mod.gpa); |
| 1540 | defer argv.deinit(); |
| 1541 | |
| 1542 | const c_source_basename = std.fs.path.basename(c_object.src_path); |
| 1543 | // Special case when doing build-obj for just one C file. When there are more than one object |
| 1544 | // file and building an object we need to link them together, but with just one it should go |
| 1545 | // directly to the output file. |
| 1546 | const direct_o = mod.c_source_files.len == 1 and mod.root_pkg == null and |
| 1547 | mod.bin_file.options.output_mode == .Obj and mod.bin_file.options.objects.len == 0; |
| 1548 | const o_basename_noext = if (direct_o) mod.root_name else mem.split(c_source_basename, ".").next().?; |
| 1549 | const o_basename = try std.fmt.allocPrint(arena, "{}{}", .{ o_basename_noext, mod.getTarget().oFileExt() }); |
| 1550 | |
| 1551 | // We can't know the digest until we do the C compiler invocation, so we need a temporary filename. |
| 1552 | const out_obj_path = try mod.tmpFilePath(arena, o_basename); |
| 1553 | |
| 1554 | try argv.appendSlice(&[_][]const u8{ self_exe_path, "clang", "-c" }); |
| 1555 | |
| 1556 | const ext = classifyFileExt(c_object.src_path); |
| 1557 | // TODO capture the .d file and deal with caching stuff |
| 1558 | try mod.addCCArgs(arena, &argv, ext, false, null); |
| 1559 | |
| 1560 | try argv.append("-o"); |
| 1561 | try argv.append(out_obj_path); |
| 1562 | |
| 1563 | try argv.append(c_object.src_path); |
| 1564 | try argv.appendSlice(c_object.extra_flags); |
| 1565 | |
| 1566 | //for (argv.items) |arg| { |
| 1567 | // std.debug.print("{} ", .{arg}); |
| 1568 | //} |
| 1569 | |
| 1570 | const child = try std.ChildProcess.init(argv.items, arena); |
| 1571 | defer child.deinit(); |
| 1572 | |
| 1573 | if (mod.clang_passthrough_mode) { |
| 1574 | child.stdin_behavior = .Inherit; |
| 1575 | child.stdout_behavior = .Inherit; |
| 1576 | child.stderr_behavior = .Inherit; |
| 1577 | |
| 1578 | const term = child.spawnAndWait() catch |err| { |
| 1579 | return mod.failCObj(c_object, "unable to spawn {}: {}", .{ argv.items[0], @errorName(err) }); |
| 1580 | }; |
| 1581 | switch (term) { |
| 1582 | .Exited => |code| { |
| 1583 | if (code != 0) { |
| 1584 | // TODO make std.process.exit and std.ChildProcess exit code have the same type |
| 1585 | // and forward it here. Currently it is u32 vs u8. |
| 1586 | std.process.exit(1); |
| 1587 | } |
| 1588 | }, |
| 1589 | else => std.process.exit(1), |
| 1590 | } |
| 1591 | } else { |
| 1592 | child.stdin_behavior = .Ignore; |
| 1593 | child.stdout_behavior = .Pipe; |
| 1594 | child.stderr_behavior = .Pipe; |
| 1595 | |
| 1596 | try child.spawn(); |
| 1597 | |
| 1598 | const stdout_reader = child.stdout.?.reader(); |
| 1599 | const stderr_reader = child.stderr.?.reader(); |
| 1600 | |
| 1601 | // TODO Need to poll to read these streams to prevent a deadlock (or rely on evented I/O). |
| 1602 | const stdout = try stdout_reader.readAllAlloc(arena, std.math.maxInt(u32)); |
| 1603 | const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024); |
| 1604 | |
| 1605 | const term = child.wait() catch |err| { |
| 1606 | return mod.failCObj(c_object, "unable to spawn {}: {}", .{ argv.items[0], @errorName(err) }); |
| 1607 | }; |
| 1608 | |
| 1609 | switch (term) { |
| 1610 | .Exited => |code| { |
| 1611 | if (code != 0) { |
| 1612 | // TODO parse clang stderr and turn it into an error message |
| 1613 | // and then call failCObjWithOwnedErrorMsg |
| 1614 | std.log.err("clang failed with stderr: {}", .{stderr}); |
| 1615 | return mod.failCObj(c_object, "clang exited with code {}", .{code}); |
| 1616 | } |
| 1617 | }, |
| 1618 | else => { |
| 1619 | std.log.err("clang terminated with stderr: {}", .{stderr}); |
| 1620 | return mod.failCObj(c_object, "clang terminated unexpectedly", .{}); |
| 1621 | }, |
| 1622 | } |
| 1623 | } |
| 1624 | |
| 1625 | // TODO handle .d files |
| 1626 | |
| 1627 | // TODO rename into place |
| 1628 | std.debug.print("TODO rename {} into cache dir\n", .{out_obj_path}); |
| 1629 | |
| 1630 | // TODO use the cache file name instead of tmp file name |
| 1631 | const success_file_path = try mod.gpa.dupe(u8, out_obj_path); |
| 1632 | c_object.status = .{ .success = success_file_path }; |
| 1633 | } |
| 1634 | |
| 1635 | fn tmpFilePath(mod: *Module, arena: *Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 { |
| 1636 | const s = std.fs.path.sep_str; |
| 1637 | return std.fmt.allocPrint( |
| 1638 | arena, |
| 1639 | "{}" ++ s ++ "tmp" ++ s ++ "{x}-{}", |
| 1640 | .{ mod.zig_cache_dir_path, mod.rand.int(u64), suffix }, |
| 1641 | ); |
| 1642 | } |
| 1643 | |
| 1644 | /// Add common C compiler args between translate-c and C object compilation. |
| 1645 | fn addCCArgs( |
| 1646 | mod: *Module, |
| 1647 | arena: *Allocator, |
| 1648 | argv: *std.ArrayList([]const u8), |
| 1649 | ext: FileExt, |
| 1650 | translate_c: bool, |
| 1651 | out_dep_path: ?[]const u8, |
| 1652 | ) !void { |
| 1653 | const target = mod.getTarget(); |
| 1654 | |
| 1655 | if (translate_c) { |
| 1656 | try argv.appendSlice(&[_][]const u8{ "-x", "c" }); |
| 1657 | } |
| 1658 | |
| 1659 | if (ext == .cpp) { |
| 1660 | try argv.append("-nostdinc++"); |
| 1661 | } |
| 1662 | try argv.appendSlice(&[_][]const u8{ |
| 1663 | "-nostdinc", |
| 1664 | "-fno-spell-checking", |
| 1665 | }); |
| 1666 | |
| 1667 | // We don't ever put `-fcolor-diagnostics` or `-fno-color-diagnostics` because in passthrough mode |
| 1668 | // we want Clang to infer it, and in normal mode we always want it off, which will be true since |
| 1669 | // clang will detect stderr as a pipe rather than a terminal. |
| 1670 | if (!mod.clang_passthrough_mode) { |
| 1671 | // Make stderr more easily parseable. |
| 1672 | try argv.append("-fno-caret-diagnostics"); |
| 1673 | } |
| 1674 | |
| 1675 | if (mod.bin_file.options.function_sections) { |
| 1676 | try argv.append("-ffunction-sections"); |
| 1677 | } |
| 1678 | |
| 1679 | try argv.ensureCapacity(argv.items.len + mod.bin_file.options.framework_dirs.len * 2); |
| 1680 | for (mod.bin_file.options.framework_dirs) |framework_dir| { |
| 1681 | argv.appendAssumeCapacity("-iframework"); |
| 1682 | argv.appendAssumeCapacity(framework_dir); |
| 1683 | } |
| 1684 | |
| 1685 | if (mod.bin_file.options.link_libcpp) { |
| 1686 | const libcxx_include_path = try std.fs.path.join(arena, &[_][]const u8{ |
| 1687 | mod.zig_lib_dir, "libcxx", "include", |
| 1688 | }); |
| 1689 | const libcxxabi_include_path = try std.fs.path.join(arena, &[_][]const u8{ |
| 1690 | mod.zig_lib_dir, "libcxxabi", "include", |
| 1691 | }); |
| 1692 | |
| 1693 | try argv.append("-isystem"); |
| 1694 | try argv.append(libcxx_include_path); |
| 1695 | |
| 1696 | try argv.append("-isystem"); |
| 1697 | try argv.append(libcxxabi_include_path); |
| 1698 | |
| 1699 | if (target.abi.isMusl()) { |
| 1700 | try argv.append("-D_LIBCPP_HAS_MUSL_LIBC"); |
| 1701 | } |
| 1702 | try argv.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS"); |
| 1703 | try argv.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS"); |
| 1704 | } |
| 1705 | |
| 1706 | const llvm_triple = try @import("codegen/llvm.zig").targetTriple(arena, target); |
| 1707 | try argv.appendSlice(&[_][]const u8{ "-target", llvm_triple }); |
| 1708 | |
| 1709 | switch (ext) { |
| 1710 | .c, .cpp, .h => { |
| 1711 | // According to Rich Felker libc headers are supposed to go before C language headers. |
| 1712 | // However as noted by @dimenus, appending libc headers before c_headers breaks intrinsics |
| 1713 | // and other compiler specific items. |
| 1714 | const c_headers_dir = try std.fs.path.join(arena, &[_][]const u8{ mod.zig_lib_dir, "include" }); |
| 1715 | try argv.append("-isystem"); |
| 1716 | try argv.append(c_headers_dir); |
| 1717 | |
| 1718 | for (mod.libc_include_dir_list) |include_dir| { |
| 1719 | try argv.append("-isystem"); |
| 1720 | try argv.append(include_dir); |
| 1721 | } |
| 1722 | |
| 1723 | if (target.cpu.model.llvm_name) |llvm_name| { |
| 1724 | try argv.appendSlice(&[_][]const u8{ |
| 1725 | "-Xclang", "-target-cpu", "-Xclang", llvm_name, |
| 1726 | }); |
| 1727 | } |
| 1728 | // TODO CLI args for target features |
| 1729 | //if (g->zig_target->llvm_cpu_features != nullptr) { |
| 1730 | // // https://github.com/ziglang/zig/issues/5017 |
| 1731 | // SplitIterator it = memSplit(str(g->zig_target->llvm_cpu_features), str(",")); |
| 1732 | // Optional<Slice<uint8_t>> flag = SplitIterator_next(&it); |
| 1733 | // while (flag.is_some) { |
| 1734 | // try argv.append("-Xclang"); |
| 1735 | // try argv.append("-target-feature"); |
| 1736 | // try argv.append("-Xclang"); |
| 1737 | // try argv.append(buf_ptr(buf_create_from_slice(flag.value))); |
| 1738 | // flag = SplitIterator_next(&it); |
| 1739 | // } |
| 1740 | //} |
| 1741 | if (translate_c) { |
| 1742 | // This gives us access to preprocessing entities, presumably at the cost of performance. |
| 1743 | try argv.append("-Xclang"); |
| 1744 | try argv.append("-detailed-preprocessing-record"); |
| 1745 | } |
| 1746 | if (out_dep_path) |p| { |
| 1747 | try argv.append("-MD"); |
| 1748 | try argv.append("-MV"); |
| 1749 | try argv.append("-MF"); |
| 1750 | try argv.append(p); |
| 1751 | } |
| 1752 | }, |
| 1753 | .assembly, .ll, .bc, .unknown => {}, |
| 1754 | } |
| 1755 | // TODO CLI args for cpu features when compiling assembly |
| 1756 | //for (size_t i = 0; i < g->zig_target->llvm_cpu_features_asm_len; i += 1) { |
| 1757 | // try argv.append(g->zig_target->llvm_cpu_features_asm_ptr[i]); |
| 1758 | //} |
| 1759 | |
| 1760 | if (target.os.tag == .freestanding) { |
| 1761 | try argv.append("-ffreestanding"); |
| 1762 | } |
| 1763 | |
| 1764 | // windows.h has files such as pshpack1.h which do #pragma packing, triggering a clang warning. |
| 1765 | // So for this target, we disable this warning. |
| 1766 | if (target.os.tag == .windows and target.abi.isGnu()) { |
| 1767 | try argv.append("-Wno-pragma-pack"); |
| 1768 | } |
| 1769 | |
| 1770 | if (!mod.bin_file.options.strip) { |
| 1771 | try argv.append("-g"); |
| 1772 | } |
| 1773 | |
| 1774 | if (mod.haveFramePointer()) { |
| 1775 | try argv.append("-fno-omit-frame-pointer"); |
| 1776 | } else { |
| 1777 | try argv.append("-fomit-frame-pointer"); |
| 1778 | } |
| 1779 | |
| 1780 | if (mod.sanitize_c) { |
| 1781 | try argv.append("-fsanitize=undefined"); |
| 1782 | try argv.append("-fsanitize-trap=undefined"); |
| 1783 | } |
| 1784 | |
| 1785 | switch (mod.bin_file.options.optimize_mode) { |
| 1786 | .Debug => { |
| 1787 | // windows c runtime requires -D_DEBUG if using debug libraries |
| 1788 | try argv.append("-D_DEBUG"); |
| 1789 | try argv.append("-Og"); |
| 1790 | |
| 1791 | if (mod.bin_file.options.link_libc) { |
| 1792 | try argv.append("-fstack-protector-strong"); |
| 1793 | try argv.append("--param"); |
| 1794 | try argv.append("ssp-buffer-size=4"); |
| 1795 | } else { |
| 1796 | try argv.append("-fno-stack-protector"); |
| 1797 | } |
| 1798 | }, |
| 1799 | .ReleaseSafe => { |
| 1800 | // See the comment in the BuildModeFastRelease case for why we pass -O2 rather |
| 1801 | // than -O3 here. |
| 1802 | try argv.append("-O2"); |
| 1803 | if (mod.bin_file.options.link_libc) { |
| 1804 | try argv.append("-D_FORTIFY_SOURCE=2"); |
| 1805 | try argv.append("-fstack-protector-strong"); |
| 1806 | try argv.append("--param"); |
| 1807 | try argv.append("ssp-buffer-size=4"); |
| 1808 | } else { |
| 1809 | try argv.append("-fno-stack-protector"); |
| 1810 | } |
| 1811 | }, |
| 1812 | .ReleaseFast => { |
| 1813 | try argv.append("-DNDEBUG"); |
| 1814 | // Here we pass -O2 rather than -O3 because, although we do the equivalent of |
| 1815 | // -O3 in Zig code, the justification for the difference here is that Zig |
| 1816 | // has better detection and prevention of undefined behavior, so -O3 is safer for |
| 1817 | // Zig code than it is for C code. Also, C programmers are used to their code |
| 1818 | // running in -O2 and thus the -O3 path has been tested less. |
| 1819 | try argv.append("-O2"); |
| 1820 | try argv.append("-fno-stack-protector"); |
| 1821 | }, |
| 1822 | .ReleaseSmall => { |
| 1823 | try argv.append("-DNDEBUG"); |
| 1824 | try argv.append("-Os"); |
| 1825 | try argv.append("-fno-stack-protector"); |
| 1826 | }, |
| 1827 | } |
| 1828 | |
| 1829 | // TODO add CLI args for PIC |
| 1830 | //if (target_supports_fpic(g->zig_target) and g->have_pic) { |
| 1831 | // try argv.append("-fPIC"); |
| 1832 | //} |
| 1833 | |
| 1834 | try argv.appendSlice(mod.clang_argv); |
| 1835 | } |
| 1836 | |
| 1466 | 1837 | pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void { |
| 1467 | 1838 | const tracy = trace(@src()); |
| 1468 | 1839 | defer tracy.end(); |
| ... | ... | @@ -3041,7 +3412,7 @@ pub fn cmpNumeric( |
| 3041 | 3412 | } else if (rhs_ty_tag == .ComptimeFloat) { |
| 3042 | 3413 | break :x lhs.ty; |
| 3043 | 3414 | } |
| 3044 | | if (lhs.ty.floatBits(self.target()) >= rhs.ty.floatBits(self.target())) { |
| 3415 | if (lhs.ty.floatBits(self.getTarget()) >= rhs.ty.floatBits(self.getTarget())) { |
| 3045 | 3416 | break :x lhs.ty; |
| 3046 | 3417 | } else { |
| 3047 | 3418 | break :x rhs.ty; |
| ... | ... | @@ -3100,7 +3471,7 @@ pub fn cmpNumeric( |
| 3100 | 3471 | } else if (lhs_is_float) { |
| 3101 | 3472 | dest_float_type = lhs.ty; |
| 3102 | 3473 | } else { |
| 3103 | | const int_info = lhs.ty.intInfo(self.target()); |
| 3474 | const int_info = lhs.ty.intInfo(self.getTarget()); |
| 3104 | 3475 | lhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed); |
| 3105 | 3476 | } |
| 3106 | 3477 | |
| ... | ... | @@ -3135,7 +3506,7 @@ pub fn cmpNumeric( |
| 3135 | 3506 | } else if (rhs_is_float) { |
| 3136 | 3507 | dest_float_type = rhs.ty; |
| 3137 | 3508 | } else { |
| 3138 | | const int_info = rhs.ty.intInfo(self.target()); |
| 3509 | const int_info = rhs.ty.intInfo(self.getTarget()); |
| 3139 | 3510 | rhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed); |
| 3140 | 3511 | } |
| 3141 | 3512 | |
| ... | ... | @@ -3200,13 +3571,13 @@ pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Ty |
| 3200 | 3571 | next_inst.ty.isInt() and |
| 3201 | 3572 | prev_inst.ty.isSignedInt() == next_inst.ty.isSignedInt()) |
| 3202 | 3573 | { |
| 3203 | | if (prev_inst.ty.intInfo(self.target()).bits < next_inst.ty.intInfo(self.target()).bits) { |
| 3574 | if (prev_inst.ty.intInfo(self.getTarget()).bits < next_inst.ty.intInfo(self.getTarget()).bits) { |
| 3204 | 3575 | prev_inst = next_inst; |
| 3205 | 3576 | } |
| 3206 | 3577 | continue; |
| 3207 | 3578 | } |
| 3208 | 3579 | if (prev_inst.ty.isFloat() and next_inst.ty.isFloat()) { |
| 3209 | | if (prev_inst.ty.floatBits(self.target()) < next_inst.ty.floatBits(self.target())) { |
| 3580 | if (prev_inst.ty.floatBits(self.getTarget()) < next_inst.ty.floatBits(self.getTarget())) { |
| 3210 | 3581 | prev_inst = next_inst; |
| 3211 | 3582 | } |
| 3212 | 3583 | continue; |
| ... | ... | @@ -3274,8 +3645,8 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst |
| 3274 | 3645 | if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) { |
| 3275 | 3646 | assert(inst.value() == null); // handled above |
| 3276 | 3647 | |
| 3277 | | const src_info = inst.ty.intInfo(self.target()); |
| 3278 | | const dst_info = dest_type.intInfo(self.target()); |
| 3648 | const src_info = inst.ty.intInfo(self.getTarget()); |
| 3649 | const dst_info = dest_type.intInfo(self.getTarget()); |
| 3279 | 3650 | if ((src_info.signed == dst_info.signed and dst_info.bits >= src_info.bits) or |
| 3280 | 3651 | // small enough unsigned ints can get casted to large enough signed ints |
| 3281 | 3652 | (src_info.signed and !dst_info.signed and dst_info.bits > src_info.bits)) |
| ... | ... | @@ -3289,8 +3660,8 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst |
| 3289 | 3660 | if (inst.ty.zigTypeTag() == .Float and dest_type.zigTypeTag() == .Float) { |
| 3290 | 3661 | assert(inst.value() == null); // handled above |
| 3291 | 3662 | |
| 3292 | | const src_bits = inst.ty.floatBits(self.target()); |
| 3293 | | const dst_bits = dest_type.floatBits(self.target()); |
| 3663 | const src_bits = inst.ty.floatBits(self.getTarget()); |
| 3664 | const dst_bits = dest_type.floatBits(self.getTarget()); |
| 3294 | 3665 | if (dst_bits >= src_bits) { |
| 3295 | 3666 | const b = try self.requireRuntimeBlock(scope, inst.src); |
| 3296 | 3667 | return self.addUnOp(b, inst.src, dest_type, .floatcast, inst); |
| ... | ... | @@ -3312,14 +3683,14 @@ pub fn coerceNum(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !?* |
| 3312 | 3683 | } |
| 3313 | 3684 | return self.fail(scope, inst.src, "TODO float to int", .{}); |
| 3314 | 3685 | } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) { |
| 3315 | | if (!val.intFitsInType(dest_type, self.target())) { |
| 3686 | if (!val.intFitsInType(dest_type, self.getTarget())) { |
| 3316 | 3687 | return self.fail(scope, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val }); |
| 3317 | 3688 | } |
| 3318 | 3689 | return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); |
| 3319 | 3690 | } |
| 3320 | 3691 | } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) { |
| 3321 | 3692 | if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) { |
| 3322 | | const res = val.floatCast(scope.arena(), dest_type, self.target()) catch |err| switch (err) { |
| 3693 | const res = val.floatCast(scope.arena(), dest_type, self.getTarget()) catch |err| switch (err) { |
| 3323 | 3694 | error.Overflow => return self.fail( |
| 3324 | 3695 | scope, |
| 3325 | 3696 | inst.src, |
| ... | ... | @@ -3370,6 +3741,22 @@ fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *I |
| 3370 | 3741 | return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{}); |
| 3371 | 3742 | } |
| 3372 | 3743 | |
| 3744 | fn failCObj(mod: *Module, c_object: *CObject, comptime format: []const u8, args: anytype) InnerError { |
| 3745 | @setCold(true); |
| 3746 | const err_msg = try ErrorMsg.create(mod.gpa, 0, "unable to build C object: " ++ format, args); |
| 3747 | return mod.failCObjWithOwnedErrorMsg(c_object, err_msg); |
| 3748 | } |
| 3749 | |
| 3750 | fn failCObjWithOwnedErrorMsg(mod: *Module, c_object: *CObject, err_msg: *ErrorMsg) InnerError { |
| 3751 | { |
| 3752 | errdefer err_msg.destroy(mod.gpa); |
| 3753 | try mod.failed_c_objects.ensureCapacity(mod.gpa, mod.failed_c_objects.items().len + 1); |
| 3754 | } |
| 3755 | mod.failed_c_objects.putAssumeCapacityNoClobber(c_object, err_msg); |
| 3756 | c_object.status = .{ .failure = "" }; |
| 3757 | return error.AnalysisFail; |
| 3758 | } |
| 3759 | |
| 3373 | 3760 | pub fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: anytype) InnerError { |
| 3374 | 3761 | @setCold(true); |
| 3375 | 3762 | const err_msg = try ErrorMsg.create(self.gpa, src, format, args); |
| ... | ... | @@ -3560,7 +3947,7 @@ pub fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value { |
| 3560 | 3947 | pub fn floatAdd(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs: Value, rhs: Value) !Value { |
| 3561 | 3948 | var bit_count = switch (float_type.tag()) { |
| 3562 | 3949 | .comptime_float => 128, |
| 3563 | | else => float_type.floatBits(self.target()), |
| 3950 | else => float_type.floatBits(self.getTarget()), |
| 3564 | 3951 | }; |
| 3565 | 3952 | |
| 3566 | 3953 | const allocator = scope.arena(); |
| ... | ... | @@ -3594,7 +3981,7 @@ pub fn floatAdd(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs: |
| 3594 | 3981 | pub fn floatSub(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs: Value, rhs: Value) !Value { |
| 3595 | 3982 | var bit_count = switch (float_type.tag()) { |
| 3596 | 3983 | .comptime_float => 128, |
| 3597 | | else => float_type.floatBits(self.target()), |
| 3984 | else => float_type.floatBits(self.getTarget()), |
| 3598 | 3985 | }; |
| 3599 | 3986 | |
| 3600 | 3987 | const allocator = scope.arena(); |
| ... | ... | @@ -3865,3 +4252,106 @@ pub fn safetyPanic(mod: *Module, block: *Scope.Block, src: usize, panic_id: Pani |
| 3865 | 4252 | _ = try mod.addNoOp(block, src, Type.initTag(.void), .breakpoint); |
| 3866 | 4253 | return mod.addNoOp(block, src, Type.initTag(.noreturn), .unreach); |
| 3867 | 4254 | } |
| 4255 | |
| 4256 | pub const FileExt = enum { |
| 4257 | c, |
| 4258 | cpp, |
| 4259 | h, |
| 4260 | ll, |
| 4261 | bc, |
| 4262 | assembly, |
| 4263 | unknown, |
| 4264 | }; |
| 4265 | |
| 4266 | pub fn hasCExt(filename: []const u8) bool { |
| 4267 | return mem.endsWith(u8, filename, ".c"); |
| 4268 | } |
| 4269 | |
| 4270 | pub fn hasCppExt(filename: []const u8) bool { |
| 4271 | return mem.endsWith(u8, filename, ".C") or |
| 4272 | mem.endsWith(u8, filename, ".cc") or |
| 4273 | mem.endsWith(u8, filename, ".cpp") or |
| 4274 | mem.endsWith(u8, filename, ".cxx"); |
| 4275 | } |
| 4276 | |
| 4277 | pub fn hasAsmExt(filename: []const u8) bool { |
| 4278 | return mem.endsWith(u8, filename, ".s") or mem.endsWith(u8, filename, ".S"); |
| 4279 | } |
| 4280 | |
| 4281 | pub fn classifyFileExt(filename: []const u8) FileExt { |
| 4282 | if (hasCExt(filename)) { |
| 4283 | return .c; |
| 4284 | } else if (hasCppExt(filename)) { |
| 4285 | return .cpp; |
| 4286 | } else if (mem.endsWith(u8, filename, ".ll")) { |
| 4287 | return .ll; |
| 4288 | } else if (mem.endsWith(u8, filename, ".bc")) { |
| 4289 | return .bc; |
| 4290 | } else if (hasAsmExt(filename)) { |
| 4291 | return .assembly; |
| 4292 | } else if (mem.endsWith(u8, filename, ".h")) { |
| 4293 | return .h; |
| 4294 | } else { |
| 4295 | // TODO look for .so, .so.X, .so.X.Y, .so.X.Y.Z |
| 4296 | return .unknown; |
| 4297 | } |
| 4298 | } |
| 4299 | |
| 4300 | fn haveFramePointer(mod: *Module) bool { |
| 4301 | return switch (mod.bin_file.options.optimize_mode) { |
| 4302 | .Debug, .ReleaseSafe => !mod.bin_file.options.strip, |
| 4303 | .ReleaseSmall, .ReleaseFast => false, |
| 4304 | }; |
| 4305 | } |
| 4306 | |
| 4307 | fn detectLibCIncludeDirs( |
| 4308 | arena: *Allocator, |
| 4309 | zig_lib_dir: []const u8, |
| 4310 | target: Target, |
| 4311 | link_libc: bool, |
| 4312 | ) ![]const []const u8 { |
| 4313 | if (!link_libc) return &[0][]u8{}; |
| 4314 | |
| 4315 | // TODO Support --libc file explicitly providing libc paths. Or not? Maybe we are better off |
| 4316 | // deleting that feature. |
| 4317 | |
| 4318 | if (target_util.canBuildLibC(target)) { |
| 4319 | const generic_name = target_util.libCGenericName(target); |
| 4320 | // Some architectures are handled by the same set of headers. |
| 4321 | const arch_name = if (target.abi.isMusl()) target_util.archMuslName(target.cpu.arch) else @tagName(target.cpu.arch); |
| 4322 | const os_name = @tagName(target.os.tag); |
| 4323 | // Musl's headers are ABI-agnostic and so they all have the "musl" ABI name. |
| 4324 | const abi_name = if (target.abi.isMusl()) "musl" else @tagName(target.abi); |
| 4325 | const s = std.fs.path.sep_str; |
| 4326 | const arch_include_dir = try std.fmt.allocPrint( |
| 4327 | arena, |
| 4328 | "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-{}-{}", |
| 4329 | .{ zig_lib_dir, arch_name, os_name, abi_name }, |
| 4330 | ); |
| 4331 | const generic_include_dir = try std.fmt.allocPrint( |
| 4332 | arena, |
| 4333 | "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "generic-{}", |
| 4334 | .{ zig_lib_dir, generic_name }, |
| 4335 | ); |
| 4336 | const arch_os_include_dir = try std.fmt.allocPrint( |
| 4337 | arena, |
| 4338 | "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-{}-any", |
| 4339 | .{ zig_lib_dir, @tagName(target.cpu.arch), os_name }, |
| 4340 | ); |
| 4341 | const generic_os_include_dir = try std.fmt.allocPrint( |
| 4342 | arena, |
| 4343 | "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-{}-any", |
| 4344 | .{ zig_lib_dir, os_name }, |
| 4345 | ); |
| 4346 | |
| 4347 | const list = try arena.alloc([]const u8, 4); |
| 4348 | list[0] = arch_include_dir; |
| 4349 | list[1] = generic_include_dir; |
| 4350 | list[2] = arch_os_include_dir; |
| 4351 | list[3] = generic_os_include_dir; |
| 4352 | return list; |
| 4353 | } |
| 4354 | |
| 4355 | // TODO finish porting detect_libc from codegen.cpp |
| 4356 | return error.LibCDetectionUnimplemented; |
| 4357 | } |