authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-08-30 13:02:43-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-08-30 13:02:43-07:00
log1e21876de25f5725dd093f2243bcfdb3f6c6c44e
tree1482b1d8b7cb025006c02b9705295d3e50cf44e1
parent1cb8065a52c9bf74f913c46a011ab0118c92f0fa
parentf559ea95b1c37fd6ede8fff6ffb2d74d5c2abc4e

Merge remote-tracking branch 'origin/master' into llvm15


58 files changed, 2924 insertions(+), 1635 deletions(-)

CMakeLists.txt+3
......@@ -753,6 +753,9 @@ set(ZIG_STAGE2_SOURCES
753753 "${CMAKE_SOURCE_DIR}/src/link.zig"
754754 "${CMAKE_SOURCE_DIR}/src/link/C.zig"
755755 "${CMAKE_SOURCE_DIR}/src/link/Coff.zig"
756 "${CMAKE_SOURCE_DIR}/src/link/Coff/Atom.zig"
757 "${CMAKE_SOURCE_DIR}/src/link/Coff/Object.zig"
758 "${CMAKE_SOURCE_DIR}/src/link/Coff/lld.zig"
756759 "${CMAKE_SOURCE_DIR}/src/link/Elf.zig"
757760 "${CMAKE_SOURCE_DIR}/src/link/MachO.zig"
758761 "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig"
ci/azure/macos_script+1
......@@ -49,6 +49,7 @@ unset CXX
4949make $JOBS install
5050
5151stage3-release/bin/zig build test docs \
52 --zig-lib-dir "$(pwd)/../lib" \
5253 -Denable-macos-sdk \
5354 -Dstatic-llvm \
5455 --search-prefix "$PREFIX"
ci/drone/test_linux_behavior+5-4
......@@ -7,7 +7,8 @@ INSTALL_PREFIX="$DRONE_WORKSPACE/stage3-release"
77ZIG="$INSTALL_PREFIX/bin/zig"
88export ZIG_GLOBAL_CACHE_DIR="$DRONE_WORKSPACE/zig-cache"
99
10$ZIG build test-behavior -Dskip-non-native
11$ZIG build test-compiler-rt -Dskip-non-native
12$ZIG build test-fmt
13$ZIG build docs
10# https://github.com/ziglang/zig/issues/12689
11# $ZIG build test-behavior -Dskip-non-native --zig-lib-dir lib
12$ZIG build test-compiler-rt -Dskip-non-native --zig-lib-dir lib
13$ZIG build test-fmt --zig-lib-dir lib
14$ZIG build docs --zig-lib-dir lib
ci/drone/test_linux_cases+3-2
......@@ -7,5 +7,6 @@ INSTALL_PREFIX="$DRONE_WORKSPACE/stage3-release"
77ZIG="$INSTALL_PREFIX/bin/zig"
88export ZIG_GLOBAL_CACHE_DIR="$DRONE_WORKSPACE/zig-cache"
99
10$ZIG build -Dskip-non-native # test building self-hosted without LLVM
11$ZIG build -Dskip-non-native test-cases
10# test building self-hosted without LLVM
11$ZIG build -Dskip-non-native --zig-lib-dir lib
12$ZIG build test-cases -Dskip-non-native --zig-lib-dir lib
ci/drone/test_linux_misc+8-7
......@@ -7,10 +7,11 @@ INSTALL_PREFIX="$DRONE_WORKSPACE/stage3-release"
77ZIG="$INSTALL_PREFIX/bin/zig"
88export ZIG_GLOBAL_CACHE_DIR="$DRONE_WORKSPACE/zig-cache"
99
10$ZIG build test-universal-libc -Dskip-non-native
11$ZIG build test-compare-output -Dskip-non-native
12$ZIG build test-standalone -Dskip-non-native -Dskip-release-safe
13$ZIG build test-stack-traces -Dskip-non-native
14$ZIG build test-cli -Dskip-non-native
15$ZIG build test-asm-link -Dskip-non-native
16$ZIG build test-translate-c -Dskip-non-native
10$ZIG build test-universal-libc -Dskip-non-native --zig-lib-dir lib
11# https://github.com/ziglang/zig/issues/12689
12# $ZIG build test-compare-output -Dskip-non-native --zig-lib-dir lib
13$ZIG build test-standalone -Dskip-non-native --zig-lib-dir lib -Dskip-release-safe
14$ZIG build test-stack-traces -Dskip-non-native --zig-lib-dir lib
15$ZIG build test-cli -Dskip-non-native --zig-lib-dir lib
16$ZIG build test-asm-link -Dskip-non-native --zig-lib-dir lib
17$ZIG build test-translate-c -Dskip-non-native --zig-lib-dir lib
ci/drone/test_linux_std_Debug+6-1
......@@ -7,4 +7,9 @@ INSTALL_PREFIX="$DRONE_WORKSPACE/stage3-release"
77ZIG="$INSTALL_PREFIX/bin/zig"
88export ZIG_GLOBAL_CACHE_DIR="$DRONE_WORKSPACE/zig-cache"
99
10$ZIG build test-std -Dskip-release-safe -Dskip-release-fast -Dskip-release-small -Dskip-non-native
10$ZIG build test-std \
11 --zig-lib-dir lib \
12 -Dskip-release-safe \
13 -Dskip-release-fast \
14 -Dskip-release-small \
15 -Dskip-non-native
ci/drone/test_linux_std_ReleaseFast+7-1
......@@ -7,4 +7,10 @@ INSTALL_PREFIX="$DRONE_WORKSPACE/stage3-release"
77ZIG="$INSTALL_PREFIX/bin/zig"
88export ZIG_GLOBAL_CACHE_DIR="$DRONE_WORKSPACE/zig-cache"
99
10$ZIG build test-std -Dskip-debug -Dskip-release-safe -Dskip-release-small -Dskip-non-native -Dskip-single-threaded
10$ZIG build test-std \
11 --zig-lib-dir lib \
12 -Dskip-debug \
13 -Dskip-release-safe \
14 -Dskip-release-small \
15 -Dskip-non-native \
16 -Dskip-single-threaded
ci/drone/test_linux_std_ReleaseSafe+7-1
......@@ -7,4 +7,10 @@ INSTALL_PREFIX="$DRONE_WORKSPACE/stage3-release"
77ZIG="$INSTALL_PREFIX/bin/zig"
88export ZIG_GLOBAL_CACHE_DIR="$DRONE_WORKSPACE/zig-cache"
99
10$ZIG build test-std -Dskip-debug -Dskip-release-fast -Dskip-release-small -Dskip-non-native -Dskip-single-threaded
10$ZIG build test-std \
11 --zig-lib-dir lib \
12 -Dskip-debug \
13 -Dskip-release-fast \
14 -Dskip-release-small \
15 -Dskip-non-native \
16 -Dskip-single-threaded
ci/drone/test_linux_std_ReleaseSmall+2-2
......@@ -12,5 +12,5 @@ export ZIG_GLOBAL_CACHE_DIR="$DRONE_WORKSPACE/zig-cache"
1212# of ReleaseSmall std lib tests.
1313# $ZIG build test-std -Dskip-debug -Dskip-release-safe -Dskip-release-fast -Dskip-non-native
1414
15$ZIG test lib/std/std.zig -OReleaseSmall
16$ZIG test lib/std/std.zig -OReleaseSmall -lc
15$ZIG test lib/std/std.zig -OReleaseSmall --zig-lib-dir lib
16$ZIG test lib/std/std.zig -OReleaseSmall -lc --zig-lib-dir lib
ci/srht/freebsd_script+1
......@@ -56,6 +56,7 @@ ZIG_LIBC="$ZIG_LIBC_TXT" samu install
5656
5757# Here we skip some tests to save time.
5858stage3/bin/zig build test docs \
59 --zig-lib-dir "$(pwd)/../lib" \
5960 -Dstatic-llvm \
6061 --search-prefix "$PREFIX" \
6162 -Dskip-stage1 \
ci/zinc/build_aarch64_macos+1-1
......@@ -16,5 +16,5 @@ SEARCH_PREFIX="/deps/$TARGET"
1616 -Drelease \
1717 -Dstrip \
1818 -Dtarget="$TARGET" \
19 -Dmcpu="$MCPU" \
19 -Dcpu="$MCPU" \
2020 -Denable-stage1
ci/zinc/linux_test_stage3_debug+2-1
......@@ -55,7 +55,8 @@ stage3/bin/zig build test \
5555 -fwasmtime \
5656 -Dstatic-llvm \
5757 -Dtarget=native-native-musl \
58 --search-prefix "$DEPS_LOCAL"
58 --search-prefix "$DEPS_LOCAL" \
59 --zig-lib-dir "$(pwd)/../lib"
5960
6061# Explicit exit helps show last command duration.
6162exit
ci/zinc/linux_test_stage3_release+4-2
......@@ -40,13 +40,15 @@ ninja install
4040 -fwasmtime \
4141 -Dstatic-llvm \
4242 -Dtarget=native-native-musl \
43 --search-prefix "$DEPS_LOCAL"
43 --search-prefix "$DEPS_LOCAL" \
44 --zig-lib-dir "$(pwd)/../lib"
4445
4546# Produce the experimental std lib documentation.
4647mkdir -p "$RELEASE_STAGING/docs/std"
4748"$RELEASE_STAGING/bin/zig" test ../lib/std/std.zig \
4849 -femit-docs=$RELEASE_STAGING/docs/std \
49 -fno-emit-bin
50 -fno-emit-bin \
51 --zig-lib-dir "$(pwd)/../lib"
5052
5153cp ../LICENSE $RELEASE_STAGING/
5254cp ../zig-cache/langref.html $RELEASE_STAGING/docs/
lib/std/coff.zig+212-123
......@@ -256,20 +256,89 @@ pub const OptionalHeaderPE64 = extern struct {
256256 number_of_rva_and_sizes: u32,
257257};
258258
259pub const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;
260
261pub const DirectoryEntry = enum(u16) {
262 /// Export Directory
263 EXPORT = 0,
264
265 /// Import Directory
266 IMPORT = 1,
267
268 /// Resource Directory
269 RESOURCE = 2,
270
271 /// Exception Directory
272 EXCEPTION = 3,
273
274 /// Security Directory
275 SECURITY = 4,
276
277 /// Base Relocation Table
278 BASERELOC = 5,
279
280 /// Debug Directory
281 DEBUG = 6,
282
283 /// Architecture Specific Data
284 ARCHITECTURE = 7,
285
286 /// RVA of GP
287 GLOBALPTR = 8,
288
289 /// TLS Directory
290 TLS = 9,
291
292 /// Load Configuration Directory
293 LOAD_CONFIG = 10,
294
295 /// Bound Import Directory in headers
296 BOUND_IMPORT = 11,
297
298 /// Import Address Table
299 IAT = 12,
300
301 /// Delay Load Import Descriptors
302 DELAY_IMPORT = 13,
303
304 /// COM Runtime descriptor
305 COM_DESCRIPTOR = 14,
306};
307
308pub const ImageDataDirectory = extern struct {
309 virtual_address: u32,
310 size: u32,
311};
312
259313pub const DebugDirectoryEntry = extern struct {
260 characteristiccs: u32,
314 characteristics: u32,
261315 time_date_stamp: u32,
262316 major_version: u16,
263317 minor_version: u16,
264 @"type": u32,
318 @"type": DebugType,
265319 size_of_data: u32,
266320 address_of_raw_data: u32,
267321 pointer_to_raw_data: u32,
268322};
269323
270pub const ImageDataDirectory = extern struct {
271 virtual_address: u32,
272 size: u32,
324pub const DebugType = enum(u32) {
325 UNKNOWN = 0,
326 COFF = 1,
327 CODEVIEW = 2,
328 FPO = 3,
329 MISC = 4,
330 EXCEPTION = 5,
331 FIXUP = 6,
332 OMAP_TO_SRC = 7,
333 OMAP_FROM_SRC = 8,
334 BORLAND = 9,
335 RESERVED10 = 10,
336 VC_FEATURE = 12,
337 POGO = 13,
338 ILTCG = 14,
339 MPX = 15,
340 REPRO = 16,
341 EX_DLLCHARACTERISTICS = 20,
273342};
274343
275344pub const SectionHeader = extern struct {
......@@ -303,6 +372,15 @@ pub const SectionHeader = extern struct {
303372 return std.math.powi(u16, 2, self.flags.ALIGN - 1) catch unreachable;
304373 }
305374
375 pub fn setAlignment(self: *SectionHeader, new_alignment: u16) void {
376 assert(new_alignment > 0 and new_alignment <= 8192);
377 self.flags.ALIGN = std.math.log2(new_alignment);
378 }
379
380 pub fn isCode(self: SectionHeader) bool {
381 return self.flags.CNT_CODE == 0b1;
382 }
383
306384 pub fn isComdat(self: SectionHeader) bool {
307385 return self.flags.LNK_COMDAT == 0b1;
308386 }
......@@ -778,6 +856,21 @@ pub const MachineType = enum(u16) {
778856 /// MIPS little-endian WCE v2
779857 WCEMIPSV2 = 0x169,
780858
859 pub fn fromTargetCpuArch(arch: std.Target.Cpu.Arch) MachineType {
860 return switch (arch) {
861 .arm => .ARM,
862 .powerpc => .POWERPC,
863 .riscv32 => .RISCV32,
864 .thumb => .Thumb,
865 .i386 => .I386,
866 .aarch64 => .ARM64,
867 .riscv64 => .RISCV64,
868 .x86_64 => .X64,
869 // there's cases we don't (yet) handle
870 else => unreachable,
871 };
872 }
873
781874 pub fn toTargetCpuArch(machine_type: MachineType) ?std.Target.Cpu.Arch {
782875 return switch (machine_type) {
783876 .ARM => .arm,
......@@ -794,10 +887,6 @@ pub const MachineType = enum(u16) {
794887 }
795888};
796889
797const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;
798const IMAGE_DEBUG_TYPE_CODEVIEW = 2;
799const DEBUG_DIRECTORY = 6;
800
801890pub const CoffError = error{
802891 InvalidPEMagic,
803892 InvalidPEHeader,
......@@ -831,7 +920,7 @@ pub const Coff = struct {
831920 var stream = std.io.fixedBufferStream(self.data);
832921 const reader = stream.reader();
833922 try stream.seekTo(pe_pointer_offset);
834 const coff_header_offset = try reader.readByte();
923 const coff_header_offset = try reader.readIntLittle(u32);
835924 try stream.seekTo(coff_header_offset);
836925 var buf: [4]u8 = undefined;
837926 try reader.readNoEof(&buf);
......@@ -862,7 +951,7 @@ pub const Coff = struct {
862951 };
863952
864953 const data_dirs = self.getDataDirectories();
865 const debug_dir = data_dirs[DEBUG_DIRECTORY];
954 const debug_dir = data_dirs[@enumToInt(DirectoryEntry.DEBUG)];
866955 const file_offset = debug_dir.virtual_address - header.virtual_address + header.pointer_to_raw_data;
867956
868957 var stream = std.io.fixedBufferStream(self.data);
......@@ -875,7 +964,7 @@ pub const Coff = struct {
875964 var i: u32 = 0;
876965 blk: while (i < debug_dir_entry_count) : (i += 1) {
877966 const debug_dir_entry = try reader.readStruct(DebugDirectoryEntry);
878 if (debug_dir_entry.type == IMAGE_DEBUG_TYPE_CODEVIEW) {
967 if (debug_dir_entry.type == .CODEVIEW) {
879968 for (self.getSectionHeaders()) |*section| {
880969 const section_start = section.virtual_address;
881970 const section_size = section.virtual_size;
......@@ -1011,132 +1100,132 @@ pub const Coff = struct {
10111100 mem.copy(u8, out_buff, self.data[sec.pointer_to_raw_data..][0..sec.virtual_size]);
10121101 return out_buff;
10131102 }
1103};
10141104
1015 pub const Symtab = struct {
1016 buffer: []const u8,
1017
1018 fn len(self: Symtab) usize {
1019 return @divExact(self.buffer.len, Symbol.sizeOf());
1020 }
1021
1022 const Tag = enum {
1023 symbol,
1024 func_def,
1025 debug_info,
1026 weak_ext,
1027 file_def,
1028 sect_def,
1029 };
1105pub const Symtab = struct {
1106 buffer: []const u8,
10301107
1031 const Record = union(Tag) {
1032 symbol: Symbol,
1033 debug_info: DebugInfoDefinition,
1034 func_def: FunctionDefinition,
1035 weak_ext: WeakExternalDefinition,
1036 file_def: FileDefinition,
1037 sect_def: SectionDefinition,
1038 };
1108 pub fn len(self: Symtab) usize {
1109 return @divExact(self.buffer.len, Symbol.sizeOf());
1110 }
10391111
1040 /// Lives as long as Symtab instance.
1041 fn at(self: Symtab, index: usize, tag: Tag) Record {
1042 const offset = index * Symbol.sizeOf();
1043 const raw = self.buffer[offset..][0..Symbol.sizeOf()];
1044 return switch (tag) {
1045 .symbol => .{ .symbol = asSymbol(raw) },
1046 .debug_info => .{ .debug_info = asDebugInfo(raw) },
1047 .func_def => .{ .func_def = asFuncDef(raw) },
1048 .weak_ext => .{ .weak_ext = asWeakExtDef(raw) },
1049 .file_def => .{ .file_def = asFileDef(raw) },
1050 .sect_def => .{ .sect_def = asSectDef(raw) },
1051 };
1052 }
1112 pub const Tag = enum {
1113 symbol,
1114 func_def,
1115 debug_info,
1116 weak_ext,
1117 file_def,
1118 sect_def,
1119 };
10531120
1054 fn asSymbol(raw: []const u8) Symbol {
1055 return .{
1056 .name = raw[0..8].*,
1057 .value = mem.readIntLittle(u32, raw[8..12]),
1058 .section_number = @intToEnum(SectionNumber, mem.readIntLittle(u16, raw[12..14])),
1059 .@"type" = @bitCast(SymType, mem.readIntLittle(u16, raw[14..16])),
1060 .storage_class = @intToEnum(StorageClass, raw[16]),
1061 .number_of_aux_symbols = raw[17],
1062 };
1063 }
1121 pub const Record = union(Tag) {
1122 symbol: Symbol,
1123 debug_info: DebugInfoDefinition,
1124 func_def: FunctionDefinition,
1125 weak_ext: WeakExternalDefinition,
1126 file_def: FileDefinition,
1127 sect_def: SectionDefinition,
1128 };
10641129
1065 fn asDebugInfo(raw: []const u8) DebugInfoDefinition {
1066 return .{
1067 .unused_1 = raw[0..4].*,
1068 .linenumber = mem.readIntLittle(u16, raw[4..6]),
1069 .unused_2 = raw[6..12].*,
1070 .pointer_to_next_function = mem.readIntLittle(u32, raw[12..16]),
1071 .unused_3 = raw[16..18].*,
1072 };
1073 }
1130 /// Lives as long as Symtab instance.
1131 pub fn at(self: Symtab, index: usize, tag: Tag) Record {
1132 const offset = index * Symbol.sizeOf();
1133 const raw = self.buffer[offset..][0..Symbol.sizeOf()];
1134 return switch (tag) {
1135 .symbol => .{ .symbol = asSymbol(raw) },
1136 .debug_info => .{ .debug_info = asDebugInfo(raw) },
1137 .func_def => .{ .func_def = asFuncDef(raw) },
1138 .weak_ext => .{ .weak_ext = asWeakExtDef(raw) },
1139 .file_def => .{ .file_def = asFileDef(raw) },
1140 .sect_def => .{ .sect_def = asSectDef(raw) },
1141 };
1142 }
10741143
1075 fn asFuncDef(raw: []const u8) FunctionDefinition {
1076 return .{
1077 .tag_index = mem.readIntLittle(u32, raw[0..4]),
1078 .total_size = mem.readIntLittle(u32, raw[4..8]),
1079 .pointer_to_linenumber = mem.readIntLittle(u32, raw[8..12]),
1080 .pointer_to_next_function = mem.readIntLittle(u32, raw[12..16]),
1081 .unused = raw[16..18].*,
1082 };
1083 }
1144 fn asSymbol(raw: []const u8) Symbol {
1145 return .{
1146 .name = raw[0..8].*,
1147 .value = mem.readIntLittle(u32, raw[8..12]),
1148 .section_number = @intToEnum(SectionNumber, mem.readIntLittle(u16, raw[12..14])),
1149 .@"type" = @bitCast(SymType, mem.readIntLittle(u16, raw[14..16])),
1150 .storage_class = @intToEnum(StorageClass, raw[16]),
1151 .number_of_aux_symbols = raw[17],
1152 };
1153 }
10841154
1085 fn asWeakExtDef(raw: []const u8) WeakExternalDefinition {
1086 return .{
1087 .tag_index = mem.readIntLittle(u32, raw[0..4]),
1088 .flag = @intToEnum(WeakExternalFlag, mem.readIntLittle(u32, raw[4..8])),
1089 .unused = raw[8..18].*,
1090 };
1091 }
1155 fn asDebugInfo(raw: []const u8) DebugInfoDefinition {
1156 return .{
1157 .unused_1 = raw[0..4].*,
1158 .linenumber = mem.readIntLittle(u16, raw[4..6]),
1159 .unused_2 = raw[6..12].*,
1160 .pointer_to_next_function = mem.readIntLittle(u32, raw[12..16]),
1161 .unused_3 = raw[16..18].*,
1162 };
1163 }
10921164
1093 fn asFileDef(raw: []const u8) FileDefinition {
1094 return .{
1095 .file_name = raw[0..18].*,
1096 };
1097 }
1165 fn asFuncDef(raw: []const u8) FunctionDefinition {
1166 return .{
1167 .tag_index = mem.readIntLittle(u32, raw[0..4]),
1168 .total_size = mem.readIntLittle(u32, raw[4..8]),
1169 .pointer_to_linenumber = mem.readIntLittle(u32, raw[8..12]),
1170 .pointer_to_next_function = mem.readIntLittle(u32, raw[12..16]),
1171 .unused = raw[16..18].*,
1172 };
1173 }
10981174
1099 fn asSectDef(raw: []const u8) SectionDefinition {
1100 return .{
1101 .length = mem.readIntLittle(u32, raw[0..4]),
1102 .number_of_relocations = mem.readIntLittle(u16, raw[4..6]),
1103 .number_of_linenumbers = mem.readIntLittle(u16, raw[6..8]),
1104 .checksum = mem.readIntLittle(u32, raw[8..12]),
1105 .number = mem.readIntLittle(u16, raw[12..14]),
1106 .selection = @intToEnum(ComdatSelection, raw[14]),
1107 .unused = raw[15..18].*,
1108 };
1109 }
1175 fn asWeakExtDef(raw: []const u8) WeakExternalDefinition {
1176 return .{
1177 .tag_index = mem.readIntLittle(u32, raw[0..4]),
1178 .flag = @intToEnum(WeakExternalFlag, mem.readIntLittle(u32, raw[4..8])),
1179 .unused = raw[8..18].*,
1180 };
1181 }
11101182
1111 const Slice = struct {
1112 buffer: []const u8,
1113 num: usize,
1114 count: usize = 0,
1115
1116 /// Lives as long as Symtab instance.
1117 fn next(self: *Slice) ?Symbol {
1118 if (self.count >= self.num) return null;
1119 const sym = asSymbol(self.buffer[0..Symbol.sizeOf()]);
1120 self.count += 1;
1121 self.buffer = self.buffer[Symbol.sizeOf()..];
1122 return sym;
1123 }
1183 fn asFileDef(raw: []const u8) FileDefinition {
1184 return .{
1185 .file_name = raw[0..18].*,
11241186 };
1187 }
11251188
1126 fn slice(self: Symtab, start: usize, end: ?usize) Slice {
1127 const offset = start * Symbol.sizeOf();
1128 const llen = if (end) |e| e * Symbol.sizeOf() else self.buffer.len;
1129 const num = @divExact(llen - offset, Symbol.sizeOf());
1130 return Slice{ .buffer = self.buffer[offset..][0..llen], .num = num };
1131 }
1132 };
1189 fn asSectDef(raw: []const u8) SectionDefinition {
1190 return .{
1191 .length = mem.readIntLittle(u32, raw[0..4]),
1192 .number_of_relocations = mem.readIntLittle(u16, raw[4..6]),
1193 .number_of_linenumbers = mem.readIntLittle(u16, raw[6..8]),
1194 .checksum = mem.readIntLittle(u32, raw[8..12]),
1195 .number = mem.readIntLittle(u16, raw[12..14]),
1196 .selection = @intToEnum(ComdatSelection, raw[14]),
1197 .unused = raw[15..18].*,
1198 };
1199 }
11331200
1134 pub const Strtab = struct {
1201 pub const Slice = struct {
11351202 buffer: []const u8,
1203 num: usize,
1204 count: usize = 0,
11361205
1137 fn get(self: Strtab, off: u32) []const u8 {
1138 assert(off < self.buffer.len);
1139 return mem.sliceTo(@ptrCast([*:0]const u8, self.buffer.ptr + off), 0);
1206 /// Lives as long as Symtab instance.
1207 pub fn next(self: *Slice) ?Symbol {
1208 if (self.count >= self.num) return null;
1209 const sym = asSymbol(self.buffer[0..Symbol.sizeOf()]);
1210 self.count += 1;
1211 self.buffer = self.buffer[Symbol.sizeOf()..];
1212 return sym;
11401213 }
11411214 };
1215
1216 pub fn slice(self: Symtab, start: usize, end: ?usize) Slice {
1217 const offset = start * Symbol.sizeOf();
1218 const llen = if (end) |e| e * Symbol.sizeOf() else self.buffer.len;
1219 const num = @divExact(llen - offset, Symbol.sizeOf());
1220 return Slice{ .buffer = self.buffer[offset..][0..llen], .num = num };
1221 }
1222};
1223
1224pub const Strtab = struct {
1225 buffer: []const u8,
1226
1227 pub fn get(self: Strtab, off: u32) []const u8 {
1228 assert(off < self.buffer.len);
1229 return mem.sliceTo(@ptrCast([*:0]const u8, self.buffer.ptr + off), 0);
1230 }
11421231};
lib/std/rand.zig+1-1
......@@ -343,7 +343,7 @@ pub const Random = struct {
343343 ///
344344 /// This is useful for selecting an item from a slice where weights are not equal.
345345 /// `T` must be a numeric type capable of holding the sum of `proportions`.
346 pub fn weightedIndex(r: std.rand.Random, comptime T: type, proportions: []T) usize {
346 pub fn weightedIndex(r: std.rand.Random, comptime T: type, proportions: []const T) usize {
347347 // This implementation works by summing the proportions and picking a random
348348 // point in [0, sum). We then loop over the proportions, accumulating
349349 // until our accumulator is greater than the random point.
lib/std/rand/test.zig+1-1
......@@ -452,7 +452,7 @@ test "Random weightedIndex" {
452452 var prng = DefaultPrng.init(0);
453453 const random = prng.random();
454454
455 var proportions = [_]T{ 2, 1, 1, 2 };
455 const proportions = [_]T{ 2, 1, 1, 2 };
456456 var counts = [_]f64{ 0, 0, 0, 0 };
457457
458458 const n_trials: u64 = 10_000;
lib/std/start.zig-2
......@@ -36,8 +36,6 @@ comptime {
3636 if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) {
3737 @export(main2, .{ .name = "main" });
3838 }
39 } else if (builtin.os.tag == .windows) {
40 @export(wWinMainCRTStartup2, .{ .name = "wWinMainCRTStartup" });
4139 } else if (builtin.os.tag == .wasi and @hasDecl(root, "main")) {
4240 @export(wasiMain2, .{ .name = "_start" });
4341 } else {
src/AstGen.zig+22-11
......@@ -226,6 +226,8 @@ pub const ResultLoc = union(enum) {
226226 ref,
227227 /// The expression will be coerced into this type, but it will be evaluated as an rvalue.
228228 ty: Zir.Inst.Ref,
229 /// Same as `ty` but for shift operands.
230 ty_shift_operand: Zir.Inst.Ref,
229231 /// Same as `ty` but it is guaranteed that Sema will additionally perform the coercion,
230232 /// so no `as` instruction needs to be emitted.
231233 coerced_ty: Zir.Inst.Ref,
......@@ -259,7 +261,7 @@ pub const ResultLoc = union(enum) {
259261 fn strategy(rl: ResultLoc, block_scope: *GenZir) Strategy {
260262 switch (rl) {
261263 // In this branch there will not be any store_to_block_ptr instructions.
262 .none, .ty, .coerced_ty, .ref => return .{
264 .none, .ty, .ty_shift_operand, .coerced_ty, .ref => return .{
263265 .tag = .break_operand,
264266 .elide_store_to_block_ptr_instructions = false,
265267 },
......@@ -302,6 +304,14 @@ pub const ResultLoc = union(enum) {
302304 else => rl,
303305 };
304306 }
307
308 fn zirTag(rl: ResultLoc) Zir.Inst.Tag {
309 return switch (rl) {
310 .ty => .as_node,
311 .ty_shift_operand => .as_shift_operand,
312 else => unreachable,
313 };
314 }
305315};
306316
307317pub const align_rl: ResultLoc = .{ .ty = .u29_type };
......@@ -1385,7 +1395,7 @@ fn arrayInitExpr(
13851395 const tag: Zir.Inst.Tag = if (types.array != .none) .array_init else .array_init_anon;
13861396 return arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag);
13871397 },
1388 .ty, .coerced_ty => {
1398 .ty, .ty_shift_operand, .coerced_ty => {
13891399 const tag: Zir.Inst.Tag = if (types.array != .none) .array_init else .array_init_anon;
13901400 const result = try arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag);
13911401 return rvalue(gz, rl, result, node);
......@@ -1631,7 +1641,7 @@ fn structInitExpr(
16311641 return structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon);
16321642 }
16331643 },
1634 .ty, .coerced_ty => |ty_inst| {
1644 .ty, .ty_shift_operand, .coerced_ty => |ty_inst| {
16351645 if (struct_init.ast.type_expr == 0) {
16361646 const result = try structInitExprRlNone(gz, scope, node, struct_init, ty_inst, .struct_init_anon);
16371647 return rvalue(gz, rl, result, node);
......@@ -2327,6 +2337,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
23272337 .anyframe_type,
23282338 .as,
23292339 .as_node,
2340 .as_shift_operand,
23302341 .bit_and,
23312342 .bitcast,
23322343 .bit_or,
......@@ -2497,7 +2508,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
24972508 .field_parent_ptr,
24982509 .maximum,
24992510 .minimum,
2500 .builtin_async_call,
25012511 .c_import,
25022512 .@"resume",
25032513 .@"await",
......@@ -7278,7 +7288,7 @@ fn as(
72787288) InnerError!Zir.Inst.Ref {
72797289 const dest_type = try typeExpr(gz, scope, lhs);
72807290 switch (rl) {
7281 .none, .discard, .ref, .ty, .coerced_ty => {
7291 .none, .discard, .ref, .ty, .ty_shift_operand, .coerced_ty => {
72827292 const result = try reachableExpr(gz, scope, .{ .ty = dest_type }, rhs, node);
72837293 return rvalue(gz, rl, result, node);
72847294 },
......@@ -7959,7 +7969,8 @@ fn builtinCall(
79597969 return rvalue(gz, rl, result, node);
79607970 },
79617971 .async_call => {
7962 const result = try gz.addPlNode(.builtin_async_call, node, Zir.Inst.AsyncCall{
7972 const result = try gz.addExtendedPayload(.builtin_async_call, Zir.Inst.AsyncCall{
7973 .node = gz.nodeIndexToRelative(node),
79637974 .frame_buffer = try expr(gz, scope, .none, params[0]),
79647975 .result_ptr = try expr(gz, scope, .none, params[1]),
79657976 .fn_ptr = try expr(gz, scope, .none, params[2]),
......@@ -8178,7 +8189,7 @@ fn shiftOp(
81788189) InnerError!Zir.Inst.Ref {
81798190 const lhs = try expr(gz, scope, .none, lhs_node);
81808191 const log2_int_type = try gz.addUnNode(.typeof_log2_int_type, lhs, lhs_node);
8181 const rhs = try expr(gz, scope, .{ .ty = log2_int_type }, rhs_node);
8192 const rhs = try expr(gz, scope, .{ .ty_shift_operand = log2_int_type }, rhs_node);
81828193 const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
81838194 .lhs = lhs,
81848195 .rhs = rhs,
......@@ -9409,7 +9420,7 @@ fn rvalue(
94099420 }
94109421 return indexToRef(gop.value_ptr.*);
94119422 },
9412 .ty => |ty_inst| {
9423 .ty, .ty_shift_operand => |ty_inst| {
94139424 // Quickly eliminate some common, unnecessary type coercion.
94149425 const as_ty = @as(u64, @enumToInt(Zir.Inst.Ref.type_type)) << 32;
94159426 const as_comptime_int = @as(u64, @enumToInt(Zir.Inst.Ref.comptime_int_type)) << 32;
......@@ -9470,7 +9481,7 @@ fn rvalue(
94709481 => return result, // type of result is already correct
94719482
94729483 // Need an explicit type coercion instruction.
9473 else => return gz.addPlNode(.as_node, src_node, Zir.Inst.As{
9484 else => return gz.addPlNode(rl.zirTag(), src_node, Zir.Inst.As{
94749485 .dest_type = ty_inst,
94759486 .operand = result,
94769487 }),
......@@ -10350,7 +10361,7 @@ const GenZir = struct {
1035010361 // we emit ZIR for the block break instructions to have the result values,
1035110362 // and then rvalue() on that to pass the value to the result location.
1035210363 switch (parent_rl) {
10353 .ty, .coerced_ty => |ty_inst| {
10364 .ty, .ty_shift_operand, .coerced_ty => |ty_inst| {
1035410365 gz.rl_ty_inst = ty_inst;
1035510366 gz.break_result_loc = parent_rl;
1035610367 },
......@@ -11506,7 +11517,7 @@ const GenZir = struct {
1150611517 fn addRet(gz: *GenZir, rl: ResultLoc, operand: Zir.Inst.Ref, node: Ast.Node.Index) !void {
1150711518 switch (rl) {
1150811519 .ptr => |ret_ptr| _ = try gz.addUnNode(.ret_load, ret_ptr, node),
11509 .ty => _ = try gz.addUnNode(.ret_node, operand, node),
11520 .ty, .ty_shift_operand => _ = try gz.addUnNode(.ret_node, operand, node),
1151011521 else => unreachable,
1151111522 }
1151211523 }
src/Autodoc.zig+1-1
......@@ -1888,7 +1888,7 @@ fn walkInstruction(
18881888 .expr = .{ .typeInfo = operand_index },
18891889 };
18901890 },
1891 .as_node => {
1891 .as_node, .as_shift_operand => {
18921892 const pl_node = data[inst_index].pl_node;
18931893 const extra = file.zir.extraData(Zir.Inst.As, pl_node.payload_index);
18941894 const dest_type_walk = try self.walkRef(
src/Compilation.zig+19-18
......@@ -1127,7 +1127,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
11271127 link_eh_frame_hdr or
11281128 options.link_emit_relocs or
11291129 options.output_mode == .Lib or
1130 options.image_base_override != null or
11311130 options.linker_script != null or options.version_script != null or
11321131 options.emit_implib != null or
11331132 build_id)
......@@ -4767,6 +4766,24 @@ pub fn dump_argv(argv: []const []const u8) void {
47674766 std.debug.print("{s}\n", .{argv[argv.len - 1]});
47684767}
47694768
4769pub fn getZigBackend(comp: Compilation) std.builtin.CompilerBackend {
4770 const use_stage1 = build_options.have_stage1 and comp.bin_file.options.use_stage1;
4771 if (use_stage1) return .stage1;
4772 if (build_options.have_llvm and comp.bin_file.options.use_llvm) return .stage2_llvm;
4773 const target = comp.bin_file.options.target;
4774 if (target.ofmt == .c) return .stage2_c;
4775 return switch (target.cpu.arch) {
4776 .wasm32, .wasm64 => std.builtin.CompilerBackend.stage2_wasm,
4777 .arm, .armeb, .thumb, .thumbeb => .stage2_arm,
4778 .x86_64 => .stage2_x86_64,
4779 .i386 => .stage2_x86,
4780 .aarch64, .aarch64_be, .aarch64_32 => .stage2_aarch64,
4781 .riscv64 => .stage2_riscv64,
4782 .sparc64 => .stage2_sparc64,
4783 else => .other,
4784 };
4785}
4786
47704787pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Allocator.Error![:0]u8 {
47714788 const tracy_trace = trace(@src());
47724789 defer tracy_trace.end();
......@@ -4776,23 +4793,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca
47764793
47774794 const target = comp.getTarget();
47784795 const generic_arch_name = target.cpu.arch.genericName();
4779 const use_stage1 = build_options.have_stage1 and comp.bin_file.options.use_stage1;
4780
4781 const zig_backend: std.builtin.CompilerBackend = blk: {
4782 if (use_stage1) break :blk .stage1;
4783 if (build_options.have_llvm and comp.bin_file.options.use_llvm) break :blk .stage2_llvm;
4784 if (target.ofmt == .c) break :blk .stage2_c;
4785 break :blk switch (target.cpu.arch) {
4786 .wasm32, .wasm64 => std.builtin.CompilerBackend.stage2_wasm,
4787 .arm, .armeb, .thumb, .thumbeb => .stage2_arm,
4788 .x86_64 => .stage2_x86_64,
4789 .i386 => .stage2_x86,
4790 .aarch64, .aarch64_be, .aarch64_32 => .stage2_aarch64,
4791 .riscv64 => .stage2_riscv64,
4792 .sparc64 => .stage2_sparc64,
4793 else => .other,
4794 };
4795 };
4796 const zig_backend = comp.getZigBackend();
47964797
47974798 @setEvalBranchQuota(4000);
47984799 try buffer.writer().print(
src/Module.zig+7-4
......@@ -5259,9 +5259,9 @@ pub fn clearDecl(
52595259 // TODO instead of a union, put this memory trailing Decl objects,
52605260 // and allow it to be variably sized.
52615261 decl.link = switch (mod.comp.bin_file.tag) {
5262 .coff => .{ .coff = link.File.Coff.TextBlock.empty },
5262 .coff => .{ .coff = link.File.Coff.Atom.empty },
52635263 .elf => .{ .elf = link.File.Elf.TextBlock.empty },
5264 .macho => .{ .macho = link.File.MachO.TextBlock.empty },
5264 .macho => .{ .macho = link.File.MachO.Atom.empty },
52655265 .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty },
52665266 .c => .{ .c = {} },
52675267 .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty },
......@@ -5391,6 +5391,9 @@ fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) void {
53915391 if (mod.comp.bin_file.cast(link.File.Wasm)) |wasm| {
53925392 wasm.deleteExport(exp.link.wasm);
53935393 }
5394 if (mod.comp.bin_file.cast(link.File.Coff)) |coff| {
5395 coff.deleteExport(exp.link.coff);
5396 }
53945397 if (mod.failed_exports.fetchSwapRemove(exp)) |failed_kv| {
53955398 failed_kv.value.destroy(mod.gpa);
53965399 }
......@@ -5680,9 +5683,9 @@ pub fn allocateNewDecl(
56805683 .zir_decl_index = 0,
56815684 .src_scope = src_scope,
56825685 .link = switch (mod.comp.bin_file.tag) {
5683 .coff => .{ .coff = link.File.Coff.TextBlock.empty },
5686 .coff => .{ .coff = link.File.Coff.Atom.empty },
56845687 .elf => .{ .elf = link.File.Elf.TextBlock.empty },
5685 .macho => .{ .macho = link.File.MachO.TextBlock.empty },
5688 .macho => .{ .macho = link.File.MachO.Atom.empty },
56865689 .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty },
56875690 .c => .{ .c = {} },
56885691 .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty },
src/Sema.zig+135-40
......@@ -712,6 +712,7 @@ fn analyzeBodyInner(
712712 .vector_type => try sema.zirVectorType(block, inst),
713713 .as => try sema.zirAs(block, inst),
714714 .as_node => try sema.zirAsNode(block, inst),
715 .as_shift_operand => try sema.zirAsShiftOperand(block, inst),
715716 .bit_and => try sema.zirBitwise(block, inst, .bit_and),
716717 .bit_not => try sema.zirBitNot(block, inst),
717718 .bit_or => try sema.zirBitwise(block, inst, .bit_or),
......@@ -848,7 +849,6 @@ fn analyzeBodyInner(
848849 .mul_add => try sema.zirMulAdd(block, inst),
849850 .builtin_call => try sema.zirBuiltinCall(block, inst),
850851 .field_parent_ptr => try sema.zirFieldParentPtr(block, inst),
851 .builtin_async_call => try sema.zirBuiltinAsyncCall(block, inst),
852852 .@"resume" => try sema.zirResume(block, inst),
853853 .@"await" => try sema.zirAwait(block, inst),
854854 .array_base_ptr => try sema.zirArrayBasePtr(block, inst),
......@@ -956,6 +956,7 @@ fn analyzeBodyInner(
956956 .error_to_int => try sema.zirErrorToInt( block, extended),
957957 .int_to_error => try sema.zirIntToError( block, extended),
958958 .reify => try sema.zirReify( block, extended, inst),
959 .builtin_async_call => try sema.zirBuiltinAsyncCall( block, extended),
959960 // zig fmt: on
960961 .fence => {
961962 try sema.zirFence(block, extended);
......@@ -5076,7 +5077,7 @@ pub fn analyzeExport(
50765077 },
50775078 .src = src,
50785079 .link = switch (mod.comp.bin_file.tag) {
5079 .coff => .{ .coff = {} },
5080 .coff => .{ .coff = .{} },
50805081 .elf => .{ .elf = .{} },
50815082 .macho => .{ .macho = .{} },
50825083 .plan9 => .{ .plan9 = null },
......@@ -6152,9 +6153,30 @@ fn analyzeCall(
61526153 if (ensure_result_used) {
61536154 try sema.ensureResultUsed(block, result, call_src);
61546155 }
6156 if (call_tag == .call_always_tail) {
6157 return sema.handleTailCall(block, call_src, func_ty, result);
6158 }
61556159 return result;
61566160}
61576161
6162fn handleTailCall(sema: *Sema, block: *Block, call_src: LazySrcLoc, func_ty: Type, result: Air.Inst.Ref) !Air.Inst.Ref {
6163 const target = sema.mod.getTarget();
6164 const backend = sema.mod.comp.getZigBackend();
6165 if (!target_util.supportsTailCall(target, backend)) {
6166 return sema.fail(block, call_src, "unable to perform tail call: compiler backend '{s}' does not support tail calls on target architecture '{s}' with the selected CPU feature flags", .{
6167 @tagName(backend), @tagName(target.cpu.arch),
6168 });
6169 }
6170 const func_decl = sema.mod.declPtr(sema.owner_func.?.owner_decl);
6171 if (!func_ty.eql(func_decl.ty, sema.mod)) {
6172 return sema.fail(block, call_src, "unable to perform tail call: type of function being called '{}' does not match type of calling function '{}'", .{
6173 func_ty.fmt(sema.mod), func_decl.ty.fmt(sema.mod),
6174 });
6175 }
6176 _ = try block.addUnOp(.ret, result);
6177 return Air.Inst.Ref.unreachable_value;
6178}
6179
61586180fn analyzeInlineCallArg(
61596181 sema: *Sema,
61606182 arg_block: *Block,
......@@ -6670,7 +6692,8 @@ fn instantiateGenericCall(
66706692 try sema.requireFunctionBlock(block, call_src);
66716693
66726694 const comptime_args = callee.comptime_args.?;
6673 const new_fn_info = mod.declPtr(callee.owner_decl).ty.fnInfo();
6695 const func_ty = mod.declPtr(callee.owner_decl).ty;
6696 const new_fn_info = func_ty.fnInfo();
66746697 const runtime_args_len = @intCast(u32, new_fn_info.param_types.len);
66756698 const runtime_args = try sema.arena.alloc(Air.Inst.Ref, runtime_args_len);
66766699 {
......@@ -6717,7 +6740,7 @@ fn instantiateGenericCall(
67176740
67186741 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len +
67196742 runtime_args_len);
6720 const func_inst = try block.addInst(.{
6743 const result = try block.addInst(.{
67216744 .tag = call_tag,
67226745 .data = .{ .pl_op = .{
67236746 .operand = callee_inst,
......@@ -6729,9 +6752,12 @@ fn instantiateGenericCall(
67296752 sema.appendRefsAssumeCapacity(runtime_args);
67306753
67316754 if (ensure_result_used) {
6732 try sema.ensureResultUsed(block, func_inst, call_src);
6755 try sema.ensureResultUsed(block, result, call_src);
67336756 }
6734 return func_inst;
6757 if (call_tag == .call_always_tail) {
6758 return sema.handleTailCall(block, call_src, func_ty, result);
6759 }
6760 return result;
67356761}
67366762
67376763fn emitDbgInline(
......@@ -8177,8 +8203,22 @@ fn zirParam(
81778203 .is_comptime = comptime_syntax,
81788204 .name = param_name,
81798205 });
8180 const result = try sema.addConstant(param_ty, Value.initTag(.generic_poison));
8181 try sema.inst_map.putNoClobber(sema.gpa, inst, result);
8206
8207 if (is_comptime) {
8208 // If this is a comptime parameter we can add a constant generic_poison
8209 // since this is also a generic parameter.
8210 const result = try sema.addConstant(param_ty, Value.initTag(.generic_poison));
8211 try sema.inst_map.putNoClobber(sema.gpa, inst, result);
8212 } else {
8213 // Otherwise we need a dummy runtime instruction.
8214 const result_index = @intCast(Air.Inst.Index, sema.air_instructions.len);
8215 try sema.air_instructions.append(sema.gpa, .{
8216 .tag = .alloc,
8217 .data = .{ .ty = param_ty },
8218 });
8219 const result = Air.indexToRef(result_index);
8220 try sema.inst_map.putNoClobber(sema.gpa, inst, result);
8221 }
81828222}
81838223
81848224fn zirParamAnytype(
......@@ -8225,7 +8265,7 @@ fn zirAs(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst
82258265 defer tracy.end();
82268266
82278267 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
8228 return sema.analyzeAs(block, sema.src, bin_inst.lhs, bin_inst.rhs);
8268 return sema.analyzeAs(block, sema.src, bin_inst.lhs, bin_inst.rhs, false);
82298269}
82308270
82318271fn zirAsNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -8235,7 +8275,17 @@ fn zirAsNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
82358275 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
82368276 const src = inst_data.src();
82378277 const extra = sema.code.extraData(Zir.Inst.As, inst_data.payload_index).data;
8238 return sema.analyzeAs(block, src, extra.dest_type, extra.operand);
8278 return sema.analyzeAs(block, src, extra.dest_type, extra.operand, false);
8279}
8280
8281fn zirAsShiftOperand(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
8282 const tracy = trace(@src());
8283 defer tracy.end();
8284
8285 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
8286 const src = inst_data.src();
8287 const extra = sema.code.extraData(Zir.Inst.As, inst_data.payload_index).data;
8288 return sema.analyzeAs(block, src, extra.dest_type, extra.operand, true);
82398289}
82408290
82418291fn analyzeAs(
......@@ -8244,6 +8294,7 @@ fn analyzeAs(
82448294 src: LazySrcLoc,
82458295 zir_dest_type: Zir.Inst.Ref,
82468296 zir_operand: Zir.Inst.Ref,
8297 no_cast_to_comptime_int: bool,
82478298) CompileError!Air.Inst.Ref {
82488299 const is_ret = if (Zir.refToIndex(zir_dest_type)) |ptr_index|
82498300 sema.code.instructions.items(.tag)[ptr_index] == .ret_type
......@@ -8255,7 +8306,7 @@ fn analyzeAs(
82558306 if (dest_ty.zigTypeTag() == .NoReturn) {
82568307 return sema.fail(block, src, "cannot cast to noreturn", .{});
82578308 }
8258 return sema.coerceExtra(block, dest_ty, operand, src, true, is_ret) catch |err| switch (err) {
8309 return sema.coerceExtra(block, dest_ty, operand, src, .{ .is_ret = is_ret, .no_cast_to_comptime_int = no_cast_to_comptime_int }) catch |err| switch (err) {
82598310 error.NotCoercible => unreachable,
82608311 else => |e| return e,
82618312 };
......@@ -10459,7 +10510,12 @@ fn zirShl(
1045910510
1046010511 const runtime_src = if (maybe_lhs_val) |lhs_val| rs: {
1046110512 if (lhs_val.isUndef()) return sema.addConstUndef(lhs_ty);
10462 const rhs_val = maybe_rhs_val orelse break :rs rhs_src;
10513 const rhs_val = maybe_rhs_val orelse {
10514 if (scalar_ty.zigTypeTag() == .ComptimeInt) {
10515 return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be a comptime known", .{});
10516 }
10517 break :rs rhs_src;
10518 };
1046310519
1046410520 const val = switch (air_tag) {
1046510521 .shl_exact => val: {
......@@ -10583,7 +10639,10 @@ fn zirShr(
1058310639 const target = sema.mod.getTarget();
1058410640 const scalar_ty = lhs_ty.scalarType();
1058510641
10586 const runtime_src = if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| rs: {
10642 const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs);
10643 const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs);
10644
10645 const runtime_src = if (maybe_rhs_val) |rhs_val| rs: {
1058710646 if (rhs_val.isUndef()) {
1058810647 return sema.addConstUndef(lhs_ty);
1058910648 }
......@@ -10615,7 +10674,7 @@ fn zirShr(
1061510674 });
1061610675 }
1061710676 }
10618 if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| {
10677 if (maybe_lhs_val) |lhs_val| {
1061910678 if (lhs_val.isUndef()) {
1062010679 return sema.addConstUndef(lhs_ty);
1062110680 }
......@@ -10633,6 +10692,10 @@ fn zirShr(
1063310692 }
1063410693 } else rhs_src;
1063510694
10695 if (maybe_rhs_val == null and scalar_ty.zigTypeTag() == .ComptimeInt) {
10696 return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be a comptime known", .{});
10697 }
10698
1063610699 try sema.requireRuntimeBlock(block, src, runtime_src);
1063710700 const result = try block.addBinOp(air_tag, lhs, rhs);
1063810701 if (block.wantSafety()) {
......@@ -15353,7 +15416,7 @@ fn analyzeRet(
1535315416 if (sema.fn_ret_ty.zigTypeTag() == .ErrorUnion) {
1535415417 try sema.addToInferredErrorSet(uncasted_operand);
1535515418 }
15356 const operand = sema.coerceExtra(block, sema.fn_ret_ty, uncasted_operand, src, true, true) catch |err| switch (err) {
15419 const operand = sema.coerceExtra(block, sema.fn_ret_ty, uncasted_operand, src, .{ .is_ret = true }) catch |err| switch (err) {
1535715420 error.NotCoercible => unreachable,
1535815421 else => |e| return e,
1535915422 };
......@@ -19262,7 +19325,7 @@ fn resolveCallOptions(
1926219325 return wanted_modifier;
1926319326 },
1926419327 // These can be upgraded to comptime. nosuspend bit can be safely ignored.
19265 .always_tail, .always_inline, .compile_time => {
19328 .always_inline, .compile_time => {
1926619329 _ = (try sema.resolveDefinedValue(block, func_src, func)) orelse {
1926719330 return sema.fail(block, func_src, "modifier '{s}' requires a comptime-known function", .{@tagName(wanted_modifier)});
1926819331 };
......@@ -19272,6 +19335,12 @@ fn resolveCallOptions(
1927219335 }
1927319336 return wanted_modifier;
1927419337 },
19338 .always_tail => {
19339 if (is_comptime) {
19340 return .compile_time;
19341 }
19342 return wanted_modifier;
19343 },
1927519344 .async_kw => {
1927619345 if (is_nosuspend) {
1927719346 return sema.fail(block, modifier_src, "modifier 'async_kw' cannot be used inside nosuspend block", .{});
......@@ -19614,9 +19683,9 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
1961419683 });
1961519684}
1961619685
19617fn zirBuiltinAsyncCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
19618 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
19619 const src = inst_data.src();
19686fn zirBuiltinAsyncCall(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
19687 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
19688 const src = LazySrcLoc.nodeOffset(extra.node);
1962019689 return sema.failWithUseOfAsync(block, src);
1962119690}
1962219691
......@@ -21871,6 +21940,7 @@ fn unionFieldPtr(
2187121940 .mutable = union_ptr_ty.ptrIsMutable(),
2187221941 .@"addrspace" = union_ptr_ty.ptrAddressSpace(),
2187321942 });
21943 const enum_field_index = @intCast(u32, union_obj.tag_ty.enumFieldIndex(field_name).?);
2187421944
2187521945 if (initializing and field.ty.zigTypeTag() == .NoReturn) {
2187621946 const msg = msg: {
......@@ -21892,11 +21962,10 @@ fn unionFieldPtr(
2189221962 if (union_val.isUndef()) {
2189321963 return sema.failWithUseOfUndef(block, src);
2189421964 }
21895 const enum_field_index = union_obj.tag_ty.enumFieldIndex(field_name).?;
2189621965 const tag_and_val = union_val.castTag(.@"union").?.data;
2189721966 var field_tag_buf: Value.Payload.U32 = .{
2189821967 .base = .{ .tag = .enum_field_index },
21899 .data = @intCast(u32, enum_field_index),
21968 .data = enum_field_index,
2190021969 };
2190121970 const field_tag = Value.initPayload(&field_tag_buf.base);
2190221971 const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, sema.mod);
......@@ -21928,7 +21997,7 @@ fn unionFieldPtr(
2192821997 if (!initializing and union_obj.layout == .Auto and block.wantSafety() and
2192921998 union_ty.unionTagTypeSafety() != null and union_obj.fields.count() > 1)
2193021999 {
21931 const wanted_tag_val = try Value.Tag.enum_field_index.create(sema.arena, field_index);
22000 const wanted_tag_val = try Value.Tag.enum_field_index.create(sema.arena, enum_field_index);
2193222001 const wanted_tag = try sema.addConstant(union_obj.tag_ty, wanted_tag_val);
2193322002 // TODO would it be better if get_union_tag supported pointers to unions?
2193422003 const union_val = try block.addTyOp(.load, union_ty, union_ptr);
......@@ -21958,15 +22027,15 @@ fn unionFieldVal(
2195822027 const union_obj = union_ty.cast(Type.Payload.Union).?.data;
2195922028 const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src);
2196022029 const field = union_obj.fields.values()[field_index];
22030 const enum_field_index = @intCast(u32, union_obj.tag_ty.enumFieldIndex(field_name).?);
2196122031
2196222032 if (try sema.resolveMaybeUndefVal(block, src, union_byval)) |union_val| {
2196322033 if (union_val.isUndef()) return sema.addConstUndef(field.ty);
2196422034
2196522035 const tag_and_val = union_val.castTag(.@"union").?.data;
21966 const enum_field_index = union_obj.tag_ty.enumFieldIndex(field_name).?;
2196722036 var field_tag_buf: Value.Payload.U32 = .{
2196822037 .base = .{ .tag = .enum_field_index },
21969 .data = @intCast(u32, enum_field_index),
22038 .data = enum_field_index,
2197022039 };
2197122040 const field_tag = Value.initPayload(&field_tag_buf.base);
2197222041 const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, sema.mod);
......@@ -22002,7 +22071,7 @@ fn unionFieldVal(
2200222071 if (union_obj.layout == .Auto and block.wantSafety() and
2200322072 union_ty.unionTagTypeSafety() != null and union_obj.fields.count() > 1)
2200422073 {
22005 const wanted_tag_val = try Value.Tag.enum_field_index.create(sema.arena, field_index);
22074 const wanted_tag_val = try Value.Tag.enum_field_index.create(sema.arena, enum_field_index);
2200622075 const wanted_tag = try sema.addConstant(union_obj.tag_ty, wanted_tag_val);
2200722076 const active_tag = try block.addTyOp(.get_union_tag, union_obj.tag_ty, union_byval);
2200822077 const ok = try block.addBinOp(.cmp_eq, active_tag, wanted_tag);
......@@ -22504,7 +22573,7 @@ fn coerce(
2250422573 inst: Air.Inst.Ref,
2250522574 inst_src: LazySrcLoc,
2250622575) CompileError!Air.Inst.Ref {
22507 return sema.coerceExtra(block, dest_ty_unresolved, inst, inst_src, true, false) catch |err| switch (err) {
22576 return sema.coerceExtra(block, dest_ty_unresolved, inst, inst_src, .{}) catch |err| switch (err) {
2250822577 error.NotCoercible => unreachable,
2250922578 else => |e| return e,
2251022579 };
......@@ -22516,14 +22585,22 @@ const CoersionError = CompileError || error{
2251622585 NotCoercible,
2251722586};
2251822587
22588const CoerceOpts = struct {
22589 /// Should coerceExtra emit error messages.
22590 report_err: bool = true,
22591 /// Ignored if `report_err == false`.
22592 is_ret: bool = false,
22593 /// Should coercion to comptime_int ermit an error message.
22594 no_cast_to_comptime_int: bool = false,
22595};
22596
2251922597fn coerceExtra(
2252022598 sema: *Sema,
2252122599 block: *Block,
2252222600 dest_ty_unresolved: Type,
2252322601 inst: Air.Inst.Ref,
2252422602 inst_src: LazySrcLoc,
22525 report_err: bool,
22526 is_ret: bool,
22603 opts: CoerceOpts,
2252722604) CoersionError!Air.Inst.Ref {
2252822605 switch (dest_ty_unresolved.tag()) {
2252922606 .var_args_param => return sema.coerceVarArgParam(block, inst, inst_src),
......@@ -22575,7 +22652,7 @@ fn coerceExtra(
2257522652
2257622653 // T to ?T
2257722654 const child_type = try dest_ty.optionalChildAlloc(sema.arena);
22578 const intermediate = sema.coerceExtra(block, child_type, inst, inst_src, false, is_ret) catch |err| switch (err) {
22655 const intermediate = sema.coerceExtra(block, child_type, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) {
2257922656 error.NotCoercible => {
2258022657 if (in_memory_result == .no_match) {
2258122658 // Try to give more useful notes
......@@ -22691,7 +22768,7 @@ fn coerceExtra(
2269122768 return sema.addConstant(dest_ty, Value.@"null");
2269222769 },
2269322770 .ComptimeInt => {
22694 const addr = sema.coerceExtra(block, Type.usize, inst, inst_src, false, is_ret) catch |err| switch (err) {
22771 const addr = sema.coerceExtra(block, Type.usize, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) {
2269522772 error.NotCoercible => break :pointer,
2269622773 else => |e| return e,
2269722774 };
......@@ -22702,7 +22779,7 @@ fn coerceExtra(
2270222779 .signed => Type.isize,
2270322780 .unsigned => Type.usize,
2270422781 };
22705 const addr = sema.coerceExtra(block, ptr_size_ty, inst, inst_src, false, is_ret) catch |err| switch (err) {
22782 const addr = sema.coerceExtra(block, ptr_size_ty, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) {
2270622783 error.NotCoercible => {
2270722784 // Try to give more useful notes
2270822785 in_memory_result = try sema.coerceInMemoryAllowed(block, ptr_size_ty, inst_ty, false, target, dest_ty_src, inst_src);
......@@ -22828,7 +22905,13 @@ fn coerceExtra(
2282822905 },
2282922906 .Int, .ComptimeInt => switch (inst_ty.zigTypeTag()) {
2283022907 .Float, .ComptimeFloat => float: {
22831 const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse break :float;
22908 const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse {
22909 if (dest_ty.zigTypeTag() == .ComptimeInt) {
22910 if (!opts.report_err) return error.NotCoercible;
22911 return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_int' must be comptime known");
22912 }
22913 break :float;
22914 };
2283222915
2283322916 if (val.floatHasFraction()) {
2283422917 return sema.fail(
......@@ -22845,11 +22928,16 @@ fn coerceExtra(
2284522928 if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| {
2284622929 // comptime known integer to other number
2284722930 if (!(try sema.intFitsInType(block, inst_src, val, dest_ty, null))) {
22848 if (!report_err) return error.NotCoercible;
22931 if (!opts.report_err) return error.NotCoercible;
2284922932 return sema.fail(block, inst_src, "type '{}' cannot represent integer value '{}'", .{ dest_ty.fmt(sema.mod), val.fmtValue(inst_ty, sema.mod) });
2285022933 }
2285122934 return try sema.addConstant(dest_ty, val);
2285222935 }
22936 if (dest_ty.zigTypeTag() == .ComptimeInt) {
22937 if (!opts.report_err) return error.NotCoercible;
22938 if (opts.no_cast_to_comptime_int) return inst;
22939 return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_int' must be comptime known");
22940 }
2285322941
2285422942 // integer widening
2285522943 const dst_info = dest_ty.intInfo(target);
......@@ -22886,6 +22974,7 @@ fn coerceExtra(
2288622974 }
2288722975 return try sema.addConstant(dest_ty, result_val);
2288822976 } else if (dest_ty.zigTypeTag() == .ComptimeFloat) {
22977 if (!opts.report_err) return error.NotCoercible;
2288922978 return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_float' must be comptime known");
2289022979 }
2289122980
......@@ -22898,7 +22987,13 @@ fn coerceExtra(
2289822987 }
2289922988 },
2290022989 .Int, .ComptimeInt => int: {
22901 const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse break :int;
22990 const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse {
22991 if (dest_ty.zigTypeTag() == .ComptimeFloat) {
22992 if (!opts.report_err) return error.NotCoercible;
22993 return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_float' must be comptime known");
22994 }
22995 break :int;
22996 };
2290222997 const result_val = try val.intToFloat(sema.arena, inst_ty, dest_ty, target);
2290322998 // TODO implement this compile error
2290422999 //const int_again_val = try result_val.floatToInt(sema.arena, inst_ty);
......@@ -23050,9 +23145,9 @@ fn coerceExtra(
2305023145 return sema.addConstUndef(dest_ty);
2305123146 }
2305223147
23053 if (!report_err) return error.NotCoercible;
23148 if (!opts.report_err) return error.NotCoercible;
2305423149
23055 if (is_ret and dest_ty.zigTypeTag() == .NoReturn) {
23150 if (opts.is_ret and dest_ty.zigTypeTag() == .NoReturn) {
2305623151 const msg = msg: {
2305723152 const msg = try sema.errMsg(block, inst_src, "function declared 'noreturn' returns", .{});
2305823153 errdefer msg.destroy(sema.gpa);
......@@ -23089,7 +23184,7 @@ fn coerceExtra(
2308923184 try in_memory_result.report(sema, block, inst_src, msg);
2309023185
2309123186 // Add notes about function return type
23092 if (is_ret and sema.mod.test_functions.get(sema.func.?.owner_decl) == null) {
23187 if (opts.is_ret and sema.mod.test_functions.get(sema.func.?.owner_decl) == null) {
2309323188 const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };
2309423189 const src_decl = sema.mod.declPtr(sema.func.?.owner_decl);
2309523190 if (inst_ty.isError() and !dest_ty.isError()) {
......@@ -24050,7 +24145,7 @@ fn storePtr2(
2405024145 // https://github.com/ziglang/zig/issues/11154
2405124146 if (sema.obtainBitCastedVectorPtr(ptr)) |vector_ptr| {
2405224147 const vector_ty = sema.typeOf(vector_ptr).childType();
24053 const vector = sema.coerceExtra(block, vector_ty, uncasted_operand, operand_src, true, is_ret) catch |err| switch (err) {
24148 const vector = sema.coerceExtra(block, vector_ty, uncasted_operand, operand_src, .{ .is_ret = is_ret }) catch |err| switch (err) {
2405424149 error.NotCoercible => unreachable,
2405524150 else => |e| return e,
2405624151 };
......@@ -24058,7 +24153,7 @@ fn storePtr2(
2405824153 return;
2405924154 }
2406024155
24061 const operand = sema.coerceExtra(block, elem_ty, uncasted_operand, operand_src, true, is_ret) catch |err| switch (err) {
24156 const operand = sema.coerceExtra(block, elem_ty, uncasted_operand, operand_src, .{ .is_ret = is_ret }) catch |err| switch (err) {
2406224157 error.NotCoercible => unreachable,
2406324158 else => |e| return e,
2406424159 };
......@@ -26793,7 +26888,7 @@ fn wrapErrorUnionPayload(
2679326888 inst_src: LazySrcLoc,
2679426889) !Air.Inst.Ref {
2679526890 const dest_payload_ty = dest_ty.errorUnionPayload();
26796 const coerced = try sema.coerceExtra(block, dest_payload_ty, inst, inst_src, false, false);
26891 const coerced = try sema.coerceExtra(block, dest_payload_ty, inst, inst_src, .{ .report_err = false });
2679726892 if (try sema.resolveMaybeUndefVal(block, inst_src, coerced)) |val| {
2679826893 return sema.addConstant(dest_ty, try Value.Tag.eu_payload.create(sema.arena, val));
2679926894 }
src/Zir.zig+9-6
......@@ -242,6 +242,8 @@ pub const Inst = struct {
242242 /// Type coercion to the function's return type.
243243 /// Uses the `pl_node` field. Payload is `As`. AST node could be many things.
244244 as_node,
245 /// Same as `as_node` but ignores runtime to comptime int error.
246 as_shift_operand,
245247 /// Bitwise AND. `&`
246248 bit_and,
247249 /// Reinterpret the memory representation of a value as a different type.
......@@ -942,9 +944,6 @@ pub const Inst = struct {
942944 /// Implements the `@maximum` builtin.
943945 /// Uses the `pl_node` union field with payload `Bin`
944946 maximum,
945 /// Implements the `@asyncCall` builtin.
946 /// Uses the `pl_node` union field with payload `AsyncCall`.
947 builtin_async_call,
948947 /// Implements the `@cImport` builtin.
949948 /// Uses the `pl_node` union field with payload `Block`.
950949 c_import,
......@@ -1029,6 +1028,7 @@ pub const Inst = struct {
10291028 .anyframe_type,
10301029 .as,
10311030 .as_node,
1031 .as_shift_operand,
10321032 .bit_and,
10331033 .bitcast,
10341034 .bit_or,
......@@ -1231,7 +1231,6 @@ pub const Inst = struct {
12311231 .memcpy,
12321232 .memset,
12331233 .minimum,
1234 .builtin_async_call,
12351234 .c_import,
12361235 .@"resume",
12371236 .@"await",
......@@ -1339,6 +1338,7 @@ pub const Inst = struct {
13391338 .anyframe_type,
13401339 .as,
13411340 .as_node,
1341 .as_shift_operand,
13421342 .bit_and,
13431343 .bitcast,
13441344 .bit_or,
......@@ -1513,7 +1513,6 @@ pub const Inst = struct {
15131513 .field_parent_ptr,
15141514 .maximum,
15151515 .minimum,
1516 .builtin_async_call,
15171516 .c_import,
15181517 .@"resume",
15191518 .@"await",
......@@ -1577,6 +1576,7 @@ pub const Inst = struct {
15771576 .anyframe_type = .un_node,
15781577 .as = .bin,
15791578 .as_node = .pl_node,
1579 .as_shift_operand = .pl_node,
15801580 .bit_and = .pl_node,
15811581 .bitcast = .pl_node,
15821582 .bit_not = .un_node,
......@@ -1801,7 +1801,6 @@ pub const Inst = struct {
18011801 .memcpy = .pl_node,
18021802 .memset = .pl_node,
18031803 .minimum = .pl_node,
1804 .builtin_async_call = .pl_node,
18051804 .c_import = .pl_node,
18061805
18071806 .alloc = .un_node,
......@@ -1972,6 +1971,9 @@ pub const Inst = struct {
19721971 /// `operand` is payload index to `UnNode`.
19731972 /// `small` contains `NameStrategy
19741973 reify,
1974 /// Implements the `@asyncCall` builtin.
1975 /// `operand` is payload index to `AsyncCall`.
1976 builtin_async_call,
19751977
19761978 pub const InstData = struct {
19771979 opcode: Extended,
......@@ -3454,6 +3456,7 @@ pub const Inst = struct {
34543456 };
34553457
34563458 pub const AsyncCall = struct {
3459 node: i32,
34573460 frame_buffer: Ref,
34583461 result_ptr: Ref,
34593462 fn_ptr: Ref,
src/arch/aarch64/CodeGen.zig+7-9
......@@ -3466,19 +3466,16 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
34663466 // on linking.
34673467 const mod = self.bin_file.options.module.?;
34683468 if (self.air.value(callee)) |func_value| {
3469 if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) {
3469 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
34703470 if (func_value.castTag(.function)) |func_payload| {
34713471 const func = func_payload.data;
34723472 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
34733473 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
34743474 const fn_owner_decl = mod.declPtr(func.owner_decl);
3475 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
3475 const got_addr = blk: {
34763476 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
34773477 break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes);
3478 } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
3479 coff_file.offset_table_virtual_address + fn_owner_decl.link.coff.offset_table_index * ptr_bytes
3480 else
3481 unreachable;
3478 };
34823479
34833480 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr });
34843481
......@@ -3546,6 +3543,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
35463543 } else {
35473544 return self.fail("TODO implement calling bitcasted functions", .{});
35483545 }
3546 } else if (self.bin_file.cast(link.File.Coff)) |_| {
3547 return self.fail("TODO implement calling in COFF for {}", .{self.target.cpu.arch});
35493548 } else unreachable;
35503549 } else {
35513550 assert(ty.zigTypeTag() == .Pointer);
......@@ -5109,9 +5108,8 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
51095108 // the linker has enough info to perform relocations.
51105109 assert(decl.link.macho.sym_index != 0);
51115110 return MCValue{ .got_load = decl.link.macho.sym_index };
5112 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
5113 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
5114 return MCValue{ .memory = got_addr };
5111 } else if (self.bin_file.cast(link.File.Coff)) |_| {
5112 return self.fail("TODO codegen COFF const Decl pointer", .{});
51155113 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
51165114 try p9.seeDecl(decl_index);
51175115 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
src/arch/arm/CodeGen.zig+5-9
......@@ -3698,7 +3698,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
36983698 // Due to incremental compilation, how function calls are generated depends
36993699 // on linking.
37003700 switch (self.bin_file.tag) {
3701 .elf, .coff => {
3701 .elf => {
37023702 if (self.air.value(callee)) |func_value| {
37033703 if (func_value.castTag(.function)) |func_payload| {
37043704 const func = func_payload.data;
......@@ -3709,11 +3709,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
37093709 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
37103710 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
37113711 break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes);
3712 } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
3713 coff_file.offset_table_virtual_address + fn_owner_decl.link.coff.offset_table_index * ptr_bytes
3714 else
3715 unreachable;
3716
3712 } else unreachable;
37173713 try self.genSetReg(Type.initTag(.usize), .lr, .{ .memory = got_addr });
37183714 } else if (func_value.castTag(.extern_fn)) |_| {
37193715 return self.fail("TODO implement calling extern functions", .{});
......@@ -3751,6 +3747,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
37513747 }
37523748 },
37533749 .macho => unreachable, // unsupported architecture for MachO
3750 .coff => return self.fail("TODO implement call in COFF for {}", .{self.target.cpu.arch}),
37543751 .plan9 => return self.fail("TODO implement call on plan9 for {}", .{self.target.cpu.arch}),
37553752 else => unreachable,
37563753 }
......@@ -5548,9 +5545,8 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
55485545 return MCValue{ .memory = got_addr };
55495546 } else if (self.bin_file.cast(link.File.MachO)) |_| {
55505547 unreachable; // unsupported architecture for MachO
5551 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
5552 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
5553 return MCValue{ .memory = got_addr };
5548 } else if (self.bin_file.cast(link.File.Coff)) |_| {
5549 return self.fail("TODO codegen COFF const Decl pointer", .{});
55545550 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
55555551 try p9.seeDecl(decl_index);
55565552 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
src/arch/riscv64/CodeGen.zig+7-9
......@@ -1718,7 +1718,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
17181718
17191719 // Due to incremental compilation, how function calls are generated depends
17201720 // on linking.
1721 if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) {
1721 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
17221722 for (info.args) |mc_arg, arg_i| {
17231723 const arg = args[arg_i];
17241724 const arg_ty = self.air.typeOf(arg);
......@@ -1752,13 +1752,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
17521752 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
17531753 const mod = self.bin_file.options.module.?;
17541754 const fn_owner_decl = mod.declPtr(func.owner_decl);
1755 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
1755 const got_addr = blk: {
17561756 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
17571757 break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes);
1758 } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
1759 coff_file.offset_table_virtual_address + fn_owner_decl.link.coff.offset_table_index * ptr_bytes
1760 else
1761 unreachable;
1758 };
17621759
17631760 try self.genSetReg(Type.initTag(.usize), .ra, .{ .memory = got_addr });
17641761 _ = try self.addInst(.{
......@@ -1777,6 +1774,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
17771774 } else {
17781775 return self.fail("TODO implement calling runtime known function pointer", .{});
17791776 }
1777 } else if (self.bin_file.cast(link.File.Coff)) |_| {
1778 return self.fail("TODO implement calling in COFF for {}", .{self.target.cpu.arch});
17801779 } else if (self.bin_file.cast(link.File.MachO)) |_| {
17811780 unreachable; // unsupported architecture for MachO
17821781 } else if (self.bin_file.cast(link.File.Plan9)) |_| {
......@@ -2591,9 +2590,8 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
25912590 // TODO I'm hacking my way through here by repurposing .memory for storing
25922591 // index to the GOT target symbol index.
25932592 return MCValue{ .memory = decl.link.macho.sym_index };
2594 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
2595 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
2596 return MCValue{ .memory = got_addr };
2593 } else if (self.bin_file.cast(link.File.Coff)) |_| {
2594 return self.fail("TODO codegen COFF const Decl pointer", .{});
25972595 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
25982596 try p9.seeDecl(decl_index);
25992597 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
src/arch/x86_64/CodeGen.zig+49-17
......@@ -2657,22 +2657,26 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue
26572657 .direct_load,
26582658 => |sym_index| {
26592659 const abi_size = @intCast(u32, ptr_ty.abiSize(self.target.*));
2660 const mod = self.bin_file.options.module.?;
2661 const fn_owner_decl = mod.declPtr(self.mod_fn.owner_decl);
2662 const atom_index = if (self.bin_file.tag == link.File.MachO.base_tag)
2663 fn_owner_decl.link.macho.sym_index
2664 else
2665 fn_owner_decl.link.coff.sym_index;
26602666 const flags: u2 = switch (ptr) {
26612667 .got_load => 0b00,
26622668 .direct_load => 0b01,
26632669 else => unreachable,
26642670 };
2665 const mod = self.bin_file.options.module.?;
2666 const fn_owner_decl = mod.declPtr(self.mod_fn.owner_decl);
26672671 _ = try self.addInst(.{
2668 .tag = .lea_pie,
2672 .tag = .lea_pic,
26692673 .ops = Mir.Inst.Ops.encode(.{
26702674 .reg1 = registerAlias(reg, abi_size),
26712675 .flags = flags,
26722676 }),
26732677 .data = .{
26742678 .relocation = .{
2675 .atom_index = fn_owner_decl.link.macho.sym_index,
2679 .atom_index = atom_index,
26762680 .sym_index = sym_index,
26772681 },
26782682 },
......@@ -3961,20 +3965,17 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
39613965 // Due to incremental compilation, how function calls are generated depends
39623966 // on linking.
39633967 const mod = self.bin_file.options.module.?;
3964 if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) {
3968 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
39653969 if (self.air.value(callee)) |func_value| {
39663970 if (func_value.castTag(.function)) |func_payload| {
39673971 const func = func_payload.data;
39683972 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
39693973 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
39703974 const fn_owner_decl = mod.declPtr(func.owner_decl);
3971 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
3975 const got_addr = blk: {
39723976 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
39733977 break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes);
3974 } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
3975 @intCast(u32, coff_file.offset_table_virtual_address + fn_owner_decl.link.coff.offset_table_index * ptr_bytes)
3976 else
3977 unreachable;
3978 };
39783979 _ = try self.addInst(.{
39793980 .tag = .call,
39803981 .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }),
......@@ -3998,14 +3999,47 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
39983999 .data = undefined,
39994000 });
40004001 }
4001 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4002 } else if (self.bin_file.cast(link.File.Coff)) |_| {
40024003 if (self.air.value(callee)) |func_value| {
40034004 if (func_value.castTag(.function)) |func_payload| {
40044005 const func = func_payload.data;
40054006 const fn_owner_decl = mod.declPtr(func.owner_decl);
40064007 try self.genSetReg(Type.initTag(.usize), .rax, .{
4007 .got_load = fn_owner_decl.link.macho.sym_index,
4008 .got_load = fn_owner_decl.link.coff.sym_index,
40084009 });
4010 _ = try self.addInst(.{
4011 .tag = .call,
4012 .ops = Mir.Inst.Ops.encode(.{
4013 .reg1 = .rax,
4014 .flags = 0b01,
4015 }),
4016 .data = undefined,
4017 });
4018 } else if (func_value.castTag(.extern_fn)) |_| {
4019 return self.fail("TODO implement calling extern functions", .{});
4020 } else {
4021 return self.fail("TODO implement calling bitcasted functions", .{});
4022 }
4023 } else {
4024 assert(ty.zigTypeTag() == .Pointer);
4025 const mcv = try self.resolveInst(callee);
4026 try self.genSetReg(Type.initTag(.usize), .rax, mcv);
4027 _ = try self.addInst(.{
4028 .tag = .call,
4029 .ops = Mir.Inst.Ops.encode(.{
4030 .reg1 = .rax,
4031 .flags = 0b01,
4032 }),
4033 .data = undefined,
4034 });
4035 }
4036 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4037 if (self.air.value(callee)) |func_value| {
4038 if (func_value.castTag(.function)) |func_payload| {
4039 const func = func_payload.data;
4040 const fn_owner_decl = mod.declPtr(func.owner_decl);
4041 const sym_index = fn_owner_decl.link.macho.sym_index;
4042 try self.genSetReg(Type.initTag(.usize), .rax, .{ .got_load = sym_index });
40094043 // callq *%rax
40104044 _ = try self.addInst(.{
40114045 .tag = .call,
......@@ -6842,13 +6876,11 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
68426876 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
68436877 return MCValue{ .memory = got_addr };
68446878 } else if (self.bin_file.cast(link.File.MachO)) |_| {
6845 // Because MachO is PIE-always-on, we defer memory address resolution until
6846 // the linker has enough info to perform relocations.
68476879 assert(decl.link.macho.sym_index != 0);
68486880 return MCValue{ .got_load = decl.link.macho.sym_index };
6849 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
6850 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
6851 return MCValue{ .memory = got_addr };
6881 } else if (self.bin_file.cast(link.File.Coff)) |_| {
6882 assert(decl.link.coff.sym_index != 0);
6883 return MCValue{ .got_load = decl.link.coff.sym_index };
68526884 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
68536885 try p9.seeDecl(decl_index);
68546886 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
src/arch/x86_64/Emit.zig+29-11
......@@ -137,7 +137,7 @@ pub fn lowerMir(emit: *Emit) InnerError!void {
137137 .fld => try emit.mirFld(inst),
138138
139139 .lea => try emit.mirLea(inst),
140 .lea_pie => try emit.mirLeaPie(inst),
140 .lea_pic => try emit.mirLeaPic(inst),
141141
142142 .shl => try emit.mirShift(.shl, inst),
143143 .sal => try emit.mirShift(.sal, inst),
......@@ -338,7 +338,7 @@ fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
338338 .base = ops.reg1,
339339 }), emit.code);
340340 },
341 0b11 => return emit.fail("TODO unused JMP/CALL variant 0b11", .{}),
341 0b11 => return emit.fail("TODO unused variant jmp/call 0b11", .{}),
342342 }
343343}
344344
......@@ -784,7 +784,7 @@ fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
784784 // FD
785785 return lowerToFdEnc(.mov, ops.reg1, imm, emit.code);
786786 },
787 else => return emit.fail("TODO unused variant: movabs 0b{b}", .{ops.flags}),
787 else => return emit.fail("TODO unused movabs variant", .{}),
788788 }
789789}
790790
......@@ -978,12 +978,17 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
978978 }
979979}
980980
981fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
981fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
982982 const tag = emit.mir.instructions.items(.tag)[inst];
983 assert(tag == .lea_pie);
983 assert(tag == .lea_pic);
984984 const ops = emit.mir.instructions.items(.ops)[inst].decode();
985985 const relocation = emit.mir.instructions.items(.data)[inst].relocation;
986986
987 switch (ops.flags) {
988 0b00, 0b01 => {},
989 else => return emit.fail("TODO unused LEA PIC variants 0b10 and 0b11", .{}),
990 }
991
987992 // lea reg1, [rip + reloc]
988993 // RM
989994 try lowerToRmEnc(
......@@ -994,16 +999,17 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
994999 );
9951000
9961001 const end_offset = emit.code.items.len;
1002 const gpa = emit.bin_file.allocator;
9971003
9981004 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
9991005 const reloc_type = switch (ops.flags) {
10001006 0b00 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT),
10011007 0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
1002 else => return emit.fail("TODO unused LEA PIE variants 0b10 and 0b11", .{}),
1008 else => unreachable,
10031009 };
10041010 const atom = macho_file.atom_by_index_table.get(relocation.atom_index).?;
10051011 log.debug("adding reloc of type {} to local @{d}", .{ reloc_type, relocation.sym_index });
1006 try atom.relocs.append(emit.bin_file.allocator, .{
1012 try atom.relocs.append(gpa, .{
10071013 .offset = @intCast(u32, end_offset - 4),
10081014 .target = .{ .sym_index = relocation.sym_index, .file = null },
10091015 .addend = 0,
......@@ -1012,11 +1018,23 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
10121018 .length = 2,
10131019 .@"type" = reloc_type,
10141020 });
1021 } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
1022 const atom = coff_file.atom_by_index_table.get(relocation.atom_index).?;
1023 try atom.addRelocation(coff_file, .{
1024 .@"type" = switch (ops.flags) {
1025 0b00 => .got,
1026 0b01 => .direct,
1027 else => unreachable,
1028 },
1029 .target = .{ .sym_index = relocation.sym_index, .file = null },
1030 .offset = @intCast(u32, end_offset - 4),
1031 .addend = 0,
1032 .pcrel = true,
1033 .length = 2,
1034 .prev_vaddr = atom.getSymbol(coff_file).value,
1035 });
10151036 } else {
1016 return emit.fail(
1017 "TODO implement lea reg, [rip + reloc] for linking backends different than MachO",
1018 .{},
1019 );
1037 return emit.fail("TODO implement lea reg, [rip + reloc] for linking backends different than MachO", .{});
10201038 }
10211039}
10221040
src/arch/x86_64/Mir.zig+6-7
......@@ -178,11 +178,11 @@ pub const Inst = struct {
178178 lea,
179179
180180 /// ops flags: form:
181 /// 0b00 reg1, [rip + reloc] // via GOT emits X86_64_RELOC_GOT relocation
182 /// 0b01 reg1, [rip + reloc] // direct load emits X86_64_RELOC_SIGNED relocation
181 /// 0b00 reg1, [rip + reloc] // via GOT PIC
182 /// 0b01 reg1, [rip + reloc] // direct load PIC
183183 /// Notes:
184184 /// * `Data` contains `relocation`
185 lea_pie,
185 lea_pic,
186186
187187 /// ops flags: form:
188188 /// 0b00 reg1, 1
......@@ -242,15 +242,14 @@ pub const Inst = struct {
242242 imul_complex,
243243
244244 /// ops flags: form:
245 /// 0bX0 reg1, imm64
246 /// 0bX1 rax, moffs64
245 /// 0b00 reg1, imm64
246 /// 0b01 rax, moffs64
247247 /// Notes:
248248 /// * If reg1 is 64-bit, the immediate is 64-bit and stored
249249 /// within extra data `Imm64`.
250 /// * For 0bX1, reg1 (or reg2) need to be
250 /// * For 0b01, reg1 (or reg2) need to be
251251 /// a version of rax. If reg1 == .none, then reg2 == .rax,
252252 /// or vice versa.
253 /// TODO handle scaling
254253 movabs,
255254
256255 /// ops flags: form:
src/codegen/llvm.zig+133-4
......@@ -198,6 +198,122 @@ pub fn targetTriple(allocator: Allocator, target: std.Target) ![:0]u8 {
198198 return llvm_triple.toOwnedSliceSentinel(0);
199199}
200200
201pub fn targetOs(os_tag: std.Target.Os.Tag) llvm.OSType {
202 return switch (os_tag) {
203 .freestanding, .other, .opencl, .glsl450, .vulkan, .plan9 => .UnknownOS,
204 .windows, .uefi => .Win32,
205 .ananas => .Ananas,
206 .cloudabi => .CloudABI,
207 .dragonfly => .DragonFly,
208 .freebsd => .FreeBSD,
209 .fuchsia => .Fuchsia,
210 .ios => .IOS,
211 .kfreebsd => .KFreeBSD,
212 .linux => .Linux,
213 .lv2 => .Lv2,
214 .macos => .MacOSX,
215 .netbsd => .NetBSD,
216 .openbsd => .OpenBSD,
217 .solaris => .Solaris,
218 .zos => .ZOS,
219 .haiku => .Haiku,
220 .minix => .Minix,
221 .rtems => .RTEMS,
222 .nacl => .NaCl,
223 .aix => .AIX,
224 .cuda => .CUDA,
225 .nvcl => .NVCL,
226 .amdhsa => .AMDHSA,
227 .ps4 => .PS4,
228 .ps5 => .PS5,
229 .elfiamcu => .ELFIAMCU,
230 .tvos => .TvOS,
231 .watchos => .WatchOS,
232 .mesa3d => .Mesa3D,
233 .contiki => .Contiki,
234 .amdpal => .AMDPAL,
235 .hermit => .HermitCore,
236 .hurd => .Hurd,
237 .wasi => .WASI,
238 .emscripten => .Emscripten,
239 .driverkit => .DriverKit,
240 .shadermodel => .ShaderModel,
241 };
242}
243
244pub fn targetArch(arch_tag: std.Target.Cpu.Arch) llvm.ArchType {
245 return switch (arch_tag) {
246 .arm => .arm,
247 .armeb => .armeb,
248 .aarch64 => .aarch64,
249 .aarch64_be => .aarch64_be,
250 .aarch64_32 => .aarch64_32,
251 .arc => .arc,
252 .avr => .avr,
253 .bpfel => .bpfel,
254 .bpfeb => .bpfeb,
255 .csky => .csky,
256 .dxil => .dxil,
257 .hexagon => .hexagon,
258 .loongarch32 => .loongarch32,
259 .loongarch64 => .loongarch64,
260 .m68k => .m68k,
261 .mips => .mips,
262 .mipsel => .mipsel,
263 .mips64 => .mips64,
264 .mips64el => .mips64el,
265 .msp430 => .msp430,
266 .powerpc => .ppc,
267 .powerpcle => .ppcle,
268 .powerpc64 => .ppc64,
269 .powerpc64le => .ppc64le,
270 .r600 => .r600,
271 .amdgcn => .amdgcn,
272 .riscv32 => .riscv32,
273 .riscv64 => .riscv64,
274 .sparc => .sparc,
275 .sparc64 => .sparcv9, // In LLVM, sparc64 == sparcv9.
276 .sparcel => .sparcel,
277 .s390x => .systemz,
278 .tce => .tce,
279 .tcele => .tcele,
280 .thumb => .thumb,
281 .thumbeb => .thumbeb,
282 .i386 => .x86,
283 .x86_64 => .x86_64,
284 .xcore => .xcore,
285 .nvptx => .nvptx,
286 .nvptx64 => .nvptx64,
287 .le32 => .le32,
288 .le64 => .le64,
289 .amdil => .amdil,
290 .amdil64 => .amdil64,
291 .hsail => .hsail,
292 .hsail64 => .hsail64,
293 .spir => .spir,
294 .spir64 => .spir64,
295 .kalimba => .kalimba,
296 .shave => .shave,
297 .lanai => .lanai,
298 .wasm32 => .wasm32,
299 .wasm64 => .wasm64,
300 .renderscript32 => .renderscript32,
301 .renderscript64 => .renderscript64,
302 .ve => .ve,
303 .spu_2, .spirv32, .spirv64 => .UnknownArch,
304 };
305}
306
307pub fn supportsTailCall(target: std.Target) bool {
308 switch (target.cpu.arch) {
309 .wasm32, .wasm64 => return std.Target.wasm.featureSetHas(target.cpu.features, .tail_call),
310 // Although these ISAs support tail calls, LLVM does not support tail calls on them.
311 .mips, .mipsel, .mips64, .mips64el => return false,
312 .powerpc, .powerpcle, .powerpc64, .powerpc64le => return false,
313 else => return true,
314 }
315}
316
201317pub const Object = struct {
202318 gpa: Allocator,
203319 module: *Module,
......@@ -4559,7 +4675,7 @@ pub const FuncGen = struct {
45594675 "",
45604676 );
45614677
4562 if (return_type.isNoReturn()) {
4678 if (return_type.isNoReturn() and attr != .AlwaysTail) {
45634679 _ = self.builder.buildUnreachable();
45644680 return null;
45654681 }
......@@ -8644,12 +8760,26 @@ pub const FuncGen = struct {
86448760 const union_llvm_ty = try self.dg.lowerType(union_ty);
86458761 const target = self.dg.module.getTarget();
86468762 const layout = union_ty.unionGetLayout(target);
8763 const union_obj = union_ty.cast(Type.Payload.Union).?.data;
8764 const tag_int = blk: {
8765 const tag_ty = union_ty.unionTagTypeHypothetical();
8766 const union_field_name = union_obj.fields.keys()[extra.field_index];
8767 const enum_field_index = tag_ty.enumFieldIndex(union_field_name).?;
8768 var tag_val_payload: Value.Payload.U32 = .{
8769 .base = .{ .tag = .enum_field_index },
8770 .data = @intCast(u32, enum_field_index),
8771 };
8772 const tag_val = Value.initPayload(&tag_val_payload.base);
8773 var int_payload: Value.Payload.U64 = undefined;
8774 const tag_int_val = tag_val.enumToInt(tag_ty, &int_payload);
8775 break :blk tag_int_val.toUnsignedInt(target);
8776 };
86478777 if (layout.payload_size == 0) {
86488778 if (layout.tag_size == 0) {
86498779 return null;
86508780 }
86518781 assert(!isByRef(union_ty));
8652 return union_llvm_ty.constInt(extra.field_index, .False);
8782 return union_llvm_ty.constInt(tag_int, .False);
86538783 }
86548784 assert(isByRef(union_ty));
86558785 // The llvm type of the alloca will the the named LLVM union type, which will not
......@@ -8658,7 +8788,6 @@ pub const FuncGen = struct {
86588788 // then set the fields appropriately.
86598789 const result_ptr = self.buildAlloca(union_llvm_ty);
86608790 const llvm_payload = try self.resolveInst(extra.init);
8661 const union_obj = union_ty.cast(Type.Payload.Union).?.data;
86628791 assert(union_obj.haveFieldTypes());
86638792 const field = union_obj.fields.values()[extra.field_index];
86648793 const field_llvm_ty = try self.dg.lowerType(field.ty);
......@@ -8742,7 +8871,7 @@ pub const FuncGen = struct {
87428871 };
87438872 const field_ptr = self.builder.buildInBoundsGEP(llvm_union_ty, casted_ptr, &indices, indices.len, "");
87448873 const tag_llvm_ty = try self.dg.lowerType(union_obj.tag_ty);
8745 const llvm_tag = tag_llvm_ty.constInt(extra.field_index, .False);
8874 const llvm_tag = tag_llvm_ty.constInt(tag_int, .False);
87468875 const store_inst = self.builder.buildStore(llvm_tag, field_ptr);
87478876 store_inst.setAlignment(union_obj.tag_ty.abiAlignment(target));
87488877 }
src/link.zig+7-6
......@@ -245,8 +245,8 @@ pub const File = struct {
245245
246246 pub const LinkBlock = union {
247247 elf: Elf.TextBlock,
248 coff: Coff.TextBlock,
249 macho: MachO.TextBlock,
248 coff: Coff.Atom,
249 macho: MachO.Atom,
250250 plan9: Plan9.DeclBlock,
251251 c: void,
252252 wasm: Wasm.DeclBlock,
......@@ -267,7 +267,7 @@ pub const File = struct {
267267
268268 pub const Export = union {
269269 elf: Elf.Export,
270 coff: void,
270 coff: Coff.Export,
271271 macho: MachO.Export,
272272 plan9: Plan9.Export,
273273 c: void,
......@@ -934,9 +934,10 @@ pub const File = struct {
934934 std.debug.print("\n", .{});
935935 }
936936
937 const llvm = @import("codegen/llvm/bindings.zig");
938 const os_type = @import("target.zig").osToLLVM(base.options.target.os.tag);
939 const bad = llvm.WriteArchive(full_out_path_z, object_files.items.ptr, object_files.items.len, os_type);
937 const llvm_bindings = @import("codegen/llvm/bindings.zig");
938 const llvm = @import("codegen/llvm.zig");
939 const os_tag = llvm.targetOs(base.options.target.os.tag);
940 const bad = llvm_bindings.WriteArchive(full_out_path_z, object_files.items.ptr, object_files.items.len, os_tag);
940941 if (bad) return error.UnableToWriteArchive;
941942
942943 if (!base.options.disable_lld_caching) {
src/link/Coff.zig+1247-1186
......@@ -1,131 +1,179 @@
11const Coff = @This();
22
33const std = @import("std");
4const build_options = @import("build_options");
45const builtin = @import("builtin");
5const log = std.log.scoped(.link);
6const Allocator = std.mem.Allocator;
76const assert = std.debug.assert;
8const fs = std.fs;
9const allocPrint = std.fmt.allocPrint;
7const coff = std.coff;
8const fmt = std.fmt;
9const log = std.log.scoped(.link);
10const math = std.math;
1011const mem = std.mem;
1112
12const lldMain = @import("../main.zig").lldMain;
13const trace = @import("../tracy.zig").trace;
14const Module = @import("../Module.zig");
15const Compilation = @import("../Compilation.zig");
13const Allocator = std.mem.Allocator;
14
1615const codegen = @import("../codegen.zig");
1716const link = @import("../link.zig");
18const build_options = @import("build_options");
19const Cache = @import("../Cache.zig");
20const mingw = @import("../mingw.zig");
17const lld = @import("Coff/lld.zig");
18const trace = @import("../tracy.zig").trace;
19
2120const Air = @import("../Air.zig");
21pub const Atom = @import("Coff/Atom.zig");
22const Compilation = @import("../Compilation.zig");
2223const Liveness = @import("../Liveness.zig");
2324const LlvmObject = @import("../codegen/llvm.zig").Object;
25const Module = @import("../Module.zig");
26const Object = @import("Coff/Object.zig");
27const StringTable = @import("strtab.zig").StringTable;
2428const TypedValue = @import("../TypedValue.zig");
2529
26const allocation_padding = 4 / 3;
27const minimum_text_block_size = 64 * allocation_padding;
28
29const section_alignment = 4096;
30const file_alignment = 512;
31const default_image_base = 0x400_000;
32const section_table_size = 2 * 40;
33comptime {
34 assert(mem.isAligned(default_image_base, section_alignment));
35}
36
3730pub const base_tag: link.File.Tag = .coff;
3831
3932const msdos_stub = @embedFile("msdos-stub.bin");
33const N_DATA_DIRS: u5 = 16;
4034
4135/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
4236llvm_object: ?*LlvmObject = null,
4337
4438base: link.File,
45ptr_width: PtrWidth,
4639error_flags: link.File.ErrorFlags = .{},
4740
48text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},
49last_text_block: ?*TextBlock = null,
50
51/// Section table file pointer.
52section_table_offset: u32 = 0,
53/// Section data file pointer.
54section_data_offset: u32 = 0,
55/// Optional header file pointer.
56optional_header_offset: u32 = 0,
57
58/// Absolute virtual address of the offset table when the executable is loaded in memory.
59offset_table_virtual_address: u32 = 0,
60/// Current size of the offset table on disk, must be a multiple of `file_alignment`
61offset_table_size: u32 = 0,
62/// Contains absolute virtual addresses
63offset_table: std.ArrayListUnmanaged(u64) = .{},
64/// Free list of offset table indices
65offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
41ptr_width: PtrWidth,
42page_size: u32,
43
44objects: std.ArrayListUnmanaged(Object) = .{},
45
46sections: std.MultiArrayList(Section) = .{},
47data_directories: [N_DATA_DIRS]coff.ImageDataDirectory,
48
49text_section_index: ?u16 = null,
50got_section_index: ?u16 = null,
51rdata_section_index: ?u16 = null,
52data_section_index: ?u16 = null,
53
54locals: std.ArrayListUnmanaged(coff.Symbol) = .{},
55globals: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{},
56
57locals_free_list: std.ArrayListUnmanaged(u32) = .{},
58
59strtab: StringTable(.strtab) = .{},
60strtab_offset: ?u32 = null,
61
62got_entries: std.AutoArrayHashMapUnmanaged(SymbolWithLoc, u32) = .{},
63got_entries_free_list: std.ArrayListUnmanaged(u32) = .{},
6664
6765/// Virtual address of the entry point procedure relative to image base.
6866entry_addr: ?u32 = null,
6967
70/// Absolute virtual address of the text section when the executable is loaded in memory.
71text_section_virtual_address: u32 = 0,
72/// Current size of the `.text` section on disk, must be a multiple of `file_alignment`
73text_section_size: u32 = 0,
68/// Table of Decls that are currently alive.
69/// We store them here so that we can properly dispose of any allocated
70/// memory within the atom in the incremental linker.
71/// TODO consolidate this.
72decls: std.AutoHashMapUnmanaged(Module.Decl.Index, ?u16) = .{},
73
74/// List of atoms that are either synthetic or map directly to the Zig source program.
75managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
76
77/// Table of atoms indexed by the symbol index.
78atom_by_index_table: std.AutoHashMapUnmanaged(u32, *Atom) = .{},
79
80/// Table of unnamed constants associated with a parent `Decl`.
81/// We store them here so that we can free the constants whenever the `Decl`
82/// needs updating or is freed.
83///
84/// For example,
85///
86/// ```zig
87/// const Foo = struct{
88/// a: u8,
89/// };
90///
91/// pub fn main() void {
92/// var foo = Foo{ .a = 1 };
93/// _ = foo;
94/// }
95/// ```
96///
97/// value assigned to label `foo` is an unnamed constant belonging/associated
98/// with `Decl` `main`, and lives as long as that `Decl`.
99unnamed_const_atoms: UnnamedConstTable = .{},
100
101/// A table of relocations indexed by the owning them `TextBlock`.
102/// Note that once we refactor `TextBlock`'s lifetime and ownership rules,
103/// this will be a table indexed by index into the list of Atoms.
104relocs: RelocTable = .{},
105
106pub const Reloc = struct {
107 @"type": enum {
108 got,
109 direct,
110 },
111 target: SymbolWithLoc,
112 offset: u32,
113 addend: u32,
114 pcrel: bool,
115 length: u2,
116 prev_vaddr: u32,
117};
74118
75offset_table_size_dirty: bool = false,
76text_section_size_dirty: bool = false,
77/// This flag is set when the virtual size of the whole image file when loaded in memory has changed
78/// and needs to be updated in the optional header.
79size_of_image_dirty: bool = false,
119const RelocTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Reloc));
120const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(*Atom));
121
122const default_file_alignment: u16 = 0x200;
123const default_image_base_dll: u64 = 0x10000000;
124const default_image_base_exe: u64 = 0x400000;
125const default_size_of_stack_reserve: u32 = 0x1000000;
126const default_size_of_stack_commit: u32 = 0x1000;
127const default_size_of_heap_reserve: u32 = 0x100000;
128const default_size_of_heap_commit: u32 = 0x1000;
129
130const Section = struct {
131 header: coff.SectionHeader,
132
133 last_atom: ?*Atom = null,
134
135 /// A list of atoms that have surplus capacity. This list can have false
136 /// positives, as functions grow and shrink over time, only sometimes being added
137 /// or removed from the freelist.
138 ///
139 /// An atom has surplus capacity when its overcapacity value is greater than
140 /// padToIdeal(minimum_atom_size). That is, when it has so
141 /// much extra capacity, that we could fit a small new symbol in it, itself with
142 /// ideal_capacity or more.
143 ///
144 /// Ideal capacity is defined by size + (size / ideal_factor).
145 ///
146 /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
147 /// overcapacity can be negative. A simple way to have negative overcapacity is to
148 /// allocate a fresh atom, which will have ideal capacity, and then grow it
149 /// by 1 byte. It will then have -1 overcapacity.
150 free_list: std.ArrayListUnmanaged(*Atom) = .{},
151};
80152
81153pub const PtrWidth = enum { p32, p64 };
154pub const SrcFn = void;
82155
83pub const TextBlock = struct {
84 /// Offset of the code relative to the start of the text section
85 text_offset: u32,
86 /// Used size of the text block
87 size: u32,
88 /// This field is undefined for symbols with size = 0.
89 offset_table_index: u32,
90 /// Points to the previous and next neighbors, based on the `text_offset`.
91 /// This can be used to find, for example, the capacity of this `TextBlock`.
92 prev: ?*TextBlock,
93 next: ?*TextBlock,
94
95 pub const empty = TextBlock{
96 .text_offset = 0,
97 .size = 0,
98 .offset_table_index = undefined,
99 .prev = null,
100 .next = null,
101 };
102
103 /// Returns how much room there is to grow in virtual address space.
104 fn capacity(self: TextBlock) u64 {
105 if (self.next) |next| {
106 return next.text_offset - self.text_offset;
107 }
108 // This is the last block, the capacity is only limited by the address space.
109 return std.math.maxInt(u32) - self.text_offset;
110 }
156pub const Export = struct {
157 sym_index: ?u32 = null,
158};
111159
112 fn freeListEligible(self: TextBlock) bool {
113 // No need to keep a free list node for the last block.
114 const next = self.next orelse return false;
115 const cap = next.text_offset - self.text_offset;
116 const ideal_cap = self.size * allocation_padding;
117 if (cap <= ideal_cap) return false;
118 const surplus = cap - ideal_cap;
119 return surplus >= minimum_text_block_size;
120 }
160pub const SymbolWithLoc = struct {
161 // Index into the respective symbol table.
162 sym_index: u32,
121163
122 /// Absolute virtual address of the text block when the file is loaded in memory.
123 fn getVAddr(self: TextBlock, coff: Coff) u32 {
124 return coff.text_section_virtual_address + self.text_offset;
125 }
164 // null means it's a synthetic global or Zig source.
165 file: ?u32 = null,
126166};
127167
128pub const SrcFn = void;
168/// When allocating, the ideal_capacity is calculated by
169/// actual_capacity + (actual_capacity / ideal_factor)
170const ideal_factor = 3;
171
172/// In order for a slice of bytes to be considered eligible to keep metadata pointing at
173/// it as a possible place to put new symbols, it must have enough room for this many bytes
174/// (plus extra for reserved capacity).
175const minimum_text_block_size = 64;
176pub const min_text_capacity = padToIdeal(minimum_text_block_size);
129177
130178pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Coff {
131179 assert(options.target.ofmt == .coff);
......@@ -144,257 +192,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
144192 });
145193 self.base.file = file;
146194
147 // TODO Write object specific relocations, COFF symbol table, then enable object file output.
148 switch (options.output_mode) {
149 .Exe => {},
150 .Obj => return error.TODOImplementWritingObjFiles,
151 .Lib => return error.TODOImplementWritingLibFiles,
152 }
153
154 var coff_file_header_offset: u32 = 0;
155 if (options.output_mode == .Exe) {
156 // Write the MS-DOS stub and the PE signature
157 try self.base.file.?.pwriteAll(msdos_stub ++ "PE\x00\x00", 0);
158 coff_file_header_offset = msdos_stub.len + 4;
159 }
160
161 // COFF file header
162 const data_directory_count = 0;
163 var hdr_data: [112 + data_directory_count * 8 + section_table_size]u8 = undefined;
164 var index: usize = 0;
165
166 const machine = self.base.options.target.cpu.arch.toCoffMachine();
167 if (machine == .Unknown) {
168 return error.UnsupportedCOFFArchitecture;
169 }
170 mem.writeIntLittle(u16, hdr_data[0..2], @enumToInt(machine));
171 index += 2;
172
173 // Number of sections (we only use .got, .text)
174 mem.writeIntLittle(u16, hdr_data[index..][0..2], 2);
175 index += 2;
176 // TimeDateStamp (u32), PointerToSymbolTable (u32), NumberOfSymbols (u32)
177 mem.set(u8, hdr_data[index..][0..12], 0);
178 index += 12;
179
180 const optional_header_size = switch (options.output_mode) {
181 .Exe => data_directory_count * 8 + switch (self.ptr_width) {
182 .p32 => @as(u16, 96),
183 .p64 => 112,
184 },
185 else => 0,
186 };
187
188 const section_table_offset = coff_file_header_offset + 20 + optional_header_size;
189 const default_offset_table_size = file_alignment;
190 const default_size_of_code = 0;
191
192 self.section_data_offset = mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, file_alignment);
193 const section_data_relative_virtual_address = mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, section_alignment);
194 self.offset_table_virtual_address = default_image_base + section_data_relative_virtual_address;
195 self.offset_table_size = default_offset_table_size;
196 self.section_table_offset = section_table_offset;
197 self.text_section_virtual_address = default_image_base + section_data_relative_virtual_address + section_alignment;
198 self.text_section_size = default_size_of_code;
199
200 // Size of file when loaded in memory
201 const size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - default_image_base + default_size_of_code, section_alignment);
202
203 mem.writeIntLittle(u16, hdr_data[index..][0..2], optional_header_size);
204 index += 2;
205
206 // Characteristics
207 var characteristics: std.coff.CoffHeaderFlags = .{
208 .DEBUG_STRIPPED = 1, // TODO remove debug info stripped flag when necessary
209 .RELOCS_STRIPPED = 1,
210 };
211 if (options.output_mode == .Exe) {
212 characteristics.EXECUTABLE_IMAGE = 1;
213 }
214 switch (self.ptr_width) {
215 .p32 => characteristics.@"32BIT_MACHINE" = 1,
216 .p64 => characteristics.LARGE_ADDRESS_AWARE = 1,
217 }
218 mem.writeIntLittle(u16, hdr_data[index..][0..2], @bitCast(u16, characteristics));
219 index += 2;
220
221 assert(index == 20);
222 try self.base.file.?.pwriteAll(hdr_data[0..index], coff_file_header_offset);
223
224 if (options.output_mode == .Exe) {
225 self.optional_header_offset = coff_file_header_offset + 20;
226 // Optional header
227 index = 0;
228 mem.writeIntLittle(u16, hdr_data[0..2], switch (self.ptr_width) {
229 .p32 => @as(u16, 0x10b),
230 .p64 => 0x20b,
231 });
232 index += 2;
233
234 // Linker version (u8 + u8)
235 mem.set(u8, hdr_data[index..][0..2], 0);
236 index += 2;
237
238 // SizeOfCode (UNUSED, u32), SizeOfInitializedData (u32), SizeOfUninitializedData (u32), AddressOfEntryPoint (u32), BaseOfCode (UNUSED, u32)
239 mem.set(u8, hdr_data[index..][0..20], 0);
240 index += 20;
241
242 if (self.ptr_width == .p32) {
243 // Base of data relative to the image base (UNUSED)
244 mem.set(u8, hdr_data[index..][0..4], 0);
245 index += 4;
246
247 // Image base address
248 mem.writeIntLittle(u32, hdr_data[index..][0..4], default_image_base);
249 index += 4;
250 } else {
251 // Image base address
252 mem.writeIntLittle(u64, hdr_data[index..][0..8], default_image_base);
253 index += 8;
254 }
255
256 // Section alignment
257 mem.writeIntLittle(u32, hdr_data[index..][0..4], section_alignment);
258 index += 4;
259 // File alignment
260 mem.writeIntLittle(u32, hdr_data[index..][0..4], file_alignment);
261 index += 4;
262 // Required OS version, 6.0 is vista
263 mem.writeIntLittle(u16, hdr_data[index..][0..2], 6);
264 index += 2;
265 mem.writeIntLittle(u16, hdr_data[index..][0..2], 0);
266 index += 2;
267 // Image version
268 mem.set(u8, hdr_data[index..][0..4], 0);
269 index += 4;
270 // Required subsystem version, same as OS version
271 mem.writeIntLittle(u16, hdr_data[index..][0..2], 6);
272 index += 2;
273 mem.writeIntLittle(u16, hdr_data[index..][0..2], 0);
274 index += 2;
275 // Reserved zeroes (u32)
276 mem.set(u8, hdr_data[index..][0..4], 0);
277 index += 4;
278 mem.writeIntLittle(u32, hdr_data[index..][0..4], size_of_image);
279 index += 4;
280 mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset);
281 index += 4;
282 // CheckSum (u32)
283 mem.set(u8, hdr_data[index..][0..4], 0);
284 index += 4;
285 // Subsystem, TODO: Let users specify the subsystem, always CUI for now
286 mem.writeIntLittle(u16, hdr_data[index..][0..2], 3);
287 index += 2;
288 // DLL characteristics
289 mem.writeIntLittle(u16, hdr_data[index..][0..2], 0x0);
290 index += 2;
291
292 switch (self.ptr_width) {
293 .p32 => {
294 // Size of stack reserve + commit
295 mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000_000);
296 index += 4;
297 mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000);
298 index += 4;
299 // Size of heap reserve + commit
300 mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x100_000);
301 index += 4;
302 mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000);
303 index += 4;
304 },
305 .p64 => {
306 // Size of stack reserve + commit
307 mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000_000);
308 index += 8;
309 mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000);
310 index += 8;
311 // Size of heap reserve + commit
312 mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x100_000);
313 index += 8;
314 mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000);
315 index += 8;
316 },
317 }
318
319 // Reserved zeroes
320 mem.set(u8, hdr_data[index..][0..4], 0);
321 index += 4;
322
323 // Number of data directories
324 mem.writeIntLittle(u32, hdr_data[index..][0..4], data_directory_count);
325 index += 4;
326 // Initialize data directories to zero
327 mem.set(u8, hdr_data[index..][0 .. data_directory_count * 8], 0);
328 index += data_directory_count * 8;
329
330 assert(index == optional_header_size);
331 }
332
333 // Write section table.
334 // First, the .got section
335 hdr_data[index..][0..8].* = ".got\x00\x00\x00\x00".*;
336 index += 8;
337 if (options.output_mode == .Exe) {
338 // Virtual size (u32)
339 mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size);
340 index += 4;
341 // Virtual address (u32)
342 mem.writeIntLittle(u32, hdr_data[index..][0..4], self.offset_table_virtual_address - default_image_base);
343 index += 4;
344 } else {
345 mem.set(u8, hdr_data[index..][0..8], 0);
346 index += 8;
347 }
348 // Size of raw data (u32)
349 mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size);
350 index += 4;
351 // File pointer to the start of the section
352 mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset);
353 index += 4;
354 // Pointer to relocations (u32), PointerToLinenumbers (u32), NumberOfRelocations (u16), NumberOfLinenumbers (u16)
355 mem.set(u8, hdr_data[index..][0..12], 0);
356 index += 12;
357 // Section flags
358 mem.writeIntLittle(u32, hdr_data[index..][0..4], @bitCast(u32, std.coff.SectionHeaderFlags{
359 .CNT_INITIALIZED_DATA = 1,
360 .MEM_READ = 1,
361 }));
362 index += 4;
363 // Then, the .text section
364 hdr_data[index..][0..8].* = ".text\x00\x00\x00".*;
365 index += 8;
366 if (options.output_mode == .Exe) {
367 // Virtual size (u32)
368 mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code);
369 index += 4;
370 // Virtual address (u32)
371 mem.writeIntLittle(u32, hdr_data[index..][0..4], self.text_section_virtual_address - default_image_base);
372 index += 4;
373 } else {
374 mem.set(u8, hdr_data[index..][0..8], 0);
375 index += 8;
376 }
377 // Size of raw data (u32)
378 mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code);
379 index += 4;
380 // File pointer to the start of the section
381 mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset + default_offset_table_size);
382 index += 4;
383 // Pointer to relocations (u32), PointerToLinenumbers (u32), NumberOfRelocations (u16), NumberOfLinenumbers (u16)
384 mem.set(u8, hdr_data[index..][0..12], 0);
385 index += 12;
386 // Section flags
387 mem.writeIntLittle(u32, hdr_data[index..][0..4], @bitCast(u32, std.coff.SectionHeaderFlags{
388 .CNT_CODE = 1,
389 .MEM_EXECUTE = 1,
390 .MEM_READ = 1,
391 .MEM_WRITE = 1,
392 }));
393 index += 4;
394
395 assert(index == optional_header_size + section_table_size);
396 try self.base.file.?.pwriteAll(hdr_data[0..index], self.optional_header_offset);
397 try self.base.file.?.setEndPos(self.section_data_offset + default_offset_table_size + default_size_of_code);
195 try self.populateMissingMetadata();
398196
399197 return self;
400198}
......@@ -405,6 +203,9 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff {
405203 33...64 => .p64,
406204 else => return error.UnsupportedCOFFArchitecture,
407205 };
206 const page_size: u32 = switch (options.target.cpu.arch) {
207 else => 0x1000,
208 };
408209 const self = try gpa.create(Coff);
409210 errdefer gpa.destroy(self);
410211 self.* = .{
......@@ -415,6 +216,8 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff {
415216 .file = null,
416217 },
417218 .ptr_width = ptr_width,
219 .page_size = page_size,
220 .data_directories = comptime mem.zeroes([N_DATA_DIRS]coff.ImageDataDirectory),
418221 };
419222
420223 const use_llvm = build_options.have_llvm and options.use_llvm;
......@@ -425,245 +228,530 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff {
425228 return self;
426229}
427230
428pub fn allocateDeclIndexes(self: *Coff, decl_index: Module.Decl.Index) !void {
429 if (self.llvm_object) |_| return;
231pub fn deinit(self: *Coff) void {
232 const gpa = self.base.allocator;
233
234 if (build_options.have_llvm) {
235 if (self.llvm_object) |llvm_object| llvm_object.destroy(gpa);
236 }
430237
431 try self.offset_table.ensureUnusedCapacity(self.base.allocator, 1);
238 for (self.objects.items) |*object| {
239 object.deinit(gpa);
240 }
241 self.objects.deinit(gpa);
432242
433 const decl = self.base.options.module.?.declPtr(decl_index);
434 if (self.offset_table_free_list.popOrNull()) |i| {
435 decl.link.coff.offset_table_index = i;
436 } else {
437 decl.link.coff.offset_table_index = @intCast(u32, self.offset_table.items.len);
438 _ = self.offset_table.addOneAssumeCapacity();
243 for (self.sections.items(.free_list)) |*free_list| {
244 free_list.deinit(gpa);
245 }
246 self.sections.deinit(gpa);
247
248 for (self.managed_atoms.items) |atom| {
249 gpa.destroy(atom);
250 }
251 self.managed_atoms.deinit(gpa);
439252
440 const entry_size = self.base.options.target.cpu.arch.ptrBitWidth() / 8;
441 if (self.offset_table.items.len > self.offset_table_size / entry_size) {
442 self.offset_table_size_dirty = true;
253 self.locals.deinit(gpa);
254 self.globals.deinit(gpa);
255 self.locals_free_list.deinit(gpa);
256 self.strtab.deinit(gpa);
257 self.got_entries.deinit(gpa);
258 self.got_entries_free_list.deinit(gpa);
259 self.decls.deinit(gpa);
260 self.atom_by_index_table.deinit(gpa);
261
262 {
263 var it = self.unnamed_const_atoms.valueIterator();
264 while (it.next()) |atoms| {
265 atoms.deinit(gpa);
443266 }
267 self.unnamed_const_atoms.deinit(gpa);
444268 }
445269
446 self.offset_table.items[decl.link.coff.offset_table_index] = 0;
270 {
271 var it = self.relocs.valueIterator();
272 while (it.next()) |relocs| {
273 relocs.deinit(gpa);
274 }
275 self.relocs.deinit(gpa);
276 }
447277}
448278
449fn allocateTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
450 const new_block_min_capacity = new_block_size * allocation_padding;
279fn populateMissingMetadata(self: *Coff) !void {
280 assert(self.llvm_object == null);
281 const gpa = self.base.allocator;
282
283 if (self.text_section_index == null) {
284 self.text_section_index = @intCast(u16, self.sections.slice().len);
285 const file_size = @intCast(u32, self.base.options.program_code_size_hint);
286 const off = self.findFreeSpace(file_size, self.page_size); // TODO we are over-aligning in file; we should track both in file and in memory pointers
287 log.debug("found .text free space 0x{x} to 0x{x}", .{ off, off + file_size });
288 var header = coff.SectionHeader{
289 .name = undefined,
290 .virtual_size = file_size,
291 .virtual_address = off,
292 .size_of_raw_data = file_size,
293 .pointer_to_raw_data = off,
294 .pointer_to_relocations = 0,
295 .pointer_to_linenumbers = 0,
296 .number_of_relocations = 0,
297 .number_of_linenumbers = 0,
298 .flags = .{
299 .CNT_CODE = 1,
300 .MEM_EXECUTE = 1,
301 .MEM_READ = 1,
302 },
303 };
304 try self.setSectionName(&header, ".text");
305 try self.sections.append(gpa, .{ .header = header });
306 }
307
308 if (self.got_section_index == null) {
309 self.got_section_index = @intCast(u16, self.sections.slice().len);
310 const file_size = @intCast(u32, self.base.options.symbol_count_hint);
311 const off = self.findFreeSpace(file_size, self.page_size);
312 log.debug("found .got free space 0x{x} to 0x{x}", .{ off, off + file_size });
313 var header = coff.SectionHeader{
314 .name = undefined,
315 .virtual_size = file_size,
316 .virtual_address = off,
317 .size_of_raw_data = file_size,
318 .pointer_to_raw_data = off,
319 .pointer_to_relocations = 0,
320 .pointer_to_linenumbers = 0,
321 .number_of_relocations = 0,
322 .number_of_linenumbers = 0,
323 .flags = .{
324 .CNT_INITIALIZED_DATA = 1,
325 .MEM_READ = 1,
326 },
327 };
328 try self.setSectionName(&header, ".got");
329 try self.sections.append(gpa, .{ .header = header });
330 }
331
332 if (self.rdata_section_index == null) {
333 self.rdata_section_index = @intCast(u16, self.sections.slice().len);
334 const file_size: u32 = 1024;
335 const off = self.findFreeSpace(file_size, self.page_size);
336 log.debug("found .rdata free space 0x{x} to 0x{x}", .{ off, off + file_size });
337 var header = coff.SectionHeader{
338 .name = undefined,
339 .virtual_size = file_size,
340 .virtual_address = off,
341 .size_of_raw_data = file_size,
342 .pointer_to_raw_data = off,
343 .pointer_to_relocations = 0,
344 .pointer_to_linenumbers = 0,
345 .number_of_relocations = 0,
346 .number_of_linenumbers = 0,
347 .flags = .{
348 .CNT_INITIALIZED_DATA = 1,
349 .MEM_READ = 1,
350 },
351 };
352 try self.setSectionName(&header, ".rdata");
353 try self.sections.append(gpa, .{ .header = header });
354 }
355
356 if (self.data_section_index == null) {
357 self.data_section_index = @intCast(u16, self.sections.slice().len);
358 const file_size: u32 = 1024;
359 const off = self.findFreeSpace(file_size, self.page_size);
360 log.debug("found .data free space 0x{x} to 0x{x}", .{ off, off + file_size });
361 var header = coff.SectionHeader{
362 .name = undefined,
363 .virtual_size = file_size,
364 .virtual_address = off,
365 .size_of_raw_data = file_size,
366 .pointer_to_raw_data = off,
367 .pointer_to_relocations = 0,
368 .pointer_to_linenumbers = 0,
369 .number_of_relocations = 0,
370 .number_of_linenumbers = 0,
371 .flags = .{
372 .CNT_INITIALIZED_DATA = 1,
373 .MEM_READ = 1,
374 .MEM_WRITE = 1,
375 },
376 };
377 try self.setSectionName(&header, ".data");
378 try self.sections.append(gpa, .{ .header = header });
379 }
451380
452 // We use these to indicate our intention to update metadata, placing the new block,
381 if (self.strtab_offset == null) {
382 try self.strtab.buffer.append(gpa, 0);
383 self.strtab_offset = self.findFreeSpace(@intCast(u32, self.strtab.len()), 1);
384 log.debug("found strtab free space 0x{x} to 0x{x}", .{ self.strtab_offset.?, self.strtab_offset.? + self.strtab.len() });
385 }
386
387 // Index 0 is always a null symbol.
388 try self.locals.append(gpa, .{
389 .name = [_]u8{0} ** 8,
390 .value = 0,
391 .section_number = @intToEnum(coff.SectionNumber, 0),
392 .@"type" = .{ .base_type = .NULL, .complex_type = .NULL },
393 .storage_class = .NULL,
394 .number_of_aux_symbols = 0,
395 });
396
397 {
398 // We need to find out what the max file offset is according to section headers.
399 // Otherwise, we may end up with an COFF binary with file size not matching the final section's
400 // offset + it's filesize.
401 // TODO I don't like this here one bit
402 var max_file_offset: u64 = 0;
403 for (self.sections.items(.header)) |header| {
404 if (header.pointer_to_raw_data + header.size_of_raw_data > max_file_offset) {
405 max_file_offset = header.pointer_to_raw_data + header.size_of_raw_data;
406 }
407 }
408 try self.base.file.?.pwriteAll(&[_]u8{0}, max_file_offset);
409 }
410}
411
412pub fn allocateDeclIndexes(self: *Coff, decl_index: Module.Decl.Index) !void {
413 if (self.llvm_object) |_| return;
414 const decl = self.base.options.module.?.declPtr(decl_index);
415 if (decl.link.coff.sym_index != 0) return;
416 decl.link.coff.sym_index = try self.allocateSymbol();
417 const gpa = self.base.allocator;
418 try self.atom_by_index_table.putNoClobber(gpa, decl.link.coff.sym_index, &decl.link.coff);
419 try self.decls.putNoClobber(gpa, decl_index, null);
420}
421
422fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u32 {
423 const tracy = trace(@src());
424 defer tracy.end();
425
426 const sect_id = @enumToInt(atom.getSymbol(self).section_number) - 1;
427 const header = &self.sections.items(.header)[sect_id];
428 const free_list = &self.sections.items(.free_list)[sect_id];
429 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];
430 const new_atom_ideal_capacity = if (header.isCode()) padToIdeal(new_atom_size) else new_atom_size;
431
432 // We use these to indicate our intention to update metadata, placing the new atom,
453433 // and possibly removing a free list node.
454434 // It would be simpler to do it inside the for loop below, but that would cause a
455435 // problem if an error was returned later in the function. So this action
456436 // is actually carried out at the end of the function, when errors are no longer possible.
457 var block_placement: ?*TextBlock = null;
437 var atom_placement: ?*Atom = null;
458438 var free_list_removal: ?usize = null;
459439
460 const vaddr = blk: {
440 // First we look for an appropriately sized free list node.
441 // The list is unordered. We'll just take the first thing that works.
442 var vaddr = blk: {
461443 var i: usize = 0;
462 while (i < self.text_block_free_list.items.len) {
463 const free_block = self.text_block_free_list.items[i];
464
465 const next_block_text_offset = free_block.text_offset + free_block.capacity();
466 const new_block_text_offset = mem.alignForwardGeneric(u64, free_block.getVAddr(self.*) + free_block.size, alignment) - self.text_section_virtual_address;
467 if (new_block_text_offset < next_block_text_offset and next_block_text_offset - new_block_text_offset >= new_block_min_capacity) {
468 block_placement = free_block;
469
470 const remaining_capacity = next_block_text_offset - new_block_text_offset - new_block_min_capacity;
471 if (remaining_capacity < minimum_text_block_size) {
472 free_list_removal = i;
473 }
474
475 break :blk new_block_text_offset + self.text_section_virtual_address;
476 } else {
477 if (!free_block.freeListEligible()) {
478 _ = self.text_block_free_list.swapRemove(i);
444 while (i < free_list.items.len) {
445 const big_atom = free_list.items[i];
446 // We now have a pointer to a live atom that has too much capacity.
447 // Is it enough that we could fit this new atom?
448 const sym = big_atom.getSymbol(self);
449 const capacity = big_atom.capacity(self);
450 const ideal_capacity = if (header.isCode()) padToIdeal(capacity) else capacity;
451 const ideal_capacity_end_vaddr = math.add(u32, sym.value, ideal_capacity) catch ideal_capacity;
452 const capacity_end_vaddr = sym.value + capacity;
453 const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity;
454 const new_start_vaddr = mem.alignBackwardGeneric(u32, new_start_vaddr_unaligned, alignment);
455 if (new_start_vaddr < ideal_capacity_end_vaddr) {
456 // Additional bookkeeping here to notice if this free list node
457 // should be deleted because the atom that it points to has grown to take up
458 // more of the extra capacity.
459 if (!big_atom.freeListEligible(self)) {
460 _ = free_list.swapRemove(i);
479461 } else {
480462 i += 1;
481463 }
482464 continue;
483465 }
484 } else if (self.last_text_block) |last| {
485 const new_block_vaddr = mem.alignForwardGeneric(u64, last.getVAddr(self.*) + last.size, alignment);
486 block_placement = last;
487 break :blk new_block_vaddr;
466 // At this point we know that we will place the new atom here. But the
467 // remaining question is whether there is still yet enough capacity left
468 // over for there to still be a free list node.
469 const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr;
470 const keep_free_list_node = remaining_capacity >= min_text_capacity;
471
472 // Set up the metadata to be updated, after errors are no longer possible.
473 atom_placement = big_atom;
474 if (!keep_free_list_node) {
475 free_list_removal = i;
476 }
477 break :blk new_start_vaddr;
478 } else if (maybe_last_atom.*) |last| {
479 const last_symbol = last.getSymbol(self);
480 const ideal_capacity = if (header.isCode()) padToIdeal(last.size) else last.size;
481 const ideal_capacity_end_vaddr = last_symbol.value + ideal_capacity;
482 const new_start_vaddr = mem.alignForwardGeneric(u32, ideal_capacity_end_vaddr, alignment);
483 atom_placement = last;
484 break :blk new_start_vaddr;
488485 } else {
489 break :blk self.text_section_virtual_address;
486 break :blk mem.alignForwardGeneric(u32, header.virtual_address, alignment);
490487 }
491488 };
492489
493 const expand_text_section = block_placement == null or block_placement.?.next == null;
494 if (expand_text_section) {
495 const needed_size = @intCast(u32, mem.alignForwardGeneric(u64, vaddr + new_block_size - self.text_section_virtual_address, file_alignment));
496 if (needed_size > self.text_section_size) {
497 const current_text_section_virtual_size = mem.alignForwardGeneric(u32, self.text_section_size, section_alignment);
498 const new_text_section_virtual_size = mem.alignForwardGeneric(u32, needed_size, section_alignment);
499 if (current_text_section_virtual_size != new_text_section_virtual_size) {
500 self.size_of_image_dirty = true;
501 // Write new virtual size
502 var buf: [4]u8 = undefined;
503 mem.writeIntLittle(u32, &buf, new_text_section_virtual_size);
504 try self.base.file.?.pwriteAll(&buf, self.section_table_offset + 40 + 8);
505 }
506
507 self.text_section_size = needed_size;
508 self.text_section_size_dirty = true;
490 const expand_section = atom_placement == null or atom_placement.?.next == null;
491 if (expand_section) {
492 const sect_capacity = self.allocatedSize(header.pointer_to_raw_data);
493 const needed_size: u32 = (vaddr + new_atom_size) - header.virtual_address;
494 if (needed_size > sect_capacity) {
495 @panic("TODO move section");
509496 }
510 self.last_text_block = text_block;
497 maybe_last_atom.* = atom;
498 // header.virtual_size = needed_size;
499 // header.size_of_raw_data = mem.alignForwardGeneric(u32, needed_size, default_file_alignment);
511500 }
512 text_block.text_offset = @intCast(u32, vaddr - self.text_section_virtual_address);
513 text_block.size = @intCast(u32, new_block_size);
514501
515 // This function can also reallocate a text block.
516 // In this case we need to "unplug" it from its previous location before
517 // plugging it in to its new location.
518 if (text_block.prev) |prev| {
519 prev.next = text_block.next;
502 // if (header.getAlignment().? < alignment) {
503 // header.setAlignment(alignment);
504 // }
505 atom.size = new_atom_size;
506 atom.alignment = alignment;
507
508 if (atom.prev) |prev| {
509 prev.next = atom.next;
520510 }
521 if (text_block.next) |next| {
522 next.prev = text_block.prev;
511 if (atom.next) |next| {
512 next.prev = atom.prev;
523513 }
524514
525 if (block_placement) |big_block| {
526 text_block.prev = big_block;
527 text_block.next = big_block.next;
528 big_block.next = text_block;
515 if (atom_placement) |big_atom| {
516 atom.prev = big_atom;
517 atom.next = big_atom.next;
518 big_atom.next = atom;
529519 } else {
530 text_block.prev = null;
531 text_block.next = null;
520 atom.prev = null;
521 atom.next = null;
532522 }
533523 if (free_list_removal) |i| {
534 _ = self.text_block_free_list.swapRemove(i);
524 _ = free_list.swapRemove(i);
535525 }
526
536527 return vaddr;
537528}
538529
539fn growTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
540 const block_vaddr = text_block.getVAddr(self.*);
541 const align_ok = mem.alignBackwardGeneric(u64, block_vaddr, alignment) == block_vaddr;
542 const need_realloc = !align_ok or new_block_size > text_block.capacity();
543 if (!need_realloc) return @as(u64, block_vaddr);
544 return self.allocateTextBlock(text_block, new_block_size, alignment);
530fn allocateSymbol(self: *Coff) !u32 {
531 const gpa = self.base.allocator;
532 try self.locals.ensureUnusedCapacity(gpa, 1);
533
534 const index = blk: {
535 if (self.locals_free_list.popOrNull()) |index| {
536 log.debug(" (reusing symbol index {d})", .{index});
537 break :blk index;
538 } else {
539 log.debug(" (allocating symbol index {d})", .{self.locals.items.len});
540 const index = @intCast(u32, self.locals.items.len);
541 _ = self.locals.addOneAssumeCapacity();
542 break :blk index;
543 }
544 };
545
546 self.locals.items[index] = .{
547 .name = [_]u8{0} ** 8,
548 .value = 0,
549 .section_number = @intToEnum(coff.SectionNumber, 0),
550 .@"type" = .{ .base_type = .NULL, .complex_type = .NULL },
551 .storage_class = .NULL,
552 .number_of_aux_symbols = 0,
553 };
554
555 return index;
545556}
546557
547fn shrinkTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64) void {
548 text_block.size = @intCast(u32, new_block_size);
549 if (text_block.capacity() - text_block.size >= minimum_text_block_size) {
550 self.text_block_free_list.append(self.base.allocator, text_block) catch {};
558pub fn allocateGotEntry(self: *Coff, target: SymbolWithLoc) !u32 {
559 const gpa = self.base.allocator;
560 try self.got_entries.ensureUnusedCapacity(gpa, 1);
561 const index: u32 = blk: {
562 if (self.got_entries_free_list.popOrNull()) |index| {
563 log.debug(" (reusing GOT entry index {d})", .{index});
564 if (self.got_entries.getIndex(target)) |existing| {
565 assert(existing == index);
566 }
567 break :blk index;
568 } else {
569 log.debug(" (allocating GOT entry at index {d})", .{self.got_entries.keys().len});
570 const index = @intCast(u32, self.got_entries.keys().len);
571 self.got_entries.putAssumeCapacityNoClobber(target, 0);
572 break :blk index;
573 }
574 };
575 self.got_entries.keys()[index] = target;
576 return index;
577}
578
579fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom {
580 const gpa = self.base.allocator;
581 const atom = try gpa.create(Atom);
582 errdefer gpa.destroy(atom);
583 atom.* = Atom.empty;
584 atom.sym_index = try self.allocateSymbol();
585 atom.size = @sizeOf(u64);
586 atom.alignment = @alignOf(u64);
587
588 try self.managed_atoms.append(gpa, atom);
589 try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom);
590 self.got_entries.getPtr(target).?.* = atom.sym_index;
591
592 const sym = atom.getSymbolPtr(self);
593 sym.section_number = @intToEnum(coff.SectionNumber, self.got_section_index.? + 1);
594 sym.value = try self.allocateAtom(atom, atom.size, atom.alignment);
595
596 log.debug("allocated GOT atom at 0x{x}", .{sym.value});
597
598 try atom.addRelocation(self, .{
599 .@"type" = .direct,
600 .target = target,
601 .offset = 0,
602 .addend = 0,
603 .pcrel = false,
604 .length = 3,
605 .prev_vaddr = sym.value,
606 });
607
608 return atom;
609}
610
611fn growAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u32 {
612 const sym = atom.getSymbol(self);
613 const align_ok = mem.alignBackwardGeneric(u32, sym.value, alignment) == sym.value;
614 const need_realloc = !align_ok or new_atom_size > atom.capacity(self);
615 if (!need_realloc) return sym.value;
616 return self.allocateAtom(atom, new_atom_size, alignment);
617}
618
619fn shrinkAtom(self: *Coff, atom: *Atom, new_block_size: u32) void {
620 _ = self;
621 _ = atom;
622 _ = new_block_size;
623 // TODO check the new capacity, and if it crosses the size threshold into a big enough
624 // capacity, insert a free list node for it.
625}
626
627fn writeAtom(self: *Coff, atom: *Atom, code: []const u8) !void {
628 const sym = atom.getSymbol(self);
629 const section = self.sections.get(@enumToInt(sym.section_number) - 1);
630 const file_offset = section.header.pointer_to_raw_data + sym.value - section.header.virtual_address;
631 log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset });
632 try self.base.file.?.pwriteAll(code, file_offset);
633 try self.resolveRelocs(atom);
634}
635
636fn writeGotAtom(self: *Coff, atom: *Atom) !void {
637 switch (self.ptr_width) {
638 .p32 => {
639 var buffer: [@sizeOf(u32)]u8 = [_]u8{0} ** @sizeOf(u32);
640 try self.writeAtom(atom, &buffer);
641 },
642 .p64 => {
643 var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);
644 try self.writeAtom(atom, &buffer);
645 },
551646 }
552647}
553648
554fn freeTextBlock(self: *Coff, text_block: *TextBlock) void {
649fn resolveRelocs(self: *Coff, atom: *Atom) !void {
650 const relocs = self.relocs.get(atom) orelse return;
651 const source_sym = atom.getSymbol(self);
652 const source_section = self.sections.get(@enumToInt(source_sym.section_number) - 1).header;
653 const file_offset = source_section.pointer_to_raw_data + source_sym.value - source_section.virtual_address;
654
655 log.debug("relocating '{s}'", .{atom.getName(self)});
656
657 for (relocs.items) |*reloc| {
658 const target_vaddr = switch (reloc.@"type") {
659 .got => blk: {
660 const got_atom = self.getGotAtomForSymbol(reloc.target) orelse continue;
661 break :blk got_atom.getSymbol(self).value;
662 },
663 .direct => self.getSymbol(reloc.target).value,
664 };
665 const target_vaddr_with_addend = target_vaddr + reloc.addend;
666
667 if (target_vaddr_with_addend == reloc.prev_vaddr) continue;
668
669 log.debug(" ({x}: [() => 0x{x} ({s})) ({s})", .{
670 reloc.offset,
671 target_vaddr_with_addend,
672 self.getSymbolName(reloc.target),
673 @tagName(reloc.@"type"),
674 });
675
676 if (reloc.pcrel) {
677 const source_vaddr = source_sym.value + reloc.offset;
678 const disp = target_vaddr_with_addend - source_vaddr - 4;
679 try self.base.file.?.pwriteAll(mem.asBytes(&@intCast(u32, disp)), file_offset + reloc.offset);
680 return;
681 }
682
683 switch (self.ptr_width) {
684 .p32 => try self.base.file.?.pwriteAll(
685 mem.asBytes(&@intCast(u32, target_vaddr_with_addend + default_image_base_exe)),
686 file_offset + reloc.offset,
687 ),
688 .p64 => switch (reloc.length) {
689 2 => try self.base.file.?.pwriteAll(
690 mem.asBytes(&@truncate(u32, target_vaddr_with_addend + default_image_base_exe)),
691 file_offset + reloc.offset,
692 ),
693 3 => try self.base.file.?.pwriteAll(
694 mem.asBytes(&(target_vaddr_with_addend + default_image_base_exe)),
695 file_offset + reloc.offset,
696 ),
697 else => unreachable,
698 },
699 }
700
701 reloc.prev_vaddr = target_vaddr_with_addend;
702 }
703}
704
705fn freeAtom(self: *Coff, atom: *Atom) void {
706 log.debug("freeAtom {*}", .{atom});
707
708 const sym = atom.getSymbol(self);
709 const sect_id = @enumToInt(sym.section_number) - 1;
710 const free_list = &self.sections.items(.free_list)[sect_id];
555711 var already_have_free_list_node = false;
556712 {
557713 var i: usize = 0;
558 // TODO turn text_block_free_list into a hash map
559 while (i < self.text_block_free_list.items.len) {
560 if (self.text_block_free_list.items[i] == text_block) {
561 _ = self.text_block_free_list.swapRemove(i);
714 // TODO turn free_list into a hash map
715 while (i < free_list.items.len) {
716 if (free_list.items[i] == atom) {
717 _ = free_list.swapRemove(i);
562718 continue;
563719 }
564 if (self.text_block_free_list.items[i] == text_block.prev) {
720 if (free_list.items[i] == atom.prev) {
565721 already_have_free_list_node = true;
566722 }
567723 i += 1;
568724 }
569725 }
570 if (self.last_text_block == text_block) {
571 self.last_text_block = text_block.prev;
726
727 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];
728 if (maybe_last_atom.*) |last_atom| {
729 if (last_atom == atom) {
730 if (atom.prev) |prev| {
731 // TODO shrink the section size here
732 maybe_last_atom.* = prev;
733 } else {
734 maybe_last_atom.* = null;
735 }
736 }
572737 }
573 if (text_block.prev) |prev| {
574 prev.next = text_block.next;
575738
576 if (!already_have_free_list_node and prev.freeListEligible()) {
739 if (atom.prev) |prev| {
740 prev.next = atom.next;
741
742 if (!already_have_free_list_node and prev.freeListEligible(self)) {
577743 // The free list is heuristics, it doesn't have to be perfect, so we can
578744 // ignore the OOM here.
579 self.text_block_free_list.append(self.base.allocator, prev) catch {};
745 free_list.append(self.base.allocator, prev) catch {};
580746 }
747 } else {
748 atom.prev = null;
581749 }
582750
583 if (text_block.next) |next| {
584 next.prev = text_block.prev;
585 }
586}
587
588fn writeOffsetTableEntry(self: *Coff, index: usize) !void {
589 const entry_size = self.base.options.target.cpu.arch.ptrBitWidth() / 8;
590 const endian = self.base.options.target.cpu.arch.endian();
591
592 const offset_table_start = self.section_data_offset;
593 if (self.offset_table_size_dirty) {
594 const current_raw_size = self.offset_table_size;
595 const new_raw_size = self.offset_table_size * 2;
596 log.debug("growing offset table from raw size {} to {}\n", .{ current_raw_size, new_raw_size });
597
598 // Move the text section to a new place in the executable
599 const current_text_section_start = self.section_data_offset + current_raw_size;
600 const new_text_section_start = self.section_data_offset + new_raw_size;
601
602 const amt = try self.base.file.?.copyRangeAll(current_text_section_start, self.base.file.?, new_text_section_start, self.text_section_size);
603 if (amt != self.text_section_size) return error.InputOutput;
604
605 // Write the new raw size in the .got header
606 var buf: [8]u8 = undefined;
607 mem.writeIntLittle(u32, buf[0..4], new_raw_size);
608 try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 16);
609 // Write the new .text section file offset in the .text section header
610 mem.writeIntLittle(u32, buf[0..4], new_text_section_start);
611 try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 20);
612
613 const current_virtual_size = mem.alignForwardGeneric(u32, self.offset_table_size, section_alignment);
614 const new_virtual_size = mem.alignForwardGeneric(u32, new_raw_size, section_alignment);
615 // 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
616 // and the virtual size of the `.got` section
617
618 if (new_virtual_size != current_virtual_size) {
619 log.debug("growing offset table from virtual size {} to {}\n", .{ current_virtual_size, new_virtual_size });
620 self.size_of_image_dirty = true;
621 const va_offset = new_virtual_size - current_virtual_size;
622
623 // Write .got virtual size
624 mem.writeIntLittle(u32, buf[0..4], new_virtual_size);
625 try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 8);
626
627 // Write .text new virtual address
628 self.text_section_virtual_address = self.text_section_virtual_address + va_offset;
629 mem.writeIntLittle(u32, buf[0..4], self.text_section_virtual_address - default_image_base);
630 try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 12);
631
632 // Fix the VAs in the offset table
633 for (self.offset_table.items) |*va, idx| {
634 if (va.* != 0) {
635 va.* += va_offset;
636
637 switch (entry_size) {
638 4 => {
639 mem.writeInt(u32, buf[0..4], @intCast(u32, va.*), endian);
640 try self.base.file.?.pwriteAll(buf[0..4], offset_table_start + idx * entry_size);
641 },
642 8 => {
643 mem.writeInt(u64, &buf, va.*, endian);
644 try self.base.file.?.pwriteAll(&buf, offset_table_start + idx * entry_size);
645 },
646 else => unreachable,
647 }
648 }
649 }
650 }
651 self.offset_table_size = new_raw_size;
652 self.offset_table_size_dirty = false;
653 }
654 // Write the new entry
655 switch (entry_size) {
656 4 => {
657 var buf: [4]u8 = undefined;
658 mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);
659 try self.base.file.?.pwriteAll(&buf, offset_table_start + index * entry_size);
660 },
661 8 => {
662 var buf: [8]u8 = undefined;
663 mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
664 try self.base.file.?.pwriteAll(&buf, offset_table_start + index * entry_size);
665 },
666 else => unreachable,
751 if (atom.next) |next| {
752 next.prev = atom.prev;
753 } else {
754 atom.next = null;
667755 }
668756}
669757
......@@ -702,15 +790,18 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live
702790 },
703791 };
704792
705 return self.finishUpdateDecl(module, func.owner_decl, code);
793 try self.updateDeclCode(decl_index, code, .FUNCTION);
794
795 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
796 const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{};
797 return self.updateDeclExports(module, decl_index, decl_exports);
706798}
707799
708800pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.Index) !u32 {
709801 _ = self;
710802 _ = tv;
711803 _ = decl_index;
712 log.debug("TODO lowerUnnamedConst for Coff", .{});
713 return error.AnalysisFail;
804 @panic("TODO lowerUnnamedConst");
714805}
715806
716807pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !void {
......@@ -728,16 +819,20 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !
728819 if (decl.val.tag() == .extern_fn) {
729820 return; // TODO Should we do more when front-end analyzed extern decl?
730821 }
731
732 // TODO COFF/PE debug information
733 // TODO Implement exports
822 if (decl.val.castTag(.variable)) |payload| {
823 const variable = payload.data;
824 if (variable.is_extern) {
825 return; // TODO Should we do more when front-end analyzed extern decl?
826 }
827 }
734828
735829 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
736830 defer code_buffer.deinit();
737831
832 const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;
738833 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
739834 .ty = decl.ty,
740 .val = decl.val,
835 .val = decl_val,
741836 }, &code_buffer, .none, .{
742837 .parent_atom_index = 0,
743838 });
......@@ -751,47 +846,98 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !
751846 },
752847 };
753848
754 return self.finishUpdateDecl(module, decl_index, code);
849 try self.updateDeclCode(decl_index, code, .NULL);
850
851 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
852 const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{};
853 return self.updateDeclExports(module, decl_index, decl_exports);
755854}
756855
757fn finishUpdateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index, code: []const u8) !void {
758 const decl = module.declPtr(decl_index);
759 const required_alignment = decl.ty.abiAlignment(self.base.options.target);
760 const curr_size = decl.link.coff.size;
761 if (curr_size != 0) {
762 const capacity = decl.link.coff.capacity();
763 const need_realloc = code.len > capacity or
764 !mem.isAlignedGeneric(u32, decl.link.coff.text_offset, required_alignment);
856fn getDeclOutputSection(self: *Coff, decl: *Module.Decl) u16 {
857 const ty = decl.ty;
858 const zig_ty = ty.zigTypeTag();
859 const val = decl.val;
860 const index: u16 = blk: {
861 if (val.isUndefDeep()) {
862 // TODO in release-fast and release-small, we should put undef in .bss
863 break :blk self.data_section_index.?;
864 }
865
866 switch (zig_ty) {
867 .Fn => break :blk self.text_section_index.?,
868 else => {
869 if (val.castTag(.variable)) |_| {
870 break :blk self.data_section_index.?;
871 }
872 break :blk self.rdata_section_index.?;
873 },
874 }
875 };
876 return index;
877}
878
879fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8, complex_type: coff.ComplexType) !void {
880 const gpa = self.base.allocator;
881 const mod = self.base.options.module.?;
882 const decl = mod.declPtr(decl_index);
883
884 const decl_name = try decl.getFullyQualifiedName(mod);
885 defer gpa.free(decl_name);
886
887 log.debug("updateDeclCode {s}{*}", .{ decl_name, decl });
888 const required_alignment = decl.getAlignment(self.base.options.target);
889
890 const decl_ptr = self.decls.getPtr(decl_index).?;
891 if (decl_ptr.* == null) {
892 decl_ptr.* = self.getDeclOutputSection(decl);
893 }
894 const sect_index = decl_ptr.*.?;
895
896 const code_len = @intCast(u32, code.len);
897 const atom = &decl.link.coff;
898 assert(atom.sym_index != 0); // Caller forgot to allocateDeclIndexes()
899 if (atom.size != 0) {
900 const sym = atom.getSymbolPtr(self);
901 try self.setSymbolName(sym, decl_name);
902 sym.section_number = @intToEnum(coff.SectionNumber, sect_index + 1);
903 sym.@"type" = .{ .complex_type = complex_type, .base_type = .NULL };
904
905 const capacity = atom.capacity(self);
906 const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u64, sym.value, required_alignment);
765907 if (need_realloc) {
766 const curr_vaddr = self.text_section_virtual_address + decl.link.coff.text_offset;
767 const vaddr = try self.growTextBlock(&decl.link.coff, code.len, required_alignment);
768 log.debug("growing {s} from 0x{x} to 0x{x}\n", .{ decl.name, curr_vaddr, vaddr });
769 if (vaddr != curr_vaddr) {
770 log.debug(" (writing new offset table entry)\n", .{});
771 self.offset_table.items[decl.link.coff.offset_table_index] = vaddr;
772 try self.writeOffsetTableEntry(decl.link.coff.offset_table_index);
908 const vaddr = try self.growAtom(atom, code_len, required_alignment);
909 log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, sym.value, vaddr });
910 log.debug(" (required alignment 0x{x}", .{required_alignment});
911
912 if (vaddr != sym.value) {
913 sym.value = vaddr;
914 log.debug(" (updating GOT entry)", .{});
915 const got_atom = self.getGotAtomForSymbol(.{ .sym_index = atom.sym_index, .file = null }).?;
916 try self.writeGotAtom(got_atom);
773917 }
774 } else if (code.len < curr_size) {
775 self.shrinkTextBlock(&decl.link.coff, code.len);
918 } else if (code_len < atom.size) {
919 self.shrinkAtom(atom, code_len);
776920 }
921 atom.size = code_len;
777922 } else {
778 const vaddr = try self.allocateTextBlock(&decl.link.coff, code.len, required_alignment);
779 log.debug("allocated text block for {s} at 0x{x} (size: {Bi})\n", .{
780 mem.sliceTo(decl.name, 0),
781 vaddr,
782 std.fmt.fmtIntSizeDec(code.len),
783 });
784 errdefer self.freeTextBlock(&decl.link.coff);
785 self.offset_table.items[decl.link.coff.offset_table_index] = vaddr;
786 try self.writeOffsetTableEntry(decl.link.coff.offset_table_index);
923 const sym = atom.getSymbolPtr(self);
924 try self.setSymbolName(sym, decl_name);
925 sym.section_number = @intToEnum(coff.SectionNumber, sect_index + 1);
926 sym.@"type" = .{ .complex_type = complex_type, .base_type = .NULL };
927
928 const vaddr = try self.allocateAtom(atom, code_len, required_alignment);
929 errdefer self.freeAtom(atom);
930 log.debug("allocated atom for {s} at 0x{x}", .{ decl_name, vaddr });
931 atom.size = code_len;
932 sym.value = vaddr;
933
934 const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null };
935 _ = try self.allocateGotEntry(got_target);
936 const got_atom = try self.createGotAtom(got_target);
937 try self.writeGotAtom(got_atom);
787938 }
788939
789 // Write the code into the file
790 try self.base.file.?.pwriteAll(code, self.section_data_offset + self.offset_table_size + decl.link.coff.text_offset);
791
792 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
793 const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{};
794 return self.updateDeclExports(module, decl_index, decl_exports);
940 try self.writeAtom(atom, code);
795941}
796942
797943pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void {
......@@ -802,9 +948,31 @@ pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void {
802948 const mod = self.base.options.module.?;
803949 const decl = mod.declPtr(decl_index);
804950
951 log.debug("freeDecl {*}", .{decl});
952
953 const kv = self.decls.fetchRemove(decl_index);
954 if (kv.?.value) |_| {
955 self.freeAtom(&decl.link.coff);
956 }
957
805958 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
806 self.freeTextBlock(&decl.link.coff);
807 self.offset_table_free_list.append(self.base.allocator, decl.link.coff.offset_table_index) catch {};
959 const gpa = self.base.allocator;
960 const sym_index = decl.link.coff.sym_index;
961 if (sym_index != 0) {
962 self.locals_free_list.append(gpa, sym_index) catch {};
963
964 // Try freeing GOT atom if this decl had one
965 const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null };
966 if (self.got_entries.getIndex(got_target)) |got_index| {
967 self.got_entries_free_list.append(gpa, @intCast(u32, got_index)) catch {};
968 self.got_entries.values()[got_index] = 0;
969 log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index });
970 }
971
972 self.locals.items[sym_index].section_number = @intToEnum(coff.SectionNumber, 0);
973 _ = self.atom_by_index_table.remove(sym_index);
974 decl.link.coff.sym_index = 0;
975 }
808976}
809977
810978pub fn updateDeclExports(
......@@ -817,64 +985,157 @@ pub fn updateDeclExports(
817985 @panic("Attempted to compile for object format that was disabled by build configuration");
818986 }
819987
820 // Even in the case of LLVM, we need to notice certain exported symbols in order to
821 // detect the default subsystem.
822 for (exports) |exp| {
823 const exported_decl = module.declPtr(exp.exported_decl);
824 if (exported_decl.getFunction() == null) continue;
825 const winapi_cc = switch (self.base.options.target.cpu.arch) {
826 .i386 => std.builtin.CallingConvention.Stdcall,
827 else => std.builtin.CallingConvention.C,
828 };
829 const decl_cc = exported_decl.ty.fnCallingConvention();
830 if (decl_cc == .C and mem.eql(u8, exp.options.name, "main") and
831 self.base.options.link_libc)
832 {
833 module.stage1_flags.have_c_main = true;
834 } else if (decl_cc == winapi_cc and self.base.options.target.os.tag == .windows) {
835 if (mem.eql(u8, exp.options.name, "WinMain")) {
836 module.stage1_flags.have_winmain = true;
837 } else if (mem.eql(u8, exp.options.name, "wWinMain")) {
838 module.stage1_flags.have_wwinmain = true;
839 } else if (mem.eql(u8, exp.options.name, "WinMainCRTStartup")) {
840 module.stage1_flags.have_winmain_crt_startup = true;
841 } else if (mem.eql(u8, exp.options.name, "wWinMainCRTStartup")) {
842 module.stage1_flags.have_wwinmain_crt_startup = true;
843 } else if (mem.eql(u8, exp.options.name, "DllMainCRTStartup")) {
844 module.stage1_flags.have_dllmain_crt_startup = true;
988 if (build_options.have_llvm) {
989 // Even in the case of LLVM, we need to notice certain exported symbols in order to
990 // detect the default subsystem.
991 for (exports) |exp| {
992 const exported_decl = module.declPtr(exp.exported_decl);
993 if (exported_decl.getFunction() == null) continue;
994 const winapi_cc = switch (self.base.options.target.cpu.arch) {
995 .i386 => std.builtin.CallingConvention.Stdcall,
996 else => std.builtin.CallingConvention.C,
997 };
998 const decl_cc = exported_decl.ty.fnCallingConvention();
999 if (decl_cc == .C and mem.eql(u8, exp.options.name, "main") and
1000 self.base.options.link_libc)
1001 {
1002 module.stage1_flags.have_c_main = true;
1003 } else if (decl_cc == winapi_cc and self.base.options.target.os.tag == .windows) {
1004 if (mem.eql(u8, exp.options.name, "WinMain")) {
1005 module.stage1_flags.have_winmain = true;
1006 } else if (mem.eql(u8, exp.options.name, "wWinMain")) {
1007 module.stage1_flags.have_wwinmain = true;
1008 } else if (mem.eql(u8, exp.options.name, "WinMainCRTStartup")) {
1009 module.stage1_flags.have_winmain_crt_startup = true;
1010 } else if (mem.eql(u8, exp.options.name, "wWinMainCRTStartup")) {
1011 module.stage1_flags.have_wwinmain_crt_startup = true;
1012 } else if (mem.eql(u8, exp.options.name, "DllMainCRTStartup")) {
1013 module.stage1_flags.have_dllmain_crt_startup = true;
1014 }
8451015 }
8461016 }
847 }
8481017
849 if (build_options.have_llvm) {
8501018 if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl_index, exports);
8511019 }
8521020
1021 const tracy = trace(@src());
1022 defer tracy.end();
1023
1024 const gpa = self.base.allocator;
1025
8531026 const decl = module.declPtr(decl_index);
1027 const atom = &decl.link.coff;
1028 if (atom.sym_index == 0) return;
1029 const decl_sym = atom.getSymbol(self);
1030
8541031 for (exports) |exp| {
1032 log.debug("adding new export '{s}'", .{exp.options.name});
1033
8551034 if (exp.options.section) |section_name| {
8561035 if (!mem.eql(u8, section_name, ".text")) {
857 try module.failed_exports.ensureUnusedCapacity(module.gpa, 1);
858 module.failed_exports.putAssumeCapacityNoClobber(
1036 try module.failed_exports.putNoClobber(
1037 module.gpa,
8591038 exp,
860 try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: ExportOptions.section", .{}),
1039 try Module.ErrorMsg.create(
1040 gpa,
1041 decl.srcLoc(),
1042 "Unimplemented: ExportOptions.section",
1043 .{},
1044 ),
8611045 );
8621046 continue;
8631047 }
8641048 }
865 if (mem.eql(u8, exp.options.name, "_start")) {
866 self.entry_addr = decl.link.coff.getVAddr(self.*) - default_image_base;
867 } else {
868 try module.failed_exports.ensureUnusedCapacity(module.gpa, 1);
869 module.failed_exports.putAssumeCapacityNoClobber(
1049
1050 if (exp.options.linkage == .LinkOnce) {
1051 try module.failed_exports.putNoClobber(
1052 module.gpa,
8701053 exp,
871 try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: Exports other than '_start'", .{}),
1054 try Module.ErrorMsg.create(
1055 gpa,
1056 decl.srcLoc(),
1057 "Unimplemented: GlobalLinkage.LinkOnce",
1058 .{},
1059 ),
8721060 );
8731061 continue;
8741062 }
1063
1064 const sym_index = exp.link.coff.sym_index orelse blk: {
1065 const sym_index = try self.allocateSymbol();
1066 exp.link.coff.sym_index = sym_index;
1067 break :blk sym_index;
1068 };
1069 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };
1070 const sym = self.getSymbolPtr(sym_loc);
1071 try self.setSymbolName(sym, exp.options.name);
1072 sym.value = decl_sym.value;
1073 sym.section_number = @intToEnum(coff.SectionNumber, self.text_section_index.? + 1);
1074 sym.@"type" = .{ .complex_type = .FUNCTION, .base_type = .NULL };
1075
1076 switch (exp.options.linkage) {
1077 .Strong => {
1078 sym.storage_class = .EXTERNAL;
1079 },
1080 .Internal => @panic("TODO Internal"),
1081 .Weak => @panic("TODO WeakExternal"),
1082 else => unreachable,
1083 }
1084
1085 try self.resolveGlobalSymbol(sym_loc);
1086 }
1087}
1088
1089pub fn deleteExport(self: *Coff, exp: Export) void {
1090 if (self.llvm_object) |_| return;
1091 const sym_index = exp.sym_index orelse return;
1092
1093 const gpa = self.base.allocator;
1094
1095 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };
1096 const sym = self.getSymbolPtr(sym_loc);
1097 const sym_name = self.getSymbolName(sym_loc);
1098 log.debug("deleting export '{s}'", .{sym_name});
1099 assert(sym.storage_class == .EXTERNAL);
1100 sym.* = .{
1101 .name = [_]u8{0} ** 8,
1102 .value = 0,
1103 .section_number = @intToEnum(coff.SectionNumber, 0),
1104 .@"type" = .{ .base_type = .NULL, .complex_type = .NULL },
1105 .storage_class = .NULL,
1106 .number_of_aux_symbols = 0,
1107 };
1108 self.locals_free_list.append(gpa, sym_index) catch {};
1109
1110 if (self.globals.get(sym_name)) |global| blk: {
1111 if (global.sym_index != sym_index) break :blk;
1112 if (global.file != null) break :blk;
1113 const kv = self.globals.fetchSwapRemove(sym_name);
1114 gpa.free(kv.?.key);
8751115 }
8761116}
8771117
1118fn resolveGlobalSymbol(self: *Coff, current: SymbolWithLoc) !void {
1119 const gpa = self.base.allocator;
1120 const sym = self.getSymbol(current);
1121 _ = sym;
1122 const sym_name = self.getSymbolName(current);
1123
1124 const name = try gpa.dupe(u8, sym_name);
1125 const global_index = @intCast(u32, self.globals.values().len);
1126 _ = global_index;
1127 const gop = try self.globals.getOrPut(gpa, name);
1128 defer if (gop.found_existing) gpa.free(name);
1129
1130 if (!gop.found_existing) {
1131 gop.value_ptr.* = current;
1132 // TODO undef + tentative
1133 return;
1134 }
1135
1136 log.debug("TODO finish resolveGlobalSymbols implementation", .{});
1137}
1138
8781139pub fn flush(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !void {
8791140 if (self.base.options.emit == null) {
8801141 if (build_options.have_llvm) {
......@@ -884,14 +1145,13 @@ pub fn flush(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !vo
8841145 }
8851146 return;
8861147 }
887 if (build_options.have_llvm and self.base.options.use_lld) {
888 return self.linkWithLLD(comp, prog_node);
889 } else {
890 switch (self.base.options.effectiveOutputMode()) {
891 .Exe, .Obj => {},
892 .Lib => return error.TODOImplementWritingLibFiles,
893 }
894 return self.flushModule(comp, prog_node);
1148 const use_lld = build_options.have_llvm and self.base.options.use_lld;
1149 if (use_lld) {
1150 return lld.linkWithLLD(self, comp, prog_node);
1151 }
1152 switch (self.base.options.output_mode) {
1153 .Exe, .Obj => return self.flushModule(comp, prog_node),
1154 .Lib => return error.TODOImplementWritingLibFiles,
8951155 }
8961156}
8971157
......@@ -909,648 +1169,449 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
9091169 sub_prog_node.activate();
9101170 defer sub_prog_node.end();
9111171
912 if (self.text_section_size_dirty) {
913 // Write the new raw size in the .text header
914 var buf: [4]u8 = undefined;
915 mem.writeIntLittle(u32, &buf, self.text_section_size);
916 try self.base.file.?.pwriteAll(&buf, self.section_table_offset + 40 + 16);
917 try self.base.file.?.setEndPos(self.section_data_offset + self.offset_table_size + self.text_section_size);
918 self.text_section_size_dirty = false;
1172 if (build_options.enable_logging) {
1173 self.logSymtab();
9191174 }
9201175
921 if (self.base.options.output_mode == .Exe and self.size_of_image_dirty) {
922 const new_size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - default_image_base + self.text_section_size, section_alignment);
923 var buf: [4]u8 = undefined;
924 mem.writeIntLittle(u32, &buf, new_size_of_image);
925 try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 56);
926 self.size_of_image_dirty = false;
1176 {
1177 var it = self.relocs.keyIterator();
1178 while (it.next()) |atom| {
1179 try self.resolveRelocs(atom.*);
1180 }
9271181 }
9281182
1183 if (self.getEntryPoint()) |entry_sym_loc| {
1184 self.entry_addr = self.getSymbol(entry_sym_loc).value;
1185 }
1186
1187 try self.writeStrtab();
1188 try self.writeDataDirectoriesHeaders();
1189 try self.writeSectionHeaders();
1190
9291191 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
9301192 log.debug("flushing. no_entry_point_found = true\n", .{});
9311193 self.error_flags.no_entry_point_found = true;
9321194 } else {
9331195 log.debug("flushing. no_entry_point_found = false\n", .{});
9341196 self.error_flags.no_entry_point_found = false;
935
936 if (self.base.options.output_mode == .Exe) {
937 // Write AddressOfEntryPoint
938 var buf: [4]u8 = undefined;
939 mem.writeIntLittle(u32, &buf, self.entry_addr.?);
940 try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 16);
941 }
1197 try self.writeHeader();
9421198 }
9431199}
9441200
945fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !void {
946 const tracy = trace(@src());
947 defer tracy.end();
948
949 var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
950 defer arena_allocator.deinit();
951 const arena = arena_allocator.allocator();
952
953 const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type.
954 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
955
956 // If there is no Zig code to compile, then we should skip flushing the output file because it
957 // will not be part of the linker line anyway.
958 const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {
959 const use_stage1 = build_options.have_stage1 and self.base.options.use_stage1;
960 if (use_stage1) {
961 const obj_basename = try std.zig.binNameAlloc(arena, .{
962 .root_name = self.base.options.root_name,
963 .target = self.base.options.target,
964 .output_mode = .Obj,
965 });
966 switch (self.base.options.cache_mode) {
967 .incremental => break :blk try module.zig_cache_artifact_directory.join(
968 arena,
969 &[_][]const u8{obj_basename},
970 ),
971 .whole => break :blk try fs.path.join(arena, &.{
972 fs.path.dirname(full_out_path).?, obj_basename,
973 }),
974 }
975 }
976
977 try self.flushModule(comp, prog_node);
1201pub fn getDeclVAddr(
1202 self: *Coff,
1203 decl_index: Module.Decl.Index,
1204 reloc_info: link.File.RelocInfo,
1205) !u64 {
1206 _ = self;
1207 _ = decl_index;
1208 _ = reloc_info;
1209 @panic("TODO getDeclVAddr");
1210}
9781211
979 if (fs.path.dirname(full_out_path)) |dirname| {
980 break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? });
981 } else {
982 break :blk self.base.intermediary_basename.?;
983 }
984 } else null;
1212pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void {
1213 _ = self;
1214 _ = module;
1215 _ = decl;
1216 log.debug("TODO implement updateDeclLineNumber", .{});
1217}
9851218
986 var sub_prog_node = prog_node.start("LLD Link", 0);
987 sub_prog_node.activate();
988 sub_prog_node.context.refresh();
989 defer sub_prog_node.end();
1219fn writeStrtab(self: *Coff) !void {
1220 const allocated_size = self.allocatedSize(self.strtab_offset.?);
1221 const needed_size = @intCast(u32, self.strtab.len());
9901222
991 const is_lib = self.base.options.output_mode == .Lib;
992 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
993 const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
994 const link_in_crt = self.base.options.link_libc and is_exe_or_dyn_lib;
995 const target = self.base.options.target;
1223 if (needed_size > allocated_size) {
1224 self.strtab_offset = null;
1225 self.strtab_offset = @intCast(u32, self.findFreeSpace(needed_size, 1));
1226 }
9961227
997 // See link/Elf.zig for comments on how this mechanism works.
998 const id_symlink_basename = "lld.id";
1228 log.debug("writing strtab from 0x{x} to 0x{x}", .{ self.strtab_offset.?, self.strtab_offset.? + needed_size });
1229 try self.base.file.?.pwriteAll(self.strtab.buffer.items, self.strtab_offset.?);
1230}
9991231
1000 var man: Cache.Manifest = undefined;
1001 defer if (!self.base.options.disable_lld_caching) man.deinit();
1232fn writeSectionHeaders(self: *Coff) !void {
1233 const offset = self.getSectionHeadersOffset();
1234 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items(.header)), offset);
1235}
10021236
1003 var digest: [Cache.hex_digest_len]u8 = undefined;
1237fn writeDataDirectoriesHeaders(self: *Coff) !void {
1238 const offset = self.getDataDirectoryHeadersOffset();
1239 try self.base.file.?.pwriteAll(mem.sliceAsBytes(&self.data_directories), offset);
1240}
10041241
1005 if (!self.base.options.disable_lld_caching) {
1006 man = comp.cache_parent.obtain();
1007 self.base.releaseLock();
1242fn writeHeader(self: *Coff) !void {
1243 const gpa = self.base.allocator;
1244 var buffer = std.ArrayList(u8).init(gpa);
1245 defer buffer.deinit();
1246 const writer = buffer.writer();
10081247
1009 comptime assert(Compilation.link_hash_implementation_version == 7);
1248 try buffer.ensureTotalCapacity(self.getSizeOfHeaders());
1249 writer.writeAll(msdos_stub) catch unreachable;
1250 mem.writeIntLittle(u32, buffer.items[0x3c..][0..4], msdos_stub.len);
10101251
1011 for (self.base.options.objects) |obj| {
1012 _ = try man.addFile(obj.path, null);
1013 man.hash.add(obj.must_link);
1014 }
1015 for (comp.c_object_table.keys()) |key| {
1016 _ = try man.addFile(key.status.success.object_path, null);
1017 }
1018 try man.addOptionalFile(module_obj_path);
1019 man.hash.addOptionalBytes(self.base.options.entry);
1020 man.hash.addOptional(self.base.options.stack_size_override);
1021 man.hash.addOptional(self.base.options.image_base_override);
1022 man.hash.addListOfBytes(self.base.options.lib_dirs);
1023 man.hash.add(self.base.options.skip_linker_dependencies);
1024 if (self.base.options.link_libc) {
1025 man.hash.add(self.base.options.libc_installation != null);
1026 if (self.base.options.libc_installation) |libc_installation| {
1027 man.hash.addBytes(libc_installation.crt_dir.?);
1028 if (target.abi == .msvc) {
1029 man.hash.addBytes(libc_installation.msvc_lib_dir.?);
1030 man.hash.addBytes(libc_installation.kernel32_lib_dir.?);
1031 }
1032 }
1033 }
1034 link.hashAddSystemLibs(&man.hash, self.base.options.system_libs);
1035 man.hash.addListOfBytes(self.base.options.force_undefined_symbols.keys());
1036 man.hash.addOptional(self.base.options.subsystem);
1037 man.hash.add(self.base.options.is_test);
1038 man.hash.add(self.base.options.tsaware);
1039 man.hash.add(self.base.options.nxcompat);
1040 man.hash.add(self.base.options.dynamicbase);
1041 // strip does not need to go into the linker hash because it is part of the hash namespace
1042 man.hash.addOptional(self.base.options.major_subsystem_version);
1043 man.hash.addOptional(self.base.options.minor_subsystem_version);
1044
1045 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
1046 _ = try man.hit();
1047 digest = man.final();
1048 var prev_digest_buf: [digest.len]u8 = undefined;
1049 const prev_digest: []u8 = Cache.readSmallFile(
1050 directory.handle,
1051 id_symlink_basename,
1052 &prev_digest_buf,
1053 ) catch |err| blk: {
1054 log.debug("COFF LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
1055 // Handle this as a cache miss.
1056 break :blk prev_digest_buf[0..0];
1057 };
1058 if (mem.eql(u8, prev_digest, &digest)) {
1059 log.debug("COFF LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
1060 // Hot diggity dog! The output binary is already there.
1061 self.base.lock = man.toOwnedLock();
1062 return;
1063 }
1064 log.debug("COFF LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });
1065
1066 // We are about to change the output file to be different, so we invalidate the build hash now.
1067 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
1068 error.FileNotFound => {},
1069 else => |e| return e,
1070 };
1252 writer.writeAll("PE\x00\x00") catch unreachable;
1253 var flags = coff.CoffHeaderFlags{
1254 .EXECUTABLE_IMAGE = 1,
1255 .DEBUG_STRIPPED = 1, // TODO
1256 };
1257 switch (self.ptr_width) {
1258 .p32 => flags.@"32BIT_MACHINE" = 1,
1259 .p64 => flags.LARGE_ADDRESS_AWARE = 1,
1260 }
1261 if (self.base.options.output_mode == .Lib and self.base.options.link_mode == .Dynamic) {
1262 flags.DLL = 1;
10711263 }
10721264
1073 if (self.base.options.output_mode == .Obj) {
1074 // LLD's COFF driver does not support the equivalent of `-r` so we do a simple file copy
1075 // here. TODO: think carefully about how we can avoid this redundant operation when doing
1076 // build-obj. See also the corresponding TODO in linkAsArchive.
1077 const the_object_path = blk: {
1078 if (self.base.options.objects.len != 0)
1079 break :blk self.base.options.objects[0].path;
1265 const timestamp = std.time.timestamp();
1266 const size_of_optional_header = @intCast(u16, self.getOptionalHeaderSize() + self.getDataDirectoryHeadersSize());
1267 var coff_header = coff.CoffHeader{
1268 .machine = coff.MachineType.fromTargetCpuArch(self.base.options.target.cpu.arch),
1269 .number_of_sections = @intCast(u16, self.sections.slice().len), // TODO what if we prune a section
1270 .time_date_stamp = @truncate(u32, @bitCast(u64, timestamp)),
1271 .pointer_to_symbol_table = self.strtab_offset orelse 0,
1272 .number_of_symbols = 0,
1273 .size_of_optional_header = size_of_optional_header,
1274 .flags = flags,
1275 };
10801276
1081 if (comp.c_object_table.count() != 0)
1082 break :blk comp.c_object_table.keys()[0].status.success.object_path;
1277 writer.writeAll(mem.asBytes(&coff_header)) catch unreachable;
10831278
1084 if (module_obj_path) |p|
1085 break :blk p;
1279 const dll_flags: coff.DllFlags = .{
1280 .HIGH_ENTROPY_VA = 0, //@boolToInt(self.base.options.pie),
1281 .DYNAMIC_BASE = 0,
1282 .TERMINAL_SERVER_AWARE = 1, // We are not a legacy app
1283 .NX_COMPAT = 1, // We are compatible with Data Execution Prevention
1284 };
1285 const subsystem: coff.Subsystem = .WINDOWS_CUI;
1286 const size_of_image: u32 = self.getSizeOfImage();
1287 const size_of_headers: u32 = mem.alignForwardGeneric(u32, self.getSizeOfHeaders(), default_file_alignment);
1288 const image_base = self.base.options.image_base_override orelse switch (self.base.options.output_mode) {
1289 .Exe => default_image_base_exe,
1290 .Lib => default_image_base_dll,
1291 else => unreachable,
1292 };
10861293
1087 // TODO I think this is unreachable. Audit this situation when solving the above TODO
1088 // regarding eliding redundant object -> object transformations.
1089 return error.NoObjectsToLink;
1090 };
1091 // This can happen when using --enable-cache and using the stage1 backend. In this case
1092 // we can skip the file copy.
1093 if (!mem.eql(u8, the_object_path, full_out_path)) {
1094 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
1095 }
1096 } else {
1097 // Create an LLD command line and invoke it.
1098 var argv = std.ArrayList([]const u8).init(self.base.allocator);
1099 defer argv.deinit();
1100 // We will invoke ourselves as a child process to gain access to LLD.
1101 // This is necessary because LLD does not behave properly as a library -
1102 // it calls exit() and does not reset all global data between invocations.
1103 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "lld-link" });
1104
1105 try argv.append("-ERRORLIMIT:0");
1106 try argv.append("-NOLOGO");
1107 if (!self.base.options.strip) {
1108 try argv.append("-DEBUG");
1109 }
1110 if (self.base.options.lto) {
1111 switch (self.base.options.optimize_mode) {
1112 .Debug => {},
1113 .ReleaseSmall => try argv.append("-OPT:lldlto=2"),
1114 .ReleaseFast, .ReleaseSafe => try argv.append("-OPT:lldlto=3"),
1115 }
1294 const base_of_code = self.sections.get(self.text_section_index.?).header.virtual_address;
1295 const base_of_data = self.sections.get(self.data_section_index.?).header.virtual_address;
1296
1297 var size_of_code: u32 = 0;
1298 var size_of_initialized_data: u32 = 0;
1299 var size_of_uninitialized_data: u32 = 0;
1300 for (self.sections.items(.header)) |header| {
1301 if (header.flags.CNT_CODE == 1) {
1302 size_of_code += header.size_of_raw_data;
11161303 }
1117 if (self.base.options.output_mode == .Exe) {
1118 const stack_size = self.base.options.stack_size_override orelse 16777216;
1119 try argv.append(try allocPrint(arena, "-STACK:{d}", .{stack_size}));
1304 if (header.flags.CNT_INITIALIZED_DATA == 1) {
1305 size_of_initialized_data += header.size_of_raw_data;
11201306 }
1121 if (self.base.options.image_base_override) |image_base| {
1122 try argv.append(try std.fmt.allocPrint(arena, "-BASE:{d}", .{image_base}));
1307 if (header.flags.CNT_UNINITIALIZED_DATA == 1) {
1308 size_of_uninitialized_data += header.size_of_raw_data;
11231309 }
1310 }
11241311
1125 if (target.cpu.arch == .i386) {
1126 try argv.append("-MACHINE:X86");
1127 } else if (target.cpu.arch == .x86_64) {
1128 try argv.append("-MACHINE:X64");
1129 } else if (target.cpu.arch.isARM()) {
1130 if (target.cpu.arch.ptrBitWidth() == 32) {
1131 try argv.append("-MACHINE:ARM");
1132 } else {
1133 try argv.append("-MACHINE:ARM64");
1134 }
1135 }
1312 switch (self.ptr_width) {
1313 .p32 => {
1314 var opt_header = coff.OptionalHeaderPE32{
1315 .magic = coff.IMAGE_NT_OPTIONAL_HDR32_MAGIC,
1316 .major_linker_version = 0,
1317 .minor_linker_version = 0,
1318 .size_of_code = size_of_code,
1319 .size_of_initialized_data = size_of_initialized_data,
1320 .size_of_uninitialized_data = size_of_uninitialized_data,
1321 .address_of_entry_point = self.entry_addr orelse 0,
1322 .base_of_code = base_of_code,
1323 .base_of_data = base_of_data,
1324 .image_base = @intCast(u32, image_base),
1325 .section_alignment = self.page_size,
1326 .file_alignment = default_file_alignment,
1327 .major_operating_system_version = 6,
1328 .minor_operating_system_version = 0,
1329 .major_image_version = 0,
1330 .minor_image_version = 0,
1331 .major_subsystem_version = 6,
1332 .minor_subsystem_version = 0,
1333 .win32_version_value = 0,
1334 .size_of_image = size_of_image,
1335 .size_of_headers = size_of_headers,
1336 .checksum = 0,
1337 .subsystem = subsystem,
1338 .dll_flags = dll_flags,
1339 .size_of_stack_reserve = default_size_of_stack_reserve,
1340 .size_of_stack_commit = default_size_of_stack_commit,
1341 .size_of_heap_reserve = default_size_of_heap_reserve,
1342 .size_of_heap_commit = default_size_of_heap_commit,
1343 .loader_flags = 0,
1344 .number_of_rva_and_sizes = @intCast(u32, self.data_directories.len),
1345 };
1346 writer.writeAll(mem.asBytes(&opt_header)) catch unreachable;
1347 },
1348 .p64 => {
1349 var opt_header = coff.OptionalHeaderPE64{
1350 .magic = coff.IMAGE_NT_OPTIONAL_HDR64_MAGIC,
1351 .major_linker_version = 0,
1352 .minor_linker_version = 0,
1353 .size_of_code = size_of_code,
1354 .size_of_initialized_data = size_of_initialized_data,
1355 .size_of_uninitialized_data = size_of_uninitialized_data,
1356 .address_of_entry_point = self.entry_addr orelse 0,
1357 .base_of_code = base_of_code,
1358 .image_base = image_base,
1359 .section_alignment = self.page_size,
1360 .file_alignment = default_file_alignment,
1361 .major_operating_system_version = 6,
1362 .minor_operating_system_version = 0,
1363 .major_image_version = 0,
1364 .minor_image_version = 0,
1365 .major_subsystem_version = 6,
1366 .minor_subsystem_version = 0,
1367 .win32_version_value = 0,
1368 .size_of_image = size_of_image,
1369 .size_of_headers = size_of_headers,
1370 .checksum = 0,
1371 .subsystem = subsystem,
1372 .dll_flags = dll_flags,
1373 .size_of_stack_reserve = default_size_of_stack_reserve,
1374 .size_of_stack_commit = default_size_of_stack_commit,
1375 .size_of_heap_reserve = default_size_of_heap_reserve,
1376 .size_of_heap_commit = default_size_of_heap_commit,
1377 .loader_flags = 0,
1378 .number_of_rva_and_sizes = @intCast(u32, self.data_directories.len),
1379 };
1380 writer.writeAll(mem.asBytes(&opt_header)) catch unreachable;
1381 },
1382 }
11361383
1137 for (self.base.options.force_undefined_symbols.keys()) |symbol| {
1138 try argv.append(try allocPrint(arena, "-INCLUDE:{s}", .{symbol}));
1139 }
1384 try self.base.file.?.pwriteAll(buffer.items, 0);
1385}
11401386
1141 if (is_dyn_lib) {
1142 try argv.append("-DLL");
1143 }
1387pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
1388 // TODO https://github.com/ziglang/zig/issues/1284
1389 return math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch
1390 math.maxInt(@TypeOf(actual_size));
1391}
11441392
1145 if (self.base.options.entry) |entry| {
1146 try argv.append(try allocPrint(arena, "-ENTRY:{s}", .{entry}));
1147 }
1393fn detectAllocCollision(self: *Coff, start: u32, size: u32) ?u32 {
1394 const headers_size = self.getSizeOfHeaders();
1395 if (start < headers_size)
1396 return headers_size;
11481397
1149 if (self.base.options.tsaware) {
1150 try argv.append("-tsaware");
1151 }
1152 if (self.base.options.nxcompat) {
1153 try argv.append("-nxcompat");
1154 }
1155 if (self.base.options.dynamicbase) {
1156 try argv.append("-dynamicbase");
1157 }
1398 const end = start + size;
11581399
1159 try argv.append(try allocPrint(arena, "-OUT:{s}", .{full_out_path}));
1400 if (self.strtab_offset) |off| {
1401 const increased_size = @intCast(u32, self.strtab.len());
1402 const test_end = off + increased_size;
1403 if (end > off and start < test_end) {
1404 return test_end;
1405 }
1406 }
11601407
1161 if (self.base.options.implib_emit) |emit| {
1162 const implib_out_path = try emit.directory.join(arena, &[_][]const u8{emit.sub_path});
1163 try argv.append(try allocPrint(arena, "-IMPLIB:{s}", .{implib_out_path}));
1408 for (self.sections.items(.header)) |header| {
1409 const increased_size = header.size_of_raw_data;
1410 const test_end = header.pointer_to_raw_data + increased_size;
1411 if (end > header.pointer_to_raw_data and start < test_end) {
1412 return test_end;
11641413 }
1414 }
11651415
1166 if (self.base.options.link_libc) {
1167 if (self.base.options.libc_installation) |libc_installation| {
1168 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.crt_dir.?}));
1416 return null;
1417}
11691418
1170 if (target.abi == .msvc) {
1171 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.msvc_lib_dir.?}));
1172 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.kernel32_lib_dir.?}));
1173 }
1174 }
1175 }
1419pub fn allocatedSize(self: *Coff, start: u32) u32 {
1420 if (start == 0)
1421 return 0;
1422 var min_pos: u32 = std.math.maxInt(u32);
1423 if (self.strtab_offset) |off| {
1424 if (off > start and off < min_pos) min_pos = off;
1425 }
1426 for (self.sections.items(.header)) |header| {
1427 if (header.pointer_to_raw_data <= start) continue;
1428 if (header.pointer_to_raw_data < min_pos) min_pos = header.pointer_to_raw_data;
1429 }
1430 return min_pos - start;
1431}
11761432
1177 for (self.base.options.lib_dirs) |lib_dir| {
1178 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{lib_dir}));
1179 }
1433pub fn findFreeSpace(self: *Coff, object_size: u32, min_alignment: u32) u32 {
1434 var start: u32 = 0;
1435 while (self.detectAllocCollision(start, object_size)) |item_end| {
1436 start = mem.alignForwardGeneric(u32, item_end, min_alignment);
1437 }
1438 return start;
1439}
11801440
1181 try argv.ensureUnusedCapacity(self.base.options.objects.len);
1182 for (self.base.options.objects) |obj| {
1183 if (obj.must_link) {
1184 argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{s}", .{obj.path}));
1185 } else {
1186 argv.appendAssumeCapacity(obj.path);
1187 }
1188 }
1441inline fn getSizeOfHeaders(self: Coff) u32 {
1442 const msdos_hdr_size = msdos_stub.len + 4;
1443 return @intCast(u32, msdos_hdr_size + @sizeOf(coff.CoffHeader) + self.getOptionalHeaderSize() +
1444 self.getDataDirectoryHeadersSize() + self.getSectionHeadersSize());
1445}
11891446
1190 for (comp.c_object_table.keys()) |key| {
1191 try argv.append(key.status.success.object_path);
1192 }
1447inline fn getOptionalHeaderSize(self: Coff) u32 {
1448 return switch (self.ptr_width) {
1449 .p32 => @intCast(u32, @sizeOf(coff.OptionalHeaderPE32)),
1450 .p64 => @intCast(u32, @sizeOf(coff.OptionalHeaderPE64)),
1451 };
1452}
11931453
1194 if (module_obj_path) |p| {
1195 try argv.append(p);
1196 }
1454inline fn getDataDirectoryHeadersSize(self: Coff) u32 {
1455 return @intCast(u32, self.data_directories.len * @sizeOf(coff.ImageDataDirectory));
1456}
11971457
1198 const resolved_subsystem: ?std.Target.SubSystem = blk: {
1199 if (self.base.options.subsystem) |explicit| break :blk explicit;
1200 switch (target.os.tag) {
1201 .windows => {
1202 if (self.base.options.module) |module| {
1203 if (module.stage1_flags.have_dllmain_crt_startup or is_dyn_lib)
1204 break :blk null;
1205 if (module.stage1_flags.have_c_main or self.base.options.is_test or
1206 module.stage1_flags.have_winmain_crt_startup or
1207 module.stage1_flags.have_wwinmain_crt_startup)
1208 {
1209 break :blk .Console;
1210 }
1211 if (module.stage1_flags.have_winmain or module.stage1_flags.have_wwinmain)
1212 break :blk .Windows;
1213 }
1214 },
1215 .uefi => break :blk .EfiApplication,
1216 else => {},
1217 }
1218 break :blk null;
1219 };
1458inline fn getSectionHeadersSize(self: Coff) u32 {
1459 return @intCast(u32, self.sections.slice().len * @sizeOf(coff.SectionHeader));
1460}
12201461
1221 const Mode = enum { uefi, win32 };
1222 const mode: Mode = mode: {
1223 if (resolved_subsystem) |subsystem| {
1224 const subsystem_suffix = ss: {
1225 if (self.base.options.major_subsystem_version) |major| {
1226 if (self.base.options.minor_subsystem_version) |minor| {
1227 break :ss try allocPrint(arena, ",{d}.{d}", .{ major, minor });
1228 } else {
1229 break :ss try allocPrint(arena, ",{d}", .{major});
1230 }
1231 }
1232 break :ss "";
1233 };
1234
1235 switch (subsystem) {
1236 .Console => {
1237 try argv.append(try allocPrint(arena, "-SUBSYSTEM:console{s}", .{
1238 subsystem_suffix,
1239 }));
1240 break :mode .win32;
1241 },
1242 .EfiApplication => {
1243 try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_application{s}", .{
1244 subsystem_suffix,
1245 }));
1246 break :mode .uefi;
1247 },
1248 .EfiBootServiceDriver => {
1249 try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_boot_service_driver{s}", .{
1250 subsystem_suffix,
1251 }));
1252 break :mode .uefi;
1253 },
1254 .EfiRom => {
1255 try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_rom{s}", .{
1256 subsystem_suffix,
1257 }));
1258 break :mode .uefi;
1259 },
1260 .EfiRuntimeDriver => {
1261 try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_runtime_driver{s}", .{
1262 subsystem_suffix,
1263 }));
1264 break :mode .uefi;
1265 },
1266 .Native => {
1267 try argv.append(try allocPrint(arena, "-SUBSYSTEM:native{s}", .{
1268 subsystem_suffix,
1269 }));
1270 break :mode .win32;
1271 },
1272 .Posix => {
1273 try argv.append(try allocPrint(arena, "-SUBSYSTEM:posix{s}", .{
1274 subsystem_suffix,
1275 }));
1276 break :mode .win32;
1277 },
1278 .Windows => {
1279 try argv.append(try allocPrint(arena, "-SUBSYSTEM:windows{s}", .{
1280 subsystem_suffix,
1281 }));
1282 break :mode .win32;
1283 },
1284 }
1285 } else if (target.os.tag == .uefi) {
1286 break :mode .uefi;
1287 } else {
1288 break :mode .win32;
1289 }
1290 };
1462inline fn getDataDirectoryHeadersOffset(self: Coff) u32 {
1463 const msdos_hdr_size = msdos_stub.len + 4;
1464 return @intCast(u32, msdos_hdr_size + @sizeOf(coff.CoffHeader) + self.getOptionalHeaderSize());
1465}
12911466
1292 switch (mode) {
1293 .uefi => try argv.appendSlice(&[_][]const u8{
1294 "-BASE:0",
1295 "-ENTRY:EfiMain",
1296 "-OPT:REF",
1297 "-SAFESEH:NO",
1298 "-MERGE:.rdata=.data",
1299 "-ALIGN:32",
1300 "-NODEFAULTLIB",
1301 "-SECTION:.xdata,D",
1302 }),
1303 .win32 => {
1304 if (link_in_crt) {
1305 if (target.abi.isGnu()) {
1306 try argv.append("-lldmingw");
1307
1308 if (target.cpu.arch == .i386) {
1309 try argv.append("-ALTERNATENAME:__image_base__=___ImageBase");
1310 } else {
1311 try argv.append("-ALTERNATENAME:__image_base__=__ImageBase");
1312 }
1313
1314 if (is_dyn_lib) {
1315 try argv.append(try comp.get_libc_crt_file(arena, "dllcrt2.obj"));
1316 if (target.cpu.arch == .i386) {
1317 try argv.append("-ALTERNATENAME:__DllMainCRTStartup@12=_DllMainCRTStartup@12");
1318 } else {
1319 try argv.append("-ALTERNATENAME:_DllMainCRTStartup=DllMainCRTStartup");
1320 }
1321 } else {
1322 try argv.append(try comp.get_libc_crt_file(arena, "crt2.obj"));
1323 }
1324
1325 try argv.append(try comp.get_libc_crt_file(arena, "mingw32.lib"));
1326 try argv.append(try comp.get_libc_crt_file(arena, "mingwex.lib"));
1327 try argv.append(try comp.get_libc_crt_file(arena, "msvcrt-os.lib"));
1328
1329 for (mingw.always_link_libs) |name| {
1330 if (!self.base.options.system_libs.contains(name)) {
1331 const lib_basename = try allocPrint(arena, "{s}.lib", .{name});
1332 try argv.append(try comp.get_libc_crt_file(arena, lib_basename));
1333 }
1334 }
1335 } else {
1336 const lib_str = switch (self.base.options.link_mode) {
1337 .Dynamic => "",
1338 .Static => "lib",
1339 };
1340 const d_str = switch (self.base.options.optimize_mode) {
1341 .Debug => "d",
1342 else => "",
1343 };
1344 switch (self.base.options.link_mode) {
1345 .Static => try argv.append(try allocPrint(arena, "libcmt{s}.lib", .{d_str})),
1346 .Dynamic => try argv.append(try allocPrint(arena, "msvcrt{s}.lib", .{d_str})),
1347 }
1348
1349 try argv.append(try allocPrint(arena, "{s}vcruntime{s}.lib", .{ lib_str, d_str }));
1350 try argv.append(try allocPrint(arena, "{s}ucrt{s}.lib", .{ lib_str, d_str }));
1351
1352 //Visual C++ 2015 Conformance Changes
1353 //https://msdn.microsoft.com/en-us/library/bb531344.aspx
1354 try argv.append("legacy_stdio_definitions.lib");
1355
1356 // msvcrt depends on kernel32 and ntdll
1357 try argv.append("kernel32.lib");
1358 try argv.append("ntdll.lib");
1359 }
1360 } else {
1361 try argv.append("-NODEFAULTLIB");
1362 if (!is_lib) {
1363 if (self.base.options.module) |module| {
1364 if (module.stage1_flags.have_winmain_crt_startup) {
1365 try argv.append("-ENTRY:WinMainCRTStartup");
1366 } else {
1367 try argv.append("-ENTRY:wWinMainCRTStartup");
1368 }
1369 } else {
1370 try argv.append("-ENTRY:wWinMainCRTStartup");
1371 }
1372 }
1373 }
1374 },
1375 }
1467inline fn getSectionHeadersOffset(self: Coff) u32 {
1468 return self.getDataDirectoryHeadersOffset() + self.getDataDirectoryHeadersSize();
1469}
13761470
1377 // libc++ dep
1378 if (self.base.options.link_libcpp) {
1379 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
1380 try argv.append(comp.libcxx_static_lib.?.full_object_path);
1381 }
1471inline fn getSizeOfImage(self: Coff) u32 {
1472 var image_size: u32 = mem.alignForwardGeneric(u32, self.getSizeOfHeaders(), self.page_size);
1473 for (self.sections.items(.header)) |header| {
1474 image_size += mem.alignForwardGeneric(u32, header.virtual_size, self.page_size);
1475 }
1476 return image_size;
1477}
13821478
1383 // libunwind dep
1384 if (self.base.options.link_libunwind) {
1385 try argv.append(comp.libunwind_static_lib.?.full_object_path);
1386 }
1479/// Returns symbol location corresponding to the set entrypoint (if any).
1480pub fn getEntryPoint(self: Coff) ?SymbolWithLoc {
1481 const entry_name = self.base.options.entry orelse "_start"; // TODO this is incomplete
1482 return self.globals.get(entry_name);
1483}
13871484
1388 if (is_exe_or_dyn_lib and !self.base.options.skip_linker_dependencies) {
1389 if (!self.base.options.link_libc) {
1390 if (comp.libc_static_lib) |lib| {
1391 try argv.append(lib.full_object_path);
1392 }
1393 }
1394 // MinGW doesn't provide libssp symbols
1395 if (target.abi.isGnu()) {
1396 if (comp.libssp_static_lib) |lib| {
1397 try argv.append(lib.full_object_path);
1398 }
1399 }
1400 // MSVC compiler_rt is missing some stuff, so we build it unconditionally but
1401 // and rely on weak linkage to allow MSVC compiler_rt functions to override ours.
1402 if (comp.compiler_rt_lib) |lib| {
1403 try argv.append(lib.full_object_path);
1404 }
1405 }
1485/// Returns pointer-to-symbol described by `sym_with_loc` descriptor.
1486pub fn getSymbolPtr(self: *Coff, sym_loc: SymbolWithLoc) *coff.Symbol {
1487 assert(sym_loc.file == null); // TODO linking object files
1488 return &self.locals.items[sym_loc.sym_index];
1489}
14061490
1407 try argv.ensureUnusedCapacity(self.base.options.system_libs.count());
1408 for (self.base.options.system_libs.keys()) |key| {
1409 const lib_basename = try allocPrint(arena, "{s}.lib", .{key});
1410 if (comp.crt_files.get(lib_basename)) |crt_file| {
1411 argv.appendAssumeCapacity(crt_file.full_object_path);
1412 continue;
1413 }
1414 if (try self.findLib(arena, lib_basename)) |full_path| {
1415 argv.appendAssumeCapacity(full_path);
1416 continue;
1417 }
1418 if (target.abi.isGnu()) {
1419 const fallback_name = try allocPrint(arena, "lib{s}.dll.a", .{key});
1420 if (try self.findLib(arena, fallback_name)) |full_path| {
1421 argv.appendAssumeCapacity(full_path);
1422 continue;
1423 }
1424 }
1425 log.err("DLL import library for -l{s} not found", .{key});
1426 return error.DllImportLibraryNotFound;
1427 }
1491/// Returns symbol described by `sym_with_loc` descriptor.
1492pub fn getSymbol(self: *const Coff, sym_loc: SymbolWithLoc) *const coff.Symbol {
1493 assert(sym_loc.file == null); // TODO linking object files
1494 return &self.locals.items[sym_loc.sym_index];
1495}
14281496
1429 if (self.base.options.verbose_link) {
1430 // Skip over our own name so that the LLD linker name is the first argv item.
1431 Compilation.dump_argv(argv.items[1..]);
1432 }
1497/// Returns name of the symbol described by `sym_with_loc` descriptor.
1498pub fn getSymbolName(self: *const Coff, sym_loc: SymbolWithLoc) []const u8 {
1499 assert(sym_loc.file == null); // TODO linking object files
1500 const sym = self.getSymbol(sym_loc);
1501 const offset = sym.getNameOffset() orelse return sym.getName().?;
1502 return self.strtab.get(offset).?;
1503}
14331504
1434 if (std.process.can_spawn) {
1435 // If possible, we run LLD as a child process because it does not always
1436 // behave properly as a library, unfortunately.
1437 // https://github.com/ziglang/zig/issues/3825
1438 var child = std.ChildProcess.init(argv.items, arena);
1439 if (comp.clang_passthrough_mode) {
1440 child.stdin_behavior = .Inherit;
1441 child.stdout_behavior = .Inherit;
1442 child.stderr_behavior = .Inherit;
1443
1444 const term = child.spawnAndWait() catch |err| {
1445 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1446 return error.UnableToSpawnSelf;
1447 };
1448 switch (term) {
1449 .Exited => |code| {
1450 if (code != 0) {
1451 std.process.exit(code);
1452 }
1453 },
1454 else => std.process.abort(),
1455 }
1456 } else {
1457 child.stdin_behavior = .Ignore;
1458 child.stdout_behavior = .Ignore;
1459 child.stderr_behavior = .Pipe;
1460
1461 try child.spawn();
1462
1463 const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024);
1464
1465 const term = child.wait() catch |err| {
1466 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
1467 return error.UnableToSpawnSelf;
1468 };
1469
1470 switch (term) {
1471 .Exited => |code| {
1472 if (code != 0) {
1473 // TODO parse this output and surface with the Compilation API rather than
1474 // directly outputting to stderr here.
1475 std.debug.print("{s}", .{stderr});
1476 return error.LLDReportedFailure;
1477 }
1478 },
1479 else => {
1480 log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
1481 return error.LLDCrashed;
1482 },
1483 }
1505/// Returns atom if there is an atom referenced by the symbol described by `sym_with_loc` descriptor.
1506/// Returns null on failure.
1507pub fn getAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom {
1508 assert(sym_loc.file == null); // TODO linking with object files
1509 return self.atom_by_index_table.get(sym_loc.sym_index);
1510}
14841511
1485 if (stderr.len != 0) {
1486 log.warn("unexpected LLD stderr:\n{s}", .{stderr});
1487 }
1488 }
1489 } else {
1490 const exit_code = try lldMain(arena, argv.items, false);
1491 if (exit_code != 0) {
1492 if (comp.clang_passthrough_mode) {
1493 std.process.exit(exit_code);
1494 } else {
1495 return error.LLDReportedFailure;
1496 }
1497 }
1498 }
1499 }
1512/// Returns GOT atom that references `sym_with_loc` if one exists.
1513/// Returns null otherwise.
1514pub fn getGotAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom {
1515 const got_index = self.got_entries.get(sym_loc) orelse return null;
1516 return self.atom_by_index_table.get(got_index);
1517}
15001518
1501 if (!self.base.options.disable_lld_caching) {
1502 // Update the file with the digest. If it fails we can continue; it only
1503 // means that the next invocation will have an unnecessary cache miss.
1504 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
1505 log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)});
1506 };
1507 // Again failure here only means an unnecessary cache miss.
1508 man.writeManifest() catch |err| {
1509 log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)});
1510 };
1511 // We hang on to this lock so that the output file path can be used without
1512 // other processes clobbering it.
1513 self.base.lock = man.toOwnedLock();
1519fn setSectionName(self: *Coff, header: *coff.SectionHeader, name: []const u8) !void {
1520 if (name.len <= 8) {
1521 mem.copy(u8, &header.name, name);
1522 mem.set(u8, header.name[name.len..], 0);
1523 return;
15141524 }
1525 const offset = try self.strtab.insert(self.base.allocator, name);
1526 const name_offset = fmt.bufPrint(&header.name, "/{d}", .{offset}) catch unreachable;
1527 mem.set(u8, header.name[name_offset.len..], 0);
15151528}
15161529
1517fn findLib(self: *Coff, arena: Allocator, name: []const u8) !?[]const u8 {
1518 for (self.base.options.lib_dirs) |lib_dir| {
1519 const full_path = try fs.path.join(arena, &.{ lib_dir, name });
1520 fs.cwd().access(full_path, .{}) catch |err| switch (err) {
1521 error.FileNotFound => continue,
1522 else => |e| return e,
1523 };
1524 return full_path;
1530fn setSymbolName(self: *Coff, symbol: *coff.Symbol, name: []const u8) !void {
1531 if (name.len <= 8) {
1532 mem.copy(u8, &symbol.name, name);
1533 mem.set(u8, symbol.name[name.len..], 0);
1534 return;
15251535 }
1526 return null;
1536 const offset = try self.strtab.insert(self.base.allocator, name);
1537 mem.set(u8, symbol.name[0..4], 0);
1538 mem.writeIntLittle(u32, symbol.name[4..8], offset);
15271539}
15281540
1529pub fn getDeclVAddr(
1530 self: *Coff,
1531 decl_index: Module.Decl.Index,
1532 reloc_info: link.File.RelocInfo,
1533) !u64 {
1534 _ = reloc_info;
1535 const mod = self.base.options.module.?;
1536 const decl = mod.declPtr(decl_index);
1537 assert(self.llvm_object == null);
1538 return self.text_section_virtual_address + decl.link.coff.text_offset;
1541fn logSymAttributes(sym: *const coff.Symbol, buf: *[4]u8) []const u8 {
1542 mem.set(u8, buf[0..4], '_');
1543 switch (sym.section_number) {
1544 .UNDEFINED => {
1545 buf[3] = 'u';
1546 switch (sym.storage_class) {
1547 .EXTERNAL => buf[1] = 'e',
1548 .WEAK_EXTERNAL => buf[1] = 'w',
1549 .NULL => {},
1550 else => unreachable,
1551 }
1552 },
1553 .ABSOLUTE => unreachable, // handle ABSOLUTE
1554 .DEBUG => unreachable,
1555 else => {
1556 buf[0] = 's';
1557 switch (sym.storage_class) {
1558 .EXTERNAL => buf[1] = 'e',
1559 .WEAK_EXTERNAL => buf[1] = 'w',
1560 .NULL => {},
1561 else => unreachable,
1562 }
1563 },
1564 }
1565 return buf[0..];
15391566}
15401567
1541pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void {
1542 _ = self;
1543 _ = module;
1544 _ = decl;
1545 // TODO Implement this
1546}
1568fn logSymtab(self: *Coff) void {
1569 var buf: [4]u8 = undefined;
1570
1571 log.debug("symtab:", .{});
1572 log.debug(" object(null)", .{});
1573 for (self.locals.items) |*sym, sym_id| {
1574 const where = if (sym.section_number == .UNDEFINED) "ord" else "sect";
1575 const def_index: u16 = switch (sym.section_number) {
1576 .UNDEFINED => 0, // TODO
1577 .ABSOLUTE => unreachable, // TODO
1578 .DEBUG => unreachable, // TODO
1579 else => @enumToInt(sym.section_number),
1580 };
1581 log.debug(" %{d}: {?s} @{x} in {s}({d}), {s}", .{
1582 sym_id,
1583 self.getSymbolName(.{ .sym_index = @intCast(u32, sym_id), .file = null }),
1584 sym.value,
1585 where,
1586 def_index,
1587 logSymAttributes(sym, &buf),
1588 });
1589 }
15471590
1548pub fn deinit(self: *Coff) void {
1549 if (build_options.have_llvm) {
1550 if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator);
1591 log.debug("globals table:", .{});
1592 for (self.globals.keys()) |name, id| {
1593 const value = self.globals.values()[id];
1594 log.debug(" {s} => %{d} in object({?d})", .{ name, value.sym_index, value.file });
15511595 }
15521596
1553 self.text_block_free_list.deinit(self.base.allocator);
1554 self.offset_table.deinit(self.base.allocator);
1555 self.offset_table_free_list.deinit(self.base.allocator);
1597 log.debug("GOT entries:", .{});
1598 for (self.got_entries.keys()) |target, i| {
1599 const got_sym = self.getSymbol(.{ .sym_index = self.got_entries.values()[i], .file = null });
1600 const target_sym = self.getSymbol(target);
1601 if (target_sym.section_number == .UNDEFINED) {
1602 log.debug(" {d}@{x} => import('{s}')", .{
1603 i,
1604 got_sym.value,
1605 self.getSymbolName(target),
1606 });
1607 } else {
1608 log.debug(" {d}@{x} => local(%{d}) in object({?d}) {s}", .{
1609 i,
1610 got_sym.value,
1611 target.sym_index,
1612 target.file,
1613 logSymAttributes(target_sym, &buf),
1614 });
1615 }
1616 }
15561617}
src/link/Coff/Atom.zig created+110
......@@ -0,0 +1,110 @@
1const Atom = @This();
2
3const std = @import("std");
4const coff = std.coff;
5
6const Allocator = std.mem.Allocator;
7
8const Coff = @import("../Coff.zig");
9const Reloc = Coff.Reloc;
10const SymbolWithLoc = Coff.SymbolWithLoc;
11
12/// Each decl always gets a local symbol with the fully qualified name.
13/// The vaddr and size are found here directly.
14/// The file offset is found by computing the vaddr offset from the section vaddr
15/// the symbol references, and adding that to the file offset of the section.
16/// If this field is 0, it means the codegen size = 0 and there is no symbol or
17/// offset table entry.
18sym_index: u32,
19
20/// null means symbol defined by Zig source.
21file: ?u32,
22
23/// Used size of the atom
24size: u32,
25
26/// Alignment of the atom
27alignment: u32,
28
29/// Points to the previous and next neighbors, based on the `text_offset`.
30/// This can be used to find, for example, the capacity of this `Atom`.
31prev: ?*Atom,
32next: ?*Atom,
33
34pub const empty = Atom{
35 .sym_index = 0,
36 .file = null,
37 .size = 0,
38 .alignment = 0,
39 .prev = null,
40 .next = null,
41};
42
43pub fn deinit(self: *Atom, gpa: Allocator) void {
44 _ = self;
45 _ = gpa;
46}
47
48/// Returns symbol referencing this atom.
49pub fn getSymbol(self: Atom, coff_file: *const Coff) *const coff.Symbol {
50 return coff_file.getSymbol(.{
51 .sym_index = self.sym_index,
52 .file = self.file,
53 });
54}
55
56/// Returns pointer-to-symbol referencing this atom.
57pub fn getSymbolPtr(self: Atom, coff_file: *Coff) *coff.Symbol {
58 return coff_file.getSymbolPtr(.{
59 .sym_index = self.sym_index,
60 .file = self.file,
61 });
62}
63
64pub fn getSymbolWithLoc(self: Atom) SymbolWithLoc {
65 return .{ .sym_index = self.sym_index, .file = self.file };
66}
67
68/// Returns the name of this atom.
69pub fn getName(self: Atom, coff_file: *const Coff) []const u8 {
70 return coff_file.getSymbolName(.{
71 .sym_index = self.sym_index,
72 .file = self.file,
73 });
74}
75
76/// Returns how much room there is to grow in virtual address space.
77pub fn capacity(self: Atom, coff_file: *const Coff) u32 {
78 const self_sym = self.getSymbol(coff_file);
79 if (self.next) |next| {
80 const next_sym = next.getSymbol(coff_file);
81 return next_sym.value - self_sym.value;
82 } else {
83 // We are the last atom.
84 // The capacity is limited only by virtual address space.
85 return std.math.maxInt(u32) - self_sym.value;
86 }
87}
88
89pub fn freeListEligible(self: Atom, coff_file: *const Coff) bool {
90 // No need to keep a free list node for the last atom.
91 const next = self.next orelse return false;
92 const self_sym = self.getSymbol(coff_file);
93 const next_sym = next.getSymbol(coff_file);
94 const cap = next_sym.value - self_sym.value;
95 const ideal_cap = Coff.padToIdeal(self.size);
96 if (cap <= ideal_cap) return false;
97 const surplus = cap - ideal_cap;
98 return surplus >= Coff.min_text_capacity;
99}
100
101pub fn addRelocation(self: *Atom, coff_file: *Coff, reloc: Reloc) !void {
102 const gpa = coff_file.base.allocator;
103 // TODO causes a segfault on Windows
104 // log.debug("adding reloc of type {s} to target %{d}", .{ @tagName(reloc.@"type"), reloc.target.sym_index });
105 const gop = try coff_file.relocs.getOrPut(gpa, self);
106 if (!gop.found_existing) {
107 gop.value_ptr.* = .{};
108 }
109 try gop.value_ptr.append(gpa, reloc);
110}
src/link/Coff/Object.zig created+12
......@@ -0,0 +1,12 @@
1const Object = @This();
2
3const std = @import("std");
4const mem = std.mem;
5
6const Allocator = mem.Allocator;
7
8name: []const u8,
9
10pub fn deinit(self: *Object, gpa: Allocator) void {
11 gpa.free(self.name);
12}
src/link/Coff/lld.zig created+602
......@@ -0,0 +1,602 @@
1const std = @import("std");
2const build_options = @import("build_options");
3const allocPrint = std.fmt.allocPrint;
4const assert = std.debug.assert;
5const fs = std.fs;
6const log = std.log.scoped(.link);
7const mem = std.mem;
8
9const mingw = @import("../../mingw.zig");
10const link = @import("../../link.zig");
11const lldMain = @import("../../main.zig").lldMain;
12const trace = @import("../../tracy.zig").trace;
13
14const Allocator = mem.Allocator;
15
16const Cache = @import("../../Cache.zig");
17const Coff = @import("../Coff.zig");
18const Compilation = @import("../../Compilation.zig");
19
20pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !void {
21 const tracy = trace(@src());
22 defer tracy.end();
23
24 var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
25 defer arena_allocator.deinit();
26 const arena = arena_allocator.allocator();
27
28 const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type.
29 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
30
31 // If there is no Zig code to compile, then we should skip flushing the output file because it
32 // will not be part of the linker line anyway.
33 const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {
34 const use_stage1 = build_options.have_stage1 and self.base.options.use_stage1;
35 if (use_stage1) {
36 const obj_basename = try std.zig.binNameAlloc(arena, .{
37 .root_name = self.base.options.root_name,
38 .target = self.base.options.target,
39 .output_mode = .Obj,
40 });
41 switch (self.base.options.cache_mode) {
42 .incremental => break :blk try module.zig_cache_artifact_directory.join(
43 arena,
44 &[_][]const u8{obj_basename},
45 ),
46 .whole => break :blk try fs.path.join(arena, &.{
47 fs.path.dirname(full_out_path).?, obj_basename,
48 }),
49 }
50 }
51
52 try self.flushModule(comp, prog_node);
53
54 if (fs.path.dirname(full_out_path)) |dirname| {
55 break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? });
56 } else {
57 break :blk self.base.intermediary_basename.?;
58 }
59 } else null;
60
61 var sub_prog_node = prog_node.start("LLD Link", 0);
62 sub_prog_node.activate();
63 sub_prog_node.context.refresh();
64 defer sub_prog_node.end();
65
66 const is_lib = self.base.options.output_mode == .Lib;
67 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
68 const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
69 const link_in_crt = self.base.options.link_libc and is_exe_or_dyn_lib;
70 const target = self.base.options.target;
71
72 // See link/Elf.zig for comments on how this mechanism works.
73 const id_symlink_basename = "lld.id";
74
75 var man: Cache.Manifest = undefined;
76 defer if (!self.base.options.disable_lld_caching) man.deinit();
77
78 var digest: [Cache.hex_digest_len]u8 = undefined;
79
80 if (!self.base.options.disable_lld_caching) {
81 man = comp.cache_parent.obtain();
82 self.base.releaseLock();
83
84 comptime assert(Compilation.link_hash_implementation_version == 7);
85
86 for (self.base.options.objects) |obj| {
87 _ = try man.addFile(obj.path, null);
88 man.hash.add(obj.must_link);
89 }
90 for (comp.c_object_table.keys()) |key| {
91 _ = try man.addFile(key.status.success.object_path, null);
92 }
93 try man.addOptionalFile(module_obj_path);
94 man.hash.addOptionalBytes(self.base.options.entry);
95 man.hash.addOptional(self.base.options.stack_size_override);
96 man.hash.addOptional(self.base.options.image_base_override);
97 man.hash.addListOfBytes(self.base.options.lib_dirs);
98 man.hash.add(self.base.options.skip_linker_dependencies);
99 if (self.base.options.link_libc) {
100 man.hash.add(self.base.options.libc_installation != null);
101 if (self.base.options.libc_installation) |libc_installation| {
102 man.hash.addBytes(libc_installation.crt_dir.?);
103 if (target.abi == .msvc) {
104 man.hash.addBytes(libc_installation.msvc_lib_dir.?);
105 man.hash.addBytes(libc_installation.kernel32_lib_dir.?);
106 }
107 }
108 }
109 link.hashAddSystemLibs(&man.hash, self.base.options.system_libs);
110 man.hash.addListOfBytes(self.base.options.force_undefined_symbols.keys());
111 man.hash.addOptional(self.base.options.subsystem);
112 man.hash.add(self.base.options.is_test);
113 man.hash.add(self.base.options.tsaware);
114 man.hash.add(self.base.options.nxcompat);
115 man.hash.add(self.base.options.dynamicbase);
116 // strip does not need to go into the linker hash because it is part of the hash namespace
117 man.hash.addOptional(self.base.options.major_subsystem_version);
118 man.hash.addOptional(self.base.options.minor_subsystem_version);
119
120 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
121 _ = try man.hit();
122 digest = man.final();
123 var prev_digest_buf: [digest.len]u8 = undefined;
124 const prev_digest: []u8 = Cache.readSmallFile(
125 directory.handle,
126 id_symlink_basename,
127 &prev_digest_buf,
128 ) catch |err| blk: {
129 log.debug("COFF LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
130 // Handle this as a cache miss.
131 break :blk prev_digest_buf[0..0];
132 };
133 if (mem.eql(u8, prev_digest, &digest)) {
134 log.debug("COFF LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
135 // Hot diggity dog! The output binary is already there.
136 self.base.lock = man.toOwnedLock();
137 return;
138 }
139 log.debug("COFF LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });
140
141 // We are about to change the output file to be different, so we invalidate the build hash now.
142 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
143 error.FileNotFound => {},
144 else => |e| return e,
145 };
146 }
147
148 if (self.base.options.output_mode == .Obj) {
149 // LLD's COFF driver does not support the equivalent of `-r` so we do a simple file copy
150 // here. TODO: think carefully about how we can avoid this redundant operation when doing
151 // build-obj. See also the corresponding TODO in linkAsArchive.
152 const the_object_path = blk: {
153 if (self.base.options.objects.len != 0)
154 break :blk self.base.options.objects[0].path;
155
156 if (comp.c_object_table.count() != 0)
157 break :blk comp.c_object_table.keys()[0].status.success.object_path;
158
159 if (module_obj_path) |p|
160 break :blk p;
161
162 // TODO I think this is unreachable. Audit this situation when solving the above TODO
163 // regarding eliding redundant object -> object transformations.
164 return error.NoObjectsToLink;
165 };
166 // This can happen when using --enable-cache and using the stage1 backend. In this case
167 // we can skip the file copy.
168 if (!mem.eql(u8, the_object_path, full_out_path)) {
169 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
170 }
171 } else {
172 // Create an LLD command line and invoke it.
173 var argv = std.ArrayList([]const u8).init(self.base.allocator);
174 defer argv.deinit();
175 // We will invoke ourselves as a child process to gain access to LLD.
176 // This is necessary because LLD does not behave properly as a library -
177 // it calls exit() and does not reset all global data between invocations.
178 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "lld-link" });
179
180 try argv.append("-ERRORLIMIT:0");
181 try argv.append("-NOLOGO");
182 if (!self.base.options.strip) {
183 try argv.append("-DEBUG");
184 }
185 if (self.base.options.lto) {
186 switch (self.base.options.optimize_mode) {
187 .Debug => {},
188 .ReleaseSmall => try argv.append("-OPT:lldlto=2"),
189 .ReleaseFast, .ReleaseSafe => try argv.append("-OPT:lldlto=3"),
190 }
191 }
192 if (self.base.options.output_mode == .Exe) {
193 const stack_size = self.base.options.stack_size_override orelse 16777216;
194 try argv.append(try allocPrint(arena, "-STACK:{d}", .{stack_size}));
195 }
196 if (self.base.options.image_base_override) |image_base| {
197 try argv.append(try std.fmt.allocPrint(arena, "-BASE:{d}", .{image_base}));
198 }
199
200 if (target.cpu.arch == .i386) {
201 try argv.append("-MACHINE:X86");
202 } else if (target.cpu.arch == .x86_64) {
203 try argv.append("-MACHINE:X64");
204 } else if (target.cpu.arch.isARM()) {
205 if (target.cpu.arch.ptrBitWidth() == 32) {
206 try argv.append("-MACHINE:ARM");
207 } else {
208 try argv.append("-MACHINE:ARM64");
209 }
210 }
211
212 for (self.base.options.force_undefined_symbols.keys()) |symbol| {
213 try argv.append(try allocPrint(arena, "-INCLUDE:{s}", .{symbol}));
214 }
215
216 if (is_dyn_lib) {
217 try argv.append("-DLL");
218 }
219
220 if (self.base.options.entry) |entry| {
221 try argv.append(try allocPrint(arena, "-ENTRY:{s}", .{entry}));
222 }
223
224 if (self.base.options.tsaware) {
225 try argv.append("-tsaware");
226 }
227 if (self.base.options.nxcompat) {
228 try argv.append("-nxcompat");
229 }
230 if (self.base.options.dynamicbase) {
231 try argv.append("-dynamicbase");
232 }
233
234 try argv.append(try allocPrint(arena, "-OUT:{s}", .{full_out_path}));
235
236 if (self.base.options.implib_emit) |emit| {
237 const implib_out_path = try emit.directory.join(arena, &[_][]const u8{emit.sub_path});
238 try argv.append(try allocPrint(arena, "-IMPLIB:{s}", .{implib_out_path}));
239 }
240
241 if (self.base.options.link_libc) {
242 if (self.base.options.libc_installation) |libc_installation| {
243 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.crt_dir.?}));
244
245 if (target.abi == .msvc) {
246 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.msvc_lib_dir.?}));
247 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.kernel32_lib_dir.?}));
248 }
249 }
250 }
251
252 for (self.base.options.lib_dirs) |lib_dir| {
253 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{lib_dir}));
254 }
255
256 try argv.ensureUnusedCapacity(self.base.options.objects.len);
257 for (self.base.options.objects) |obj| {
258 if (obj.must_link) {
259 argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{s}", .{obj.path}));
260 } else {
261 argv.appendAssumeCapacity(obj.path);
262 }
263 }
264
265 for (comp.c_object_table.keys()) |key| {
266 try argv.append(key.status.success.object_path);
267 }
268
269 if (module_obj_path) |p| {
270 try argv.append(p);
271 }
272
273 const resolved_subsystem: ?std.Target.SubSystem = blk: {
274 if (self.base.options.subsystem) |explicit| break :blk explicit;
275 switch (target.os.tag) {
276 .windows => {
277 if (self.base.options.module) |module| {
278 if (module.stage1_flags.have_dllmain_crt_startup or is_dyn_lib)
279 break :blk null;
280 if (module.stage1_flags.have_c_main or self.base.options.is_test or
281 module.stage1_flags.have_winmain_crt_startup or
282 module.stage1_flags.have_wwinmain_crt_startup)
283 {
284 break :blk .Console;
285 }
286 if (module.stage1_flags.have_winmain or module.stage1_flags.have_wwinmain)
287 break :blk .Windows;
288 }
289 },
290 .uefi => break :blk .EfiApplication,
291 else => {},
292 }
293 break :blk null;
294 };
295
296 const Mode = enum { uefi, win32 };
297 const mode: Mode = mode: {
298 if (resolved_subsystem) |subsystem| {
299 const subsystem_suffix = ss: {
300 if (self.base.options.major_subsystem_version) |major| {
301 if (self.base.options.minor_subsystem_version) |minor| {
302 break :ss try allocPrint(arena, ",{d}.{d}", .{ major, minor });
303 } else {
304 break :ss try allocPrint(arena, ",{d}", .{major});
305 }
306 }
307 break :ss "";
308 };
309
310 switch (subsystem) {
311 .Console => {
312 try argv.append(try allocPrint(arena, "-SUBSYSTEM:console{s}", .{
313 subsystem_suffix,
314 }));
315 break :mode .win32;
316 },
317 .EfiApplication => {
318 try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_application{s}", .{
319 subsystem_suffix,
320 }));
321 break :mode .uefi;
322 },
323 .EfiBootServiceDriver => {
324 try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_boot_service_driver{s}", .{
325 subsystem_suffix,
326 }));
327 break :mode .uefi;
328 },
329 .EfiRom => {
330 try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_rom{s}", .{
331 subsystem_suffix,
332 }));
333 break :mode .uefi;
334 },
335 .EfiRuntimeDriver => {
336 try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_runtime_driver{s}", .{
337 subsystem_suffix,
338 }));
339 break :mode .uefi;
340 },
341 .Native => {
342 try argv.append(try allocPrint(arena, "-SUBSYSTEM:native{s}", .{
343 subsystem_suffix,
344 }));
345 break :mode .win32;
346 },
347 .Posix => {
348 try argv.append(try allocPrint(arena, "-SUBSYSTEM:posix{s}", .{
349 subsystem_suffix,
350 }));
351 break :mode .win32;
352 },
353 .Windows => {
354 try argv.append(try allocPrint(arena, "-SUBSYSTEM:windows{s}", .{
355 subsystem_suffix,
356 }));
357 break :mode .win32;
358 },
359 }
360 } else if (target.os.tag == .uefi) {
361 break :mode .uefi;
362 } else {
363 break :mode .win32;
364 }
365 };
366
367 switch (mode) {
368 .uefi => try argv.appendSlice(&[_][]const u8{
369 "-BASE:0",
370 "-ENTRY:EfiMain",
371 "-OPT:REF",
372 "-SAFESEH:NO",
373 "-MERGE:.rdata=.data",
374 "-ALIGN:32",
375 "-NODEFAULTLIB",
376 "-SECTION:.xdata,D",
377 }),
378 .win32 => {
379 if (link_in_crt) {
380 if (target.abi.isGnu()) {
381 try argv.append("-lldmingw");
382
383 if (target.cpu.arch == .i386) {
384 try argv.append("-ALTERNATENAME:__image_base__=___ImageBase");
385 } else {
386 try argv.append("-ALTERNATENAME:__image_base__=__ImageBase");
387 }
388
389 if (is_dyn_lib) {
390 try argv.append(try comp.get_libc_crt_file(arena, "dllcrt2.obj"));
391 if (target.cpu.arch == .i386) {
392 try argv.append("-ALTERNATENAME:__DllMainCRTStartup@12=_DllMainCRTStartup@12");
393 } else {
394 try argv.append("-ALTERNATENAME:_DllMainCRTStartup=DllMainCRTStartup");
395 }
396 } else {
397 try argv.append(try comp.get_libc_crt_file(arena, "crt2.obj"));
398 }
399
400 try argv.append(try comp.get_libc_crt_file(arena, "mingw32.lib"));
401 try argv.append(try comp.get_libc_crt_file(arena, "mingwex.lib"));
402 try argv.append(try comp.get_libc_crt_file(arena, "msvcrt-os.lib"));
403
404 for (mingw.always_link_libs) |name| {
405 if (!self.base.options.system_libs.contains(name)) {
406 const lib_basename = try allocPrint(arena, "{s}.lib", .{name});
407 try argv.append(try comp.get_libc_crt_file(arena, lib_basename));
408 }
409 }
410 } else {
411 const lib_str = switch (self.base.options.link_mode) {
412 .Dynamic => "",
413 .Static => "lib",
414 };
415 const d_str = switch (self.base.options.optimize_mode) {
416 .Debug => "d",
417 else => "",
418 };
419 switch (self.base.options.link_mode) {
420 .Static => try argv.append(try allocPrint(arena, "libcmt{s}.lib", .{d_str})),
421 .Dynamic => try argv.append(try allocPrint(arena, "msvcrt{s}.lib", .{d_str})),
422 }
423
424 try argv.append(try allocPrint(arena, "{s}vcruntime{s}.lib", .{ lib_str, d_str }));
425 try argv.append(try allocPrint(arena, "{s}ucrt{s}.lib", .{ lib_str, d_str }));
426
427 //Visual C++ 2015 Conformance Changes
428 //https://msdn.microsoft.com/en-us/library/bb531344.aspx
429 try argv.append("legacy_stdio_definitions.lib");
430
431 // msvcrt depends on kernel32 and ntdll
432 try argv.append("kernel32.lib");
433 try argv.append("ntdll.lib");
434 }
435 } else {
436 try argv.append("-NODEFAULTLIB");
437 if (!is_lib) {
438 if (self.base.options.module) |module| {
439 if (module.stage1_flags.have_winmain_crt_startup) {
440 try argv.append("-ENTRY:WinMainCRTStartup");
441 } else {
442 try argv.append("-ENTRY:wWinMainCRTStartup");
443 }
444 } else {
445 try argv.append("-ENTRY:wWinMainCRTStartup");
446 }
447 }
448 }
449 },
450 }
451
452 // libc++ dep
453 if (self.base.options.link_libcpp) {
454 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
455 try argv.append(comp.libcxx_static_lib.?.full_object_path);
456 }
457
458 // libunwind dep
459 if (self.base.options.link_libunwind) {
460 try argv.append(comp.libunwind_static_lib.?.full_object_path);
461 }
462
463 if (is_exe_or_dyn_lib and !self.base.options.skip_linker_dependencies) {
464 if (!self.base.options.link_libc) {
465 if (comp.libc_static_lib) |lib| {
466 try argv.append(lib.full_object_path);
467 }
468 }
469 // MinGW doesn't provide libssp symbols
470 if (target.abi.isGnu()) {
471 if (comp.libssp_static_lib) |lib| {
472 try argv.append(lib.full_object_path);
473 }
474 }
475 // MSVC compiler_rt is missing some stuff, so we build it unconditionally but
476 // and rely on weak linkage to allow MSVC compiler_rt functions to override ours.
477 if (comp.compiler_rt_lib) |lib| {
478 try argv.append(lib.full_object_path);
479 }
480 }
481
482 try argv.ensureUnusedCapacity(self.base.options.system_libs.count());
483 for (self.base.options.system_libs.keys()) |key| {
484 const lib_basename = try allocPrint(arena, "{s}.lib", .{key});
485 if (comp.crt_files.get(lib_basename)) |crt_file| {
486 argv.appendAssumeCapacity(crt_file.full_object_path);
487 continue;
488 }
489 if (try findLib(arena, lib_basename, self.base.options.lib_dirs)) |full_path| {
490 argv.appendAssumeCapacity(full_path);
491 continue;
492 }
493 if (target.abi.isGnu()) {
494 const fallback_name = try allocPrint(arena, "lib{s}.dll.a", .{key});
495 if (try findLib(arena, fallback_name, self.base.options.lib_dirs)) |full_path| {
496 argv.appendAssumeCapacity(full_path);
497 continue;
498 }
499 }
500 log.err("DLL import library for -l{s} not found", .{key});
501 return error.DllImportLibraryNotFound;
502 }
503
504 if (self.base.options.verbose_link) {
505 // Skip over our own name so that the LLD linker name is the first argv item.
506 Compilation.dump_argv(argv.items[1..]);
507 }
508
509 if (std.process.can_spawn) {
510 // If possible, we run LLD as a child process because it does not always
511 // behave properly as a library, unfortunately.
512 // https://github.com/ziglang/zig/issues/3825
513 var child = std.ChildProcess.init(argv.items, arena);
514 if (comp.clang_passthrough_mode) {
515 child.stdin_behavior = .Inherit;
516 child.stdout_behavior = .Inherit;
517 child.stderr_behavior = .Inherit;
518
519 const term = child.spawnAndWait() catch |err| {
520 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
521 return error.UnableToSpawnSelf;
522 };
523 switch (term) {
524 .Exited => |code| {
525 if (code != 0) {
526 std.process.exit(code);
527 }
528 },
529 else => std.process.abort(),
530 }
531 } else {
532 child.stdin_behavior = .Ignore;
533 child.stdout_behavior = .Ignore;
534 child.stderr_behavior = .Pipe;
535
536 try child.spawn();
537
538 const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024);
539
540 const term = child.wait() catch |err| {
541 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
542 return error.UnableToSpawnSelf;
543 };
544
545 switch (term) {
546 .Exited => |code| {
547 if (code != 0) {
548 // TODO parse this output and surface with the Compilation API rather than
549 // directly outputting to stderr here.
550 std.debug.print("{s}", .{stderr});
551 return error.LLDReportedFailure;
552 }
553 },
554 else => {
555 log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
556 return error.LLDCrashed;
557 },
558 }
559
560 if (stderr.len != 0) {
561 log.warn("unexpected LLD stderr:\n{s}", .{stderr});
562 }
563 }
564 } else {
565 const exit_code = try lldMain(arena, argv.items, false);
566 if (exit_code != 0) {
567 if (comp.clang_passthrough_mode) {
568 std.process.exit(exit_code);
569 } else {
570 return error.LLDReportedFailure;
571 }
572 }
573 }
574 }
575
576 if (!self.base.options.disable_lld_caching) {
577 // Update the file with the digest. If it fails we can continue; it only
578 // means that the next invocation will have an unnecessary cache miss.
579 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
580 log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)});
581 };
582 // Again failure here only means an unnecessary cache miss.
583 man.writeManifest() catch |err| {
584 log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)});
585 };
586 // We hang on to this lock so that the output file path can be used without
587 // other processes clobbering it.
588 self.base.lock = man.toOwnedLock();
589 }
590}
591
592fn findLib(arena: Allocator, name: []const u8, lib_dirs: []const []const u8) !?[]const u8 {
593 for (lib_dirs) |lib_dir| {
594 const full_path = try fs.path.join(arena, &.{ lib_dir, name });
595 fs.cwd().access(full_path, .{}) catch |err| switch (err) {
596 error.FileNotFound => continue,
597 else => |e| return e,
598 };
599 return full_path;
600 }
601 return null;
602}
src/link/MachO.zig+1-2
......@@ -26,7 +26,7 @@ const trace = @import("../tracy.zig").trace;
2626const Air = @import("../Air.zig");
2727const Allocator = mem.Allocator;
2828const Archive = @import("MachO/Archive.zig");
29const Atom = @import("MachO/Atom.zig");
29pub const Atom = @import("MachO/Atom.zig");
3030const Cache = @import("../Cache.zig");
3131const CodeSignature = @import("MachO/CodeSignature.zig");
3232const Compilation = @import("../Compilation.zig");
......@@ -44,7 +44,6 @@ const Type = @import("../type.zig").Type;
4444const TypedValue = @import("../TypedValue.zig");
4545const Value = @import("../value.zig").Value;
4646
47pub const TextBlock = Atom;
4847pub const DebugSymbols = @import("MachO/DebugSymbols.zig");
4948
5049pub const base_tag: File.Tag = File.Tag.macho;
src/link/MachO/DebugSymbols.zig-1
......@@ -18,7 +18,6 @@ const Dwarf = @import("../Dwarf.zig");
1818const MachO = @import("../MachO.zig");
1919const Module = @import("../../Module.zig");
2020const StringTable = @import("../strtab.zig").StringTable;
21const TextBlock = MachO.TextBlock;
2221const Type = @import("../../type.zig").Type;
2322
2423base: *MachO,
src/link/Wasm.zig+8-13
......@@ -2836,24 +2836,19 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
28362836 try argv.append(entry);
28372837 }
28382838
2839 if (self.base.options.output_mode == .Exe) {
2840 // Increase the default stack size to a more reasonable value of 1MB instead of
2841 // the default of 1 Wasm page being 64KB, unless overridden by the user.
2842 try argv.append("-z");
2843 const stack_size = self.base.options.stack_size_override orelse 1048576;
2844 const arg = try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size});
2845 try argv.append(arg);
2839 // Increase the default stack size to a more reasonable value of 1MB instead of
2840 // the default of 1 Wasm page being 64KB, unless overridden by the user.
2841 try argv.append("-z");
2842 const stack_size = self.base.options.stack_size_override orelse wasm.page_size * 16;
2843 const arg = try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size});
2844 try argv.append(arg);
28462845
2846 if (self.base.options.output_mode == .Exe) {
28472847 if (self.base.options.wasi_exec_model == .reactor) {
28482848 // Reactor execution model does not have _start so lld doesn't look for it.
28492849 try argv.append("--no-entry");
28502850 }
2851 } else {
2852 if (self.base.options.stack_size_override) |stack_size| {
2853 try argv.append("-z");
2854 const arg = try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size});
2855 try argv.append(arg);
2856 }
2851 } else if (self.base.options.entry == null) {
28572852 try argv.append("--no-entry"); // So lld doesn't look for _start.
28582853 }
28592854 try argv.appendSlice(&[_][]const u8{
src/link/strtab.zig+4
......@@ -109,5 +109,9 @@ pub fn StringTable(comptime log_scope: @Type(.EnumLiteral)) type {
109109 pub fn getAssumeExists(self: Self, off: u32) []const u8 {
110110 return self.get(off) orelse unreachable;
111111 }
112
113 pub fn len(self: Self) usize {
114 return self.buffer.items.len;
115 }
112116 };
113117}
src/main.zig+3-3
......@@ -3734,9 +3734,9 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
37343734 const self_exe_path = try introspect.findZigExePath(arena);
37353735
37363736 var build_file: ?[]const u8 = null;
3737 var override_lib_dir: ?[]const u8 = null;
3738 var override_global_cache_dir: ?[]const u8 = null;
3739 var override_local_cache_dir: ?[]const u8 = null;
3737 var override_lib_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LIB_DIR");
3738 var override_global_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_GLOBAL_CACHE_DIR");
3739 var override_local_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LOCAL_CACHE_DIR");
37403740 var child_argv = std.ArrayList([]const u8).init(arena);
37413741
37423742 const argv_index_exe = child_argv.items.len;
src/mingw.zig+4-4
......@@ -6,7 +6,6 @@ const assert = std.debug.assert;
66const log = std.log.scoped(.mingw);
77
88const builtin = @import("builtin");
9const target_util = @import("target.zig");
109const Compilation = @import("Compilation.zig");
1110const build_options = @import("build_options");
1211const Cache = @import("Cache.zig");
......@@ -404,11 +403,12 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
404403 });
405404 errdefer comp.gpa.free(lib_final_path);
406405
407 const llvm = @import("codegen/llvm/bindings.zig");
408 const arch_type = target_util.archToLLVM(target.cpu.arch);
406 const llvm_bindings = @import("codegen/llvm/bindings.zig");
407 const llvm = @import("codegen/llvm.zig");
408 const arch_tag = llvm.targetArch(target.cpu.arch);
409409 const def_final_path_z = try arena.dupeZ(u8, def_final_path);
410410 const lib_final_path_z = try arena.dupeZ(u8, lib_final_path);
411 if (llvm.WriteImportLibrary(def_final_path_z.ptr, arch_type, lib_final_path_z.ptr, true)) {
411 if (llvm_bindings.WriteImportLibrary(def_final_path_z.ptr, arch_tag, lib_final_path_z.ptr, true)) {
412412 // TODO surface a proper error here
413413 log.err("unable to turn {s}.def into {s}.lib", .{ lib_name, lib_name });
414414 return error.WritingImportLibFailed;
src/print_zir.zig+5-6
......@@ -283,7 +283,6 @@ const Writer = struct {
283283 .mul_add => try self.writeMulAdd(stream, inst),
284284 .field_parent_ptr => try self.writeFieldParentPtr(stream, inst),
285285 .builtin_call => try self.writeBuiltinCall(stream, inst),
286 .builtin_async_call => try self.writeBuiltinAsyncCall(stream, inst),
287286
288287 .struct_init_anon,
289288 .struct_init_anon_ref,
......@@ -397,7 +396,7 @@ const Writer = struct {
397396 .field_val_named,
398397 => try self.writePlNodeFieldNamed(stream, inst),
399398
400 .as_node => try self.writeAs(stream, inst),
399 .as_node, .as_shift_operand => try self.writeAs(stream, inst),
401400
402401 .repeat,
403402 .repeat_inline,
......@@ -531,6 +530,7 @@ const Writer = struct {
531530 try stream.writeAll(") ");
532531 try self.writeSrc(stream, src);
533532 },
533 .builtin_async_call => try self.writeBuiltinAsyncCall(stream, extended),
534534 }
535535 }
536536
......@@ -814,9 +814,8 @@ const Writer = struct {
814814 try self.writeSrc(stream, inst_data.src());
815815 }
816816
817 fn writeBuiltinAsyncCall(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
818 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
819 const extra = self.code.extraData(Zir.Inst.AsyncCall, inst_data.payload_index).data;
817 fn writeBuiltinAsyncCall(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void {
818 const extra = self.code.extraData(Zir.Inst.AsyncCall, extended.operand).data;
820819 try self.writeInstRef(stream, extra.frame_buffer);
821820 try stream.writeAll(", ");
822821 try self.writeInstRef(stream, extra.result_ptr);
......@@ -825,7 +824,7 @@ const Writer = struct {
825824 try stream.writeAll(", ");
826825 try self.writeInstRef(stream, extra.args);
827826 try stream.writeAll(") ");
828 try self.writeSrc(stream, inst_data.src());
827 try self.writeSrc(stream, LazySrcLoc.nodeOffset(extra.node));
829828 }
830829
831830 fn writeParam(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
src/target.zig+7-107
......@@ -1,5 +1,4 @@
11const std = @import("std");
2const llvm = @import("codegen/llvm/bindings.zig");
32const Type = @import("type.zig").Type;
43
54pub const ArchOsAbi = struct {
......@@ -338,112 +337,6 @@ pub fn supportsReturnAddress(target: std.Target) bool {
338337 };
339338}
340339
341pub fn osToLLVM(os_tag: std.Target.Os.Tag) llvm.OSType {
342 return switch (os_tag) {
343 .freestanding, .other, .opencl, .glsl450, .vulkan, .plan9 => .UnknownOS,
344 .windows, .uefi => .Win32,
345 .ananas => .Ananas,
346 .cloudabi => .CloudABI,
347 .dragonfly => .DragonFly,
348 .freebsd => .FreeBSD,
349 .fuchsia => .Fuchsia,
350 .ios => .IOS,
351 .kfreebsd => .KFreeBSD,
352 .linux => .Linux,
353 .lv2 => .Lv2,
354 .macos => .MacOSX,
355 .netbsd => .NetBSD,
356 .openbsd => .OpenBSD,
357 .solaris => .Solaris,
358 .zos => .ZOS,
359 .haiku => .Haiku,
360 .minix => .Minix,
361 .rtems => .RTEMS,
362 .nacl => .NaCl,
363 .aix => .AIX,
364 .cuda => .CUDA,
365 .nvcl => .NVCL,
366 .amdhsa => .AMDHSA,
367 .ps4 => .PS4,
368 .ps5 => .PS5,
369 .elfiamcu => .ELFIAMCU,
370 .tvos => .TvOS,
371 .watchos => .WatchOS,
372 .mesa3d => .Mesa3D,
373 .contiki => .Contiki,
374 .amdpal => .AMDPAL,
375 .hermit => .HermitCore,
376 .hurd => .Hurd,
377 .wasi => .WASI,
378 .emscripten => .Emscripten,
379 .driverkit => .DriverKit,
380 .shadermodel => .ShaderModel,
381 };
382}
383
384pub fn archToLLVM(arch_tag: std.Target.Cpu.Arch) llvm.ArchType {
385 return switch (arch_tag) {
386 .arm => .arm,
387 .armeb => .armeb,
388 .aarch64 => .aarch64,
389 .aarch64_be => .aarch64_be,
390 .aarch64_32 => .aarch64_32,
391 .arc => .arc,
392 .avr => .avr,
393 .bpfel => .bpfel,
394 .bpfeb => .bpfeb,
395 .csky => .csky,
396 .dxil => .dxil,
397 .hexagon => .hexagon,
398 .loongarch32 => .loongarch32,
399 .loongarch64 => .loongarch64,
400 .m68k => .m68k,
401 .mips => .mips,
402 .mipsel => .mipsel,
403 .mips64 => .mips64,
404 .mips64el => .mips64el,
405 .msp430 => .msp430,
406 .powerpc => .ppc,
407 .powerpcle => .ppcle,
408 .powerpc64 => .ppc64,
409 .powerpc64le => .ppc64le,
410 .r600 => .r600,
411 .amdgcn => .amdgcn,
412 .riscv32 => .riscv32,
413 .riscv64 => .riscv64,
414 .sparc => .sparc,
415 .sparc64 => .sparcv9, // In LLVM, sparc64 == sparcv9.
416 .sparcel => .sparcel,
417 .s390x => .systemz,
418 .tce => .tce,
419 .tcele => .tcele,
420 .thumb => .thumb,
421 .thumbeb => .thumbeb,
422 .i386 => .x86,
423 .x86_64 => .x86_64,
424 .xcore => .xcore,
425 .nvptx => .nvptx,
426 .nvptx64 => .nvptx64,
427 .le32 => .le32,
428 .le64 => .le64,
429 .amdil => .amdil,
430 .amdil64 => .amdil64,
431 .hsail => .hsail,
432 .hsail64 => .hsail64,
433 .spir => .spir,
434 .spir64 => .spir64,
435 .kalimba => .kalimba,
436 .shave => .shave,
437 .lanai => .lanai,
438 .wasm32 => .wasm32,
439 .wasm64 => .wasm64,
440 .renderscript32 => .renderscript32,
441 .renderscript64 => .renderscript64,
442 .ve => .ve,
443 .spu_2, .spirv32, .spirv64 => .UnknownArch,
444 };
445}
446
447340fn eqlIgnoreCase(ignore_case: bool, a: []const u8, b: []const u8) bool {
448341 if (ignore_case) {
449342 return std.ascii.eqlIgnoreCase(a, b);
......@@ -800,3 +693,10 @@ pub fn supportsFunctionAlignment(target: std.Target) bool {
800693 else => true,
801694 };
802695}
696
697pub fn supportsTailCall(target: std.Target, backend: std.builtin.CompilerBackend) bool {
698 switch (backend) {
699 .stage1, .stage2_llvm => return @import("codegen/llvm.zig").supportsTailCall(target),
700 else => return false,
701 }
702}
src/type.zig+4
......@@ -6598,6 +6598,8 @@ pub const CType = enum {
65986598 .powerpcle,
65996599 .powerpc64,
66006600 .powerpc64le,
6601 .wasm32,
6602 .wasm64,
66016603 => return 128,
66026604
66036605 else => return 64,
......@@ -6646,6 +6648,8 @@ pub const CType = enum {
66466648 .powerpcle,
66476649 .powerpc64,
66486650 .powerpc64le,
6651 .wasm32,
6652 .wasm64,
66496653 => return 128,
66506654
66516655 else => return 64,
test/behavior/call.zig+68
......@@ -261,3 +261,71 @@ test "arguments to comptime parameters generated in comptime blocks" {
261261 };
262262 S.foo(S.fortyTwo());
263263}
264
265test "forced tail call" {
266 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
267 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
268 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
269 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
270 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
271 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
272
273 if (builtin.zig_backend == .stage2_llvm) {
274 // Only attempt this test on targets we know have tail call support in LLVM.
275 if (builtin.cpu.arch != .x86_64 and builtin.cpu.arch != .aarch64) {
276 return error.SkipZigTest;
277 }
278 }
279
280 const S = struct {
281 fn fibonacciTailInternal(n: u16, a: u16, b: u16) u16 {
282 if (n == 0) return a;
283 if (n == 1) return b;
284 return @call(
285 .{ .modifier = .always_tail },
286 fibonacciTailInternal,
287 .{ n - 1, b, a + b },
288 );
289 }
290
291 fn fibonacciTail(n: u16) u16 {
292 return fibonacciTailInternal(n, 0, 1);
293 }
294 };
295 try expect(S.fibonacciTail(10) == 55);
296}
297
298test "inline call preserves tail call" {
299 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
300 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
301 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
302 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
303 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
304 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
305
306 if (builtin.zig_backend == .stage2_llvm) {
307 // Only attempt this test on targets we know have tail call support in LLVM.
308 if (builtin.cpu.arch != .x86_64 and builtin.cpu.arch != .aarch64) {
309 return error.SkipZigTest;
310 }
311 }
312
313 const max = std.math.maxInt(u16);
314 const S = struct {
315 var a: u16 = 0;
316 fn foo() void {
317 return bar();
318 }
319
320 inline fn bar() void {
321 if (a == max) return;
322 // Stack overflow if not tail called
323 var buf: [max]u16 = undefined;
324 buf[a] = a;
325 a += 1;
326 return @call(.{ .modifier = .always_tail }, foo, .{});
327 }
328 };
329 S.foo();
330 try expect(S.a == std.math.maxInt(u16));
331}
test/behavior/cast.zig+1
......@@ -1430,6 +1430,7 @@ test "coerce between pointers of compatible differently-named floats" {
14301430 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
14311431 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
14321432 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1433 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
14331434
14341435 if (builtin.os.tag == .windows) {
14351436 // https://github.com/ziglang/zig/issues/12396
test/behavior/union.zig+28
......@@ -1324,3 +1324,31 @@ test "union and enum field order doesn't match" {
13241324 x = .b;
13251325 try expect(x == .b);
13261326}
1327
1328test "@unionInit uses tag value instead of field index" {
1329 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1330 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1331 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1332 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1333 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1334
1335 const E = enum(u8) {
1336 b = 255,
1337 a = 3,
1338 };
1339 const U = union(E) {
1340 a: usize,
1341 b: isize,
1342 };
1343 var i: isize = -1;
1344 var u = @unionInit(U, "b", i);
1345 {
1346 var a = u.b;
1347 try expect(a == i);
1348 }
1349 {
1350 var a = &u.b;
1351 try expect(a.* == i);
1352 }
1353 try expect(@enumToInt(u) == 255);
1354}
test/c_abi/cfuncs.c+6
......@@ -33,6 +33,7 @@ void zig_five_integers(int32_t, int32_t, int32_t, int32_t, int32_t);
3333
3434void zig_f32(float);
3535void zig_f64(double);
36void zig_longdouble(long double);
3637void zig_five_floats(float, float, float, float, float);
3738
3839bool zig_ret_bool();
......@@ -157,6 +158,7 @@ void run_c_tests(void) {
157158
158159 zig_f32(12.34f);
159160 zig_f64(56.78);
161 zig_longdouble(12.34l);
160162 zig_five_floats(1.0f, 2.0f, 3.0f, 4.0f, 5.0f);
161163
162164 zig_ptr((void*)0xdeadbeefL);
......@@ -271,6 +273,10 @@ void c_f64(double x) {
271273 assert_or_panic(x == 56.78);
272274}
273275
276void c_long_double(long double x) {
277 assert_or_panic(x == 12.34l);
278}
279
274280void c_ptr(void *x) {
275281 assert_or_panic(x == (void*)0xdeadbeefL);
276282}
test/c_abi/main.zig+11
......@@ -1,4 +1,5 @@
11const std = @import("std");
2const builtin = @import("builtin");
23const print = std.debug.print;
34const expect = std.testing.expect;
45
......@@ -89,6 +90,7 @@ export fn zig_struct_u128(a: U128) void {
8990
9091extern fn c_f32(f32) void;
9192extern fn c_f64(f64) void;
93extern fn c_long_double(c_longdouble) void;
9294
9395// On windows x64, the first 4 are passed via registers, others on the stack.
9496extern fn c_five_floats(f32, f32, f32, f32, f32) void;
......@@ -107,12 +109,21 @@ test "C ABI floats" {
107109 c_five_floats(1.0, 2.0, 3.0, 4.0, 5.0);
108110}
109111
112test "C ABI long double" {
113 if (!builtin.cpu.arch.isWasm()) return error.SkipZigTest;
114 c_long_double(12.34);
115}
116
110117export fn zig_f32(x: f32) void {
111118 expect(x == 12.34) catch @panic("test failure: zig_f32");
112119}
113120export fn zig_f64(x: f64) void {
114121 expect(x == 56.78) catch @panic("test failure: zig_f64");
115122}
123export fn zig_longdouble(x: c_longdouble) void {
124 if (!builtin.cpu.arch.isWasm()) return; // waiting for #1481
125 expect(x == 12.34) catch @panic("test failure: zig_longdouble");
126}
116127
117128extern fn c_ptr(*anyopaque) void;
118129
test/cases/aarch64-macos/hello_world_with_updates.0.zig+1-1
......@@ -2,5 +2,5 @@
22// output_mode=Exe
33// target=aarch64-macos
44//
5// :107:9: error: struct 'tmp.tmp' has no member named 'main'
5// :105:9: error: struct 'tmp.tmp' has no member named 'main'
66// :7:1: note: struct declared here
test/cases/compile_errors/invalid_tail_call.zig created+12
......@@ -0,0 +1,12 @@
1fn myFn(_: usize) void {
2 return;
3}
4pub export fn entry() void {
5 @call(.{ .modifier = .always_tail }, myFn, .{0});
6}
7
8// error
9// backend=llvm
10// target=native
11//
12// :5:5: error: unable to perform tail call: type of function being called 'fn(usize) void' does not match type of calling function 'fn() callconv(.C) void'
test/cases/compile_errors/non-comptime-parameter-used-as-array-size.zig created+16
......@@ -0,0 +1,16 @@
1export fn entry() void {
2 const llamas1 = makeLlamas(5);
3 const llamas2 = makeLlamas(5);
4 _ = llamas1;
5 _ = llamas2;
6}
7
8fn makeLlamas(count: usize) [count]u8 {
9 _ = count;
10}
11
12// error
13// target=native
14//
15// :8:30: error: unable to resolve comptime value
16// :8:30: note: array length must be comptime known
test/cases/compile_errors/runtime_to_comptime_num.zig created+31
......@@ -0,0 +1,31 @@
1pub export fn entry() void {
2 var a: u32 = 0;
3 _ = @as(comptime_int, a);
4}
5pub export fn entry2() void{
6 var a: u32 = 0;
7 _ = @as(comptime_float, a);
8}
9pub export fn entry3() void{
10 comptime var aa: comptime_float = 0.0;
11 var a: f32 = 4;
12 aa = a;
13}
14pub export fn entry4() void{
15 comptime var aa: comptime_int = 0.0;
16 var a: f32 = 4;
17 aa = a;
18}
19
20// error
21// backend=stage2
22// target=native
23//
24// :3:27: error: unable to resolve comptime value
25// :3:27: note: value being casted to 'comptime_int' must be comptime known
26// :7:29: error: unable to resolve comptime value
27// :7:29: note: value being casted to 'comptime_float' must be comptime known
28// :12:10: error: unable to resolve comptime value
29// :12:10: note: value being casted to 'comptime_float' must be comptime known
30// :17:10: error: unable to resolve comptime value
31// :17:10: note: value being casted to 'comptime_int' must be comptime known
test/cases/compile_errors/shifting_without_int_type_or_comptime_known.zig created+23
......@@ -0,0 +1,23 @@
1export fn entry(x: u8) u8 {
2 return 0x11 << x;
3}
4export fn entry1(x: u8) u8 {
5 return 0x11 >> x;
6}
7export fn entry2() void {
8 var x: u5 = 1;
9 _ = @shlExact(12345, x);
10}
11export fn entry3() void {
12 var x: u5 = 1;
13 _ = @shrExact(12345, x);
14}
15
16// error
17// backend=stage2
18// target=native
19//
20// :2:17: error: LHS of shift must be a fixed-width integer type, or RHS must be a comptime known
21// :5:17: error: LHS of shift must be a fixed-width integer type, or RHS must be a comptime known
22// :9:9: error: LHS of shift must be a fixed-width integer type, or RHS must be a comptime known
23// :13:9: error: LHS of shift must be a fixed-width integer type, or RHS must be a comptime known
test/cases/compile_errors/stage1/obj/shifting_without_int_type_or_comptime_known.zig deleted-9
......@@ -1,9 +0,0 @@
1export fn entry(x: u8) u8 {
2 return 0x11 << x;
3}
4
5// error
6// backend=stage1
7// target=native
8//
9// tmp.zig:2:17: error: LHS of shift must be a fixed-width integer type, or RHS must be compile-time known
test/cases/taill_call_noreturn.zig created+18
......@@ -0,0 +1,18 @@
1const std = @import("std");
2const builtin = std.builtin;
3pub fn foo(message: []const u8, stack_trace: ?*builtin.StackTrace) noreturn {
4 @call(.{ .modifier = .always_tail }, bar, .{ message, stack_trace });
5}
6pub fn bar(message: []const u8, stack_trace: ?*builtin.StackTrace) noreturn {
7 _ = message;
8 _ = stack_trace;
9 std.process.exit(0);
10}
11
12pub fn main() void {
13 foo("foo", null);
14}
15
16// run
17// backend=llvm
18// target=x86_64-linux,x86_64-macos,aarch64-linux,aarch64-macos
test/cases/x86_64-linux/hello_world_with_updates.0.zig+1-1
......@@ -2,5 +2,5 @@
22// output_mode=Exe
33// target=x86_64-linux
44//
5// :107:9: error: struct 'tmp.tmp' has no member named 'main'
5// :105:9: error: struct 'tmp.tmp' has no member named 'main'
66// :7:1: note: struct declared here
test/cases/x86_64-macos/hello_world_with_updates.0.zig+1-1
......@@ -2,5 +2,5 @@
22// output_mode=Exe
33// target=x86_64-macos
44//
5// :107:9: error: struct 'tmp.tmp' has no member named 'main'
5// :105:9: error: struct 'tmp.tmp' has no member named 'main'
66// :7:1: note: struct declared here