authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-23 17:06:19+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-23 17:06:19+02:00
log5533f77054acad376f2fce0d529569a7449e9949
treeeba6945383fefa59a8858f7eec8beee3cfca7731
parent1beda818e1c10bde98b35759b3c131a864be58d9
parente5b476209a1215a03164890d331e2013f20882a6

Merge remote-tracking branch 'origin/master' into zld-incremental-2


32 files changed, 963 insertions(+), 270 deletions(-)

CMakeLists.txt+1
......@@ -792,6 +792,7 @@ set(BUILD_ZIG1_ARGS
792792 --name zig1
793793 --zig-lib-dir "${CMAKE_SOURCE_DIR}/lib"
794794 "-femit-bin=${ZIG1_OBJECT}"
795 -fcompiler-rt
795796 "${ZIG1_RELEASE_ARG}"
796797 "${ZIG1_SINGLE_THREADED_ARG}"
797798 -lc
doc/docgen.zig+4-4
......@@ -1,5 +1,5 @@
11const std = @import("std");
2const builtin = std.builtin;
2const builtin = @import("builtin");
33const io = std.io;
44const fs = std.fs;
55const process = std.process;
......@@ -13,7 +13,7 @@ const Allocator = std.mem.Allocator;
1313const max_doc_file_size = 10 * 1024 * 1024;
1414
1515const exe_ext = @as(std.zig.CrossTarget, .{}).exeFileExt();
16const obj_ext = @as(std.zig.CrossTarget, .{}).oFileExt();
16const obj_ext = builtin.object_format.fileExt(builtin.cpu.arch);
1717const tmp_dir_name = "docgen_tmp";
1818const test_out_path = tmp_dir_name ++ fs.path.sep_str ++ "test" ++ exe_ext;
1919
......@@ -281,7 +281,7 @@ const Code = struct {
281281 name: []const u8,
282282 source_token: Token,
283283 is_inline: bool,
284 mode: builtin.Mode,
284 mode: std.builtin.Mode,
285285 link_objects: []const []const u8,
286286 target_str: ?[]const u8,
287287 link_libc: bool,
......@@ -531,7 +531,7 @@ fn genToc(allocator: *Allocator, tokenizer: *Tokenizer) !Toc {
531531 return parseError(tokenizer, code_kind_tok, "unrecognized code kind: {s}", .{code_kind_str});
532532 }
533533
534 var mode: builtin.Mode = .Debug;
534 var mode: std.builtin.Mode = .Debug;
535535 var link_objects = std.ArrayList([]const u8).init(allocator);
536536 defer link_objects.deinit();
537537 var target_str: ?[]const u8 = null;
doc/langref.html.in+8-9
......@@ -5337,16 +5337,15 @@ test "implicit cast to comptime_int" {
53375337}
53385338 {#code_end#}
53395339 {#header_close#}
5340 {#header_open|Type Coercion: Arrays and Pointers#}
5341 {#code_begin|test|coerce_arrays_and_ptrs#}
5340 {#header_open|Type Coercion: Slices, Arrays and Pointers#}
5341 {#code_begin|test|coerce__slices_arrays_and_ptrs#}
53425342const std = @import("std");
53435343const expect = std.testing.expect;
53445344
5345// This cast exists primarily so that string literals can be
5346// passed to functions that accept const slices. However
5347// it is probably going to be removed from the language when
5348// https://github.com/ziglang/zig/issues/265 is implemented.
5349test "[N]T to []const T" {
5345// You can assign constant pointers to arrays to a slice with
5346// const modifier on the element type. Useful in particular for
5347// String literals.
5348test "*const [N]T to []const T" {
53505349 var x1: []const u8 = "hello";
53515350 var x2: []const u8 = &[5]u8{ 'h', 'e', 'l', 'l', 111 };
53525351 try expect(std.mem.eql(u8, x1, x2));
......@@ -5356,7 +5355,7 @@ test "[N]T to []const T" {
53565355}
53575356
53585357// Likewise, it works when the destination type is an error union.
5359test "[N]T to E![]const T" {
5358test "*const [N]T to E![]const T" {
53605359 var x1: anyerror![]const u8 = "hello";
53615360 var x2: anyerror![]const u8 = &[5]u8{ 'h', 'e', 'l', 'l', 111 };
53625361 try expect(std.mem.eql(u8, try x1, try x2));
......@@ -5366,7 +5365,7 @@ test "[N]T to E![]const T" {
53665365}
53675366
53685367// Likewise, it works when the destination type is an optional.
5369test "[N]T to ?[]const T" {
5368test "*const [N]T to ?[]const T" {
53705369 var x1: ?[]const u8 = "hello";
53715370 var x2: ?[]const u8 = &[5]u8{ 'h', 'e', 'l', 'l', 111 };
53725371 try expect(std.mem.eql(u8, x1.?, x2.?));
lib/std/crypto/25519/field.zig+2-2
......@@ -93,7 +93,7 @@ pub const Fe = struct {
9393 return s;
9494 }
9595
96 /// Map a 64-bit big endian string into a field element
96 /// Map a 64 bytes big endian string into a field element
9797 pub fn fromBytes64(s: [64]u8) Fe {
9898 var fl: [32]u8 = undefined;
9999 var gl: [32]u8 = undefined;
......@@ -106,7 +106,7 @@ pub const Fe = struct {
106106 gl[31] &= 0x7f;
107107 var fe_f = fromBytes(fl);
108108 const fe_g = fromBytes(gl);
109 fe_f.limbs[0] += (s[32] >> 7) * 19;
109 fe_f.limbs[0] += (s[32] >> 7) * 19 + @as(u10, s[0] >> 7) * 722;
110110 i = 0;
111111 while (i < 5) : (i += 1) {
112112 fe_f.limbs[i] += 38 * fe_g.limbs[i];
lib/std/os/bits/linux.zig+5-5
......@@ -339,19 +339,19 @@ pub const O_RDWR = 0o2;
339339pub const kernel_rwf = u32;
340340
341341/// high priority request, poll if possible
342pub const RWF_HIPRI = kernel_rwf(0x00000001);
342pub const RWF_HIPRI: kernel_rwf = 0x00000001;
343343
344344/// per-IO O_DSYNC
345pub const RWF_DSYNC = kernel_rwf(0x00000002);
345pub const RWF_DSYNC: kernel_rwf = 0x00000002;
346346
347347/// per-IO O_SYNC
348pub const RWF_SYNC = kernel_rwf(0x00000004);
348pub const RWF_SYNC: kernel_rwf = 0x00000004;
349349
350350/// per-IO, return -EAGAIN if operation would block
351pub const RWF_NOWAIT = kernel_rwf(0x00000008);
351pub const RWF_NOWAIT: kernel_rwf = 0x00000008;
352352
353353/// per-IO O_APPEND
354pub const RWF_APPEND = kernel_rwf(0x00000010);
354pub const RWF_APPEND: kernel_rwf = 0x00000010;
355355
356356pub const SEEK_SET = 0;
357357pub const SEEK_CUR = 1;
lib/std/os/windows/user32.zig+1-1
......@@ -1336,7 +1336,7 @@ pub extern "user32" fn AdjustWindowRectEx(lpRect: *RECT, dwStyle: DWORD, bMenu:
13361336pub fn adjustWindowRectEx(lpRect: *RECT, dwStyle: u32, bMenu: bool, dwExStyle: u32) !void {
13371337 assert(dwStyle & WS_OVERLAPPED == 0);
13381338
1339 if (AdjustWindowRectEx(lpRect, dwStyle, bMenu, dwExStyle) == 0) {
1339 if (AdjustWindowRectEx(lpRect, dwStyle, @boolToInt(bMenu), dwExStyle) == 0) {
13401340 switch (GetLastError()) {
13411341 .INVALID_PARAMETER => unreachable,
13421342 else => |err| return windows.unexpectedError(err),
lib/std/target.zig+63-37
......@@ -549,16 +549,36 @@ pub const Target = struct {
549549 };
550550
551551 pub const ObjectFormat = enum {
552 /// Common Object File Format (Windows)
552553 coff,
553 pe,
554 /// Executable and Linking Format
554555 elf,
556 /// macOS relocatables
555557 macho,
558 /// WebAssembly
556559 wasm,
560 /// C source code
557561 c,
562 /// Standard, Portable Intermediate Representation V
558563 spirv,
564 /// Intel IHEX
559565 hex,
566 /// Machine code with no metadata.
560567 raw,
568 /// Plan 9 from Bell Labs
561569 plan9,
570
571 pub fn fileExt(of: ObjectFormat, cpu_arch: Cpu.Arch) [:0]const u8 {
572 return switch (of) {
573 .coff => ".obj",
574 .elf, .macho, .wasm => ".o",
575 .c => ".c",
576 .spirv => ".spv",
577 .hex => ".ihex",
578 .raw => ".bin",
579 .plan9 => plan9Ext(cpu_arch),
580 };
581 }
562582 };
563583
564584 pub const SubSystem = enum {
......@@ -1290,30 +1310,16 @@ pub const Target = struct {
12901310 return linuxTripleSimple(allocator, self.cpu.arch, self.os.tag, self.abi);
12911311 }
12921312
1293 pub fn oFileExt_os_abi(os_tag: Os.Tag, abi: Abi) [:0]const u8 {
1294 if (abi == .msvc) {
1295 return ".obj";
1296 }
1297 switch (os_tag) {
1298 .windows, .uefi => return ".obj",
1299 else => return ".o",
1300 }
1301 }
1302
1303 pub fn oFileExt(self: Target) [:0]const u8 {
1304 return oFileExt_os_abi(self.os.tag, self.abi);
1305 }
1306
13071313 pub fn exeFileExtSimple(cpu_arch: Cpu.Arch, os_tag: Os.Tag) [:0]const u8 {
1308 switch (os_tag) {
1309 .windows => return ".exe",
1310 .uefi => return ".efi",
1311 else => if (cpu_arch.isWasm()) {
1312 return ".wasm";
1313 } else {
1314 return "";
1314 return switch (os_tag) {
1315 .windows => ".exe",
1316 .uefi => ".efi",
1317 .plan9 => plan9Ext(cpu_arch),
1318 else => switch (cpu_arch) {
1319 .wasm32, .wasm64 => ".wasm",
1320 else => "",
13151321 },
1316 }
1322 };
13171323 }
13181324
13191325 pub fn exeFileExt(self: Target) [:0]const u8 {
......@@ -1353,20 +1359,16 @@ pub const Target = struct {
13531359 }
13541360
13551361 pub fn getObjectFormatSimple(os_tag: Os.Tag, cpu_arch: Cpu.Arch) ObjectFormat {
1356 if (os_tag == .windows or os_tag == .uefi) {
1357 return .coff;
1358 } else if (os_tag.isDarwin()) {
1359 return .macho;
1360 }
1361 if (cpu_arch.isWasm()) {
1362 return .wasm;
1363 }
1364 if (cpu_arch.isSPIRV()) {
1365 return .spirv;
1366 }
1367 if (os_tag == .plan9)
1368 return .plan9;
1369 return .elf;
1362 return switch (os_tag) {
1363 .windows, .uefi => .coff,
1364 .ios, .macos, .watchos, .tvos => .macho,
1365 .plan9 => .plan9,
1366 else => return switch (cpu_arch) {
1367 .wasm32, .wasm64 => .wasm,
1368 .spirv32, .spirv64 => .spirv,
1369 else => .elf,
1370 },
1371 };
13701372 }
13711373
13721374 pub fn getObjectFormat(self: Target) ObjectFormat {
......@@ -1677,6 +1679,30 @@ pub const Target = struct {
16771679
16781680 return false;
16791681 }
1682
1683 /// 0c spim little-endian MIPS 3000 family
1684 /// 1c 68000 Motorola MC68000
1685 /// 2c 68020 Motorola MC68020
1686 /// 5c arm little-endian ARM
1687 /// 6c amd64 AMD64 and compatibles (e.g., Intel EM64T)
1688 /// 7c arm64 ARM64 (ARMv8)
1689 /// 8c 386 Intel i386, i486, Pentium, etc.
1690 /// kc sparc Sun SPARC
1691 /// qc power Power PC
1692 /// vc mips big-endian MIPS 3000 family
1693 pub fn plan9Ext(cpu_arch: Cpu.Arch) [:0]const u8 {
1694 return switch (cpu_arch) {
1695 .arm => ".5",
1696 .x86_64 => ".6",
1697 .aarch64 => ".7",
1698 .i386 => ".8",
1699 .sparc => ".k",
1700 .powerpc, .powerpcle => ".q",
1701 .mips, .mipsel => ".v",
1702 // ISAs without designated characters get 'X' for lack of a better option.
1703 else => ".X",
1704 };
1705 }
16801706};
16811707
16821708test {
lib/std/zig.zig+10-30
......@@ -108,8 +108,9 @@ pub const BinNameOptions = struct {
108108pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) error{OutOfMemory}![]u8 {
109109 const root_name = options.root_name;
110110 const target = options.target;
111 switch (options.object_format orelse target.getObjectFormat()) {
112 .coff, .pe => switch (options.output_mode) {
111 const ofmt = options.object_format orelse target.getObjectFormat();
112 switch (ofmt) {
113 .coff => switch (options.output_mode) {
113114 .Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.exeFileExt() }),
114115 .Lib => {
115116 const suffix = switch (options.link_mode orelse .Static) {
......@@ -118,7 +119,7 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro
118119 };
119120 return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, suffix });
120121 },
121 .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.oFileExt() }),
122 .Obj => return std.fmt.allocPrint(allocator, "{s}.obj", .{root_name}),
122123 },
123124 .elf => switch (options.output_mode) {
124125 .Exe => return allocator.dupe(u8, root_name),
......@@ -140,7 +141,7 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro
140141 },
141142 }
142143 },
143 .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.oFileExt() }),
144 .Obj => return std.fmt.allocPrint(allocator, "{s}.o", .{root_name}),
144145 },
145146 .macho => switch (options.output_mode) {
146147 .Exe => return allocator.dupe(u8, root_name),
......@@ -163,7 +164,7 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro
163164 }
164165 return std.fmt.allocPrint(allocator, "{s}{s}{s}", .{ target.libPrefix(), root_name, suffix });
165166 },
166 .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.oFileExt() }),
167 .Obj => return std.fmt.allocPrint(allocator, "{s}.o", .{root_name}),
167168 },
168169 .wasm => switch (options.output_mode) {
169170 .Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.exeFileExt() }),
......@@ -175,36 +176,15 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro
175176 .Dynamic => return std.fmt.allocPrint(allocator, "{s}.wasm", .{root_name}),
176177 }
177178 },
178 .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.oFileExt() }),
179 .Obj => return std.fmt.allocPrint(allocator, "{s}.o", .{root_name}),
179180 },
180181 .c => return std.fmt.allocPrint(allocator, "{s}.c", .{root_name}),
181182 .spirv => return std.fmt.allocPrint(allocator, "{s}.spv", .{root_name}),
182183 .hex => return std.fmt.allocPrint(allocator, "{s}.ihex", .{root_name}),
183184 .raw => return std.fmt.allocPrint(allocator, "{s}.bin", .{root_name}),
184 .plan9 => {
185 // copied from 2c(1)
186 // 0c spim little-endian MIPS 3000 family
187 // 1c 68000 Motorola MC68000
188 // 2c 68020 Motorola MC68020
189 // 5c arm little-endian ARM
190 // 6c amd64 AMD64 and compatibles (e.g., Intel EM64T)
191 // 7c arm64 ARM64 (ARMv8)
192 // 8c 386 Intel i386, i486, Pentium, etc.
193 // kc sparc Sun SPARC
194 // qc power Power PC
195 // vc mips big-endian MIPS 3000 family
196 const char: u8 = switch (target.cpu.arch) {
197 .arm => '5',
198 .x86_64 => '6',
199 .aarch64 => '7',
200 .i386 => '8',
201 .sparc => 'k',
202 .powerpc, .powerpcle => 'q',
203 .mips, .mipsel => 'v',
204 else => 'X', // this arch does not have a char or maybe was not ported to plan9 so we just use X
205 };
206 return std.fmt.allocPrint(allocator, "{s}.{c}", .{ root_name, char });
207 },
185 .plan9 => return std.fmt.allocPrint(allocator, "{s}{s}", .{
186 root_name, ofmt.fileExt(target.cpu.arch),
187 }),
208188 }
209189}
210190
lib/std/zig/c_translation.zig+132
......@@ -350,3 +350,135 @@ test "Flexible Array Type" {
350350 try testing.expectEqual(FlexibleArrayType(*volatile Container, c_int), [*c]volatile c_int);
351351 try testing.expectEqual(FlexibleArrayType(*const volatile Container, c_int), [*c]const volatile c_int);
352352}
353
354pub const Macros = struct {
355 pub fn U_SUFFIX(comptime n: comptime_int) @TypeOf(promoteIntLiteral(c_uint, n, .decimal)) {
356 return promoteIntLiteral(c_uint, n, .decimal);
357 }
358
359 fn L_SUFFIX_ReturnType(comptime number: anytype) type {
360 switch (@TypeOf(number)) {
361 comptime_int => return @TypeOf(promoteIntLiteral(c_long, number, .decimal)),
362 comptime_float => return c_longdouble,
363 else => @compileError("Invalid value for L suffix"),
364 }
365 }
366 pub fn L_SUFFIX(comptime number: anytype) L_SUFFIX_ReturnType(number) {
367 switch (@TypeOf(number)) {
368 comptime_int => return promoteIntLiteral(c_long, number, .decimal),
369 comptime_float => @compileError("TODO: c_longdouble initialization from comptime_float not supported"),
370 else => @compileError("Invalid value for L suffix"),
371 }
372 }
373
374 pub fn UL_SUFFIX(comptime n: comptime_int) @TypeOf(promoteIntLiteral(c_ulong, n, .decimal)) {
375 return promoteIntLiteral(c_ulong, n, .decimal);
376 }
377
378 pub fn LL_SUFFIX(comptime n: comptime_int) @TypeOf(promoteIntLiteral(c_longlong, n, .decimal)) {
379 return promoteIntLiteral(c_longlong, n, .decimal);
380 }
381
382 pub fn ULL_SUFFIX(comptime n: comptime_int) @TypeOf(promoteIntLiteral(c_ulonglong, n, .decimal)) {
383 return promoteIntLiteral(c_ulonglong, n, .decimal);
384 }
385
386 pub fn F_SUFFIX(comptime f: comptime_float) f32 {
387 return @as(f32, f);
388 }
389
390 pub fn WL_CONTAINER_OF(ptr: anytype, sample: anytype, comptime member: []const u8) @TypeOf(sample) {
391 return @fieldParentPtr(@TypeOf(sample.*), member, ptr);
392 }
393
394 /// A 2-argument function-like macro defined as #define FOO(A, B) (A)(B)
395 /// could be either: cast B to A, or call A with the value B.
396 pub fn CAST_OR_CALL(a: anytype, b: anytype) switch (@typeInfo(@TypeOf(a))) {
397 .Type => a,
398 .Fn => |fn_info| fn_info.return_type orelse void,
399 else => |info| @compileError("Unexpected argument type: " ++ @tagName(info)),
400 } {
401 switch (@typeInfo(@TypeOf(a))) {
402 .Type => return cast(a, b),
403 .Fn => return a(b),
404 else => unreachable, // return type will be a compile error otherwise
405 }
406 }
407};
408
409test "Macro suffix functions" {
410 try testing.expect(@TypeOf(Macros.F_SUFFIX(1)) == f32);
411
412 try testing.expect(@TypeOf(Macros.U_SUFFIX(1)) == c_uint);
413 if (math.maxInt(c_ulong) > math.maxInt(c_uint)) {
414 try testing.expect(@TypeOf(Macros.U_SUFFIX(math.maxInt(c_uint) + 1)) == c_ulong);
415 }
416 if (math.maxInt(c_ulonglong) > math.maxInt(c_ulong)) {
417 try testing.expect(@TypeOf(Macros.U_SUFFIX(math.maxInt(c_ulong) + 1)) == c_ulonglong);
418 }
419
420 try testing.expect(@TypeOf(Macros.L_SUFFIX(1)) == c_long);
421 if (math.maxInt(c_long) > math.maxInt(c_int)) {
422 try testing.expect(@TypeOf(Macros.L_SUFFIX(math.maxInt(c_int) + 1)) == c_long);
423 }
424 if (math.maxInt(c_longlong) > math.maxInt(c_long)) {
425 try testing.expect(@TypeOf(Macros.L_SUFFIX(math.maxInt(c_long) + 1)) == c_longlong);
426 }
427
428 try testing.expect(@TypeOf(Macros.UL_SUFFIX(1)) == c_ulong);
429 if (math.maxInt(c_ulonglong) > math.maxInt(c_ulong)) {
430 try testing.expect(@TypeOf(Macros.UL_SUFFIX(math.maxInt(c_ulong) + 1)) == c_ulonglong);
431 }
432
433 try testing.expect(@TypeOf(Macros.LL_SUFFIX(1)) == c_longlong);
434 try testing.expect(@TypeOf(Macros.ULL_SUFFIX(1)) == c_ulonglong);
435}
436
437test "WL_CONTAINER_OF" {
438 const S = struct {
439 a: u32 = 0,
440 b: u32 = 0,
441 };
442 var x = S{};
443 var y = S{};
444 var ptr = Macros.WL_CONTAINER_OF(&x.b, &y, "b");
445 try testing.expectEqual(&x, ptr);
446}
447
448test "CAST_OR_CALL casting" {
449 var arg = @as(c_int, 1000);
450 var casted = Macros.CAST_OR_CALL(u8, arg);
451 try testing.expectEqual(cast(u8, arg), casted);
452
453 const S = struct {
454 x: u32 = 0,
455 };
456 var s = S{};
457 var casted_ptr = Macros.CAST_OR_CALL(*u8, &s);
458 try testing.expectEqual(cast(*u8, &s), casted_ptr);
459}
460
461test "CAST_OR_CALL calling" {
462 const Helper = struct {
463 var last_val: bool = false;
464 fn returnsVoid(val: bool) void {
465 last_val = val;
466 }
467 fn returnsBool(f: f32) bool {
468 return f > 0;
469 }
470 fn identity(self: c_uint) c_uint {
471 return self;
472 }
473 };
474
475 Macros.CAST_OR_CALL(Helper.returnsVoid, true);
476 try testing.expectEqual(true, Helper.last_val);
477 Macros.CAST_OR_CALL(Helper.returnsVoid, false);
478 try testing.expectEqual(false, Helper.last_val);
479
480 try testing.expectEqual(Helper.returnsBool(1), Macros.CAST_OR_CALL(Helper.returnsBool, @as(f32, 1)));
481 try testing.expectEqual(Helper.returnsBool(-1), Macros.CAST_OR_CALL(Helper.returnsBool, @as(f32, -1)));
482
483 try testing.expectEqual(Helper.identity(@as(c_uint, 100)), Macros.CAST_OR_CALL(Helper.identity, @as(c_uint, 100)));
484}
lib/std/zig/cross_target.zig-4
......@@ -473,10 +473,6 @@ pub const CrossTarget = struct {
473473 return self.getOsTag() == .windows;
474474 }
475475
476 pub fn oFileExt(self: CrossTarget) [:0]const u8 {
477 return Target.oFileExt_os_abi(self.getOsTag(), self.getAbi());
478 }
479
480476 pub fn exeFileExt(self: CrossTarget) [:0]const u8 {
481477 return Target.exeFileExtSimple(self.getCpuArch(), self.getOsTag());
482478 }
src/AstGen.zig+10-1
......@@ -5009,6 +5009,7 @@ fn ifExpr(
50095009 const token_name_str = tree.tokenSlice(token_name_index);
50105010 if (mem.eql(u8, "_", token_name_str))
50115011 break :s &then_scope.base;
5012 try astgen.detectLocalShadowing(&then_scope.base, ident_name, token_name_index);
50125013 payload_val_scope = .{
50135014 .parent = &then_scope.base,
50145015 .gen_zir = &then_scope,
......@@ -5031,6 +5032,7 @@ fn ifExpr(
50315032 break :s &then_scope.base;
50325033 const payload_inst = try then_scope.addUnNode(tag, cond.inst, node);
50335034 const ident_name = try astgen.identAsString(ident_token);
5035 try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token);
50345036 payload_val_scope = .{
50355037 .parent = &then_scope.base,
50365038 .gen_zir = &then_scope,
......@@ -5072,6 +5074,7 @@ fn ifExpr(
50725074 const error_token_str = tree.tokenSlice(error_token);
50735075 if (mem.eql(u8, "_", error_token_str))
50745076 break :s &else_scope.base;
5077 try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token);
50755078 payload_val_scope = .{
50765079 .parent = &else_scope.base,
50775080 .gen_zir = &else_scope,
......@@ -5265,7 +5268,9 @@ fn whileExpr(
52655268 const ident_token = if (payload_is_ref) payload_token + 1 else payload_token;
52665269 if (mem.eql(u8, "_", tree.tokenSlice(ident_token)))
52675270 break :s &then_scope.base;
5268 const ident_name = try astgen.identAsString(payload_token + @boolToInt(payload_is_ref));
5271 const payload_name_loc = payload_token + @boolToInt(payload_is_ref);
5272 const ident_name = try astgen.identAsString(payload_name_loc);
5273 try astgen.detectLocalShadowing(&then_scope.base, ident_name, payload_name_loc);
52695274 payload_val_scope = .{
52705275 .parent = &then_scope.base,
52715276 .gen_zir = &then_scope,
......@@ -5288,6 +5293,7 @@ fn whileExpr(
52885293 const ident_name = try astgen.identAsString(ident_token);
52895294 if (mem.eql(u8, "_", tree.tokenSlice(ident_token)))
52905295 break :s &then_scope.base;
5296 try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token);
52915297 payload_val_scope = .{
52925298 .parent = &then_scope.base,
52935299 .gen_zir = &then_scope,
......@@ -5345,6 +5351,7 @@ fn whileExpr(
53455351 const ident_name = try astgen.identAsString(error_token);
53465352 if (mem.eql(u8, tree.tokenSlice(error_token), "_"))
53475353 break :s &else_scope.base;
5354 try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token);
53485355 payload_val_scope = .{
53495356 .parent = &else_scope.base,
53505357 .gen_zir = &else_scope,
......@@ -5484,6 +5491,7 @@ fn forExpr(
54845491 const name_str_index = try astgen.identAsString(ident);
54855492 const tag: Zir.Inst.Tag = if (is_ptr) .elem_ptr else .elem_val;
54865493 const payload_inst = try then_scope.addBin(tag, array_ptr, index);
5494 try astgen.detectLocalShadowing(&then_scope.base, name_str_index, ident);
54875495 payload_val_scope = .{
54885496 .parent = &then_scope.base,
54895497 .gen_zir = &then_scope,
......@@ -5507,6 +5515,7 @@ fn forExpr(
55075515 return astgen.failTok(index_token, "discard of index capture; omit it instead", .{});
55085516 }
55095517 const index_name = try astgen.identAsString(index_token);
5518 try astgen.detectLocalShadowing(payload_sub_scope, index_name, index_token);
55105519 index_scope = .{
55115520 .parent = payload_sub_scope,
55125521 .gen_zir = &then_scope,
src/Compilation.zig+59-18
......@@ -143,6 +143,7 @@ debug_compiler_runtime_libs: bool,
143143
144144emit_asm: ?EmitLoc,
145145emit_llvm_ir: ?EmitLoc,
146emit_llvm_bc: ?EmitLoc,
146147emit_analysis: ?EmitLoc,
147148emit_docs: ?EmitLoc,
148149
......@@ -586,6 +587,17 @@ pub const Directory = struct {
586587 return std.fs.path.join(allocator, paths);
587588 }
588589 }
590
591 pub fn joinZ(self: Directory, allocator: *Allocator, paths: []const []const u8) ![:0]u8 {
592 if (self.path) |p| {
593 // TODO clean way to do this with only 1 allocation
594 const part2 = try std.fs.path.join(allocator, paths);
595 defer allocator.free(part2);
596 return std.fs.path.joinZ(allocator, &[_][]const u8{ p, part2 });
597 } else {
598 return std.fs.path.joinZ(allocator, paths);
599 }
600 }
589601};
590602
591603pub const EmitLoc = struct {
......@@ -623,6 +635,8 @@ pub const InitOptions = struct {
623635 emit_asm: ?EmitLoc = null,
624636 /// `null` means to not emit LLVM IR.
625637 emit_llvm_ir: ?EmitLoc = null,
638 /// `null` means to not emit LLVM module bitcode.
639 emit_llvm_bc: ?EmitLoc = null,
626640 /// `null` means to not emit semantic analysis JSON.
627641 emit_analysis: ?EmitLoc = null,
628642 /// `null` means to not emit docs.
......@@ -812,6 +826,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
812826 const ofmt = options.object_format orelse options.target.getObjectFormat();
813827
814828 const use_stage1 = options.use_stage1 orelse blk: {
829 // Even though we may have no Zig code to compile (depending on `options.root_pkg`),
830 // we may need to use stage1 for building compiler-rt and other dependencies.
831
815832 if (build_options.omit_stage2)
816833 break :blk true;
817834 if (options.use_llvm) |use_llvm| {
......@@ -819,6 +836,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
819836 break :blk false;
820837 }
821838 }
839
822840 break :blk build_options.is_stage1;
823841 };
824842
......@@ -835,6 +853,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
835853 if (ofmt == .c)
836854 break :blk false;
837855
856 // If emitting to LLVM bitcode object format, must use LLVM backend.
857 if (options.emit_llvm_ir != null or options.emit_llvm_bc != null)
858 break :blk true;
859
838860 // The stage1 compiler depends on the stage1 C++ LLVM backend
839861 // to compile zig code.
840862 if (use_stage1)
......@@ -853,6 +875,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
853875 if (options.machine_code_model != .default) {
854876 return error.MachineCodeModelNotSupportedWithoutLlvm;
855877 }
878 if (options.emit_llvm_ir != null or options.emit_llvm_bc != null) {
879 return error.EmittingLlvmModuleRequiresUsingLlvmBackend;
880 }
856881 }
857882
858883 const tsan = options.want_tsan orelse false;
......@@ -996,7 +1021,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
9961021 break :blk lm;
9971022 } else default_link_mode;
9981023
999 const dll_export_fns = if (options.dll_export_fns) |explicit| explicit else is_dyn_lib;
1024 const dll_export_fns = if (options.dll_export_fns) |explicit| explicit else is_dyn_lib or options.rdynamic;
10001025
10011026 const libc_dirs = try detectLibCIncludeDirs(
10021027 arena,
......@@ -1386,6 +1411,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
13861411 .bin_file = bin_file,
13871412 .emit_asm = options.emit_asm,
13881413 .emit_llvm_ir = options.emit_llvm_ir,
1414 .emit_llvm_bc = options.emit_llvm_bc,
13891415 .emit_analysis = options.emit_analysis,
13901416 .emit_docs = options.emit_docs,
13911417 .work_queue = std.fifo.LinearFifo(Job, .Dynamic).init(gpa),
......@@ -1518,24 +1544,19 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
15181544 }
15191545
15201546 // The `use_stage1` condition is here only because stage2 cannot yet build compiler-rt.
1521 // Once it is capable this condition should be removed.
1547 // Once it is capable this condition should be removed. When removing this condition,
1548 // also test the use case of `build-obj -fcompiler-rt` with the self-hosted compiler
1549 // and make sure the compiler-rt symbols are emitted. Currently this is hooked up for
1550 // stage1 but not stage2.
15221551 if (comp.bin_file.options.use_stage1) {
15231552 if (comp.bin_file.options.include_compiler_rt) {
15241553 if (is_exe_or_dyn_lib) {
15251554 try comp.work_queue.writeItem(.{ .compiler_rt_lib = {} });
1526 } else {
1555 } else if (options.output_mode != .Obj) {
1556 // If build-obj with -fcompiler-rt is requested, that is handled specially
1557 // elsewhere. In this case we are making a static library, so we ask
1558 // for a compiler-rt object to put in it.
15271559 try comp.work_queue.writeItem(.{ .compiler_rt_obj = {} });
1528 if (comp.bin_file.options.object_format != .elf and
1529 comp.bin_file.options.output_mode == .Obj)
1530 {
1531 // For ELF we can rely on using -r to link multiple objects together into one,
1532 // but to truly support `build-obj -fcompiler-rt` will require virtually
1533 // injecting `_ = @import("compiler_rt.zig")` into the root source file of
1534 // the compilation.
1535 fatal("Embedding compiler-rt into {s} objects is not yet implemented.", .{
1536 @tagName(comp.bin_file.options.object_format),
1537 });
1538 }
15391560 }
15401561 }
15411562 if (needs_c_symbols) {
......@@ -2733,7 +2754,10 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
27332754 comp.bin_file.options.root_name
27342755 else
27352756 c_source_basename[0 .. c_source_basename.len - std.fs.path.extension(c_source_basename).len];
2736 const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{ o_basename_noext, comp.getTarget().oFileExt() });
2757 const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{
2758 o_basename_noext,
2759 comp.bin_file.options.object_format.fileExt(comp.bin_file.options.target.cpu.arch),
2760 });
27372761
27382762 const digest = if (!comp.disable_c_depfile and try man.hit()) man.final() else blk: {
27392763 var argv = std.ArrayList([]const u8).init(comp.gpa);
......@@ -3028,7 +3052,7 @@ pub fn addCCArgs(
30283052 if (!comp.bin_file.options.strip) {
30293053 try argv.append("-g");
30303054 switch (comp.bin_file.options.object_format) {
3031 .coff, .pe => try argv.append("-gcodeview"),
3055 .coff => try argv.append("-gcodeview"),
30323056 else => {},
30333057 }
30343058 }
......@@ -3954,6 +3978,16 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
39543978 const id_symlink_basename = "stage1.id";
39553979 const libs_txt_basename = "libs.txt";
39563980
3981 // The include_compiler_rt stored in the bin file options here means that we need
3982 // compiler-rt symbols *somehow*. However, in the context of using the stage1 backend
3983 // we need to tell stage1 to include compiler-rt only if stage1 is the place that
3984 // needs to provide those symbols. Otherwise the stage2 infrastructure will take care
3985 // of it in the linker, by putting compiler_rt.o into a static archive, or linking
3986 // compiler_rt.a against an executable. In other words we only want to set this flag
3987 // for stage1 if we are using build-obj.
3988 const include_compiler_rt = comp.bin_file.options.output_mode == .Obj and
3989 comp.bin_file.options.include_compiler_rt;
3990
39573991 // We are about to obtain this lock, so here we give other processes a chance first.
39583992 comp.releaseStage1Lock();
39593993
......@@ -3975,6 +4009,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
39754009 man.hash.add(target.os.getVersionRange());
39764010 man.hash.add(comp.bin_file.options.dll_export_fns);
39774011 man.hash.add(comp.bin_file.options.function_sections);
4012 man.hash.add(include_compiler_rt);
39784013 man.hash.add(comp.bin_file.options.is_test);
39794014 man.hash.add(comp.bin_file.options.emit != null);
39804015 man.hash.add(mod.emit_h != null);
......@@ -3983,6 +4018,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
39834018 }
39844019 man.hash.addOptionalEmitLoc(comp.emit_asm);
39854020 man.hash.addOptionalEmitLoc(comp.emit_llvm_ir);
4021 man.hash.addOptionalEmitLoc(comp.emit_llvm_bc);
39864022 man.hash.addOptionalEmitLoc(comp.emit_analysis);
39874023 man.hash.addOptionalEmitLoc(comp.emit_docs);
39884024 man.hash.add(comp.test_evented_io);
......@@ -4088,13 +4124,14 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
40884124 ) orelse return error.OutOfMemory;
40894125
40904126 const emit_bin_path = if (comp.bin_file.options.emit != null) blk: {
4091 const bin_basename = try std.zig.binNameAlloc(arena, .{
4127 const obj_basename = try std.zig.binNameAlloc(arena, .{
40924128 .root_name = comp.bin_file.options.root_name,
40934129 .target = target,
40944130 .output_mode = .Obj,
40954131 });
4096 break :blk try directory.join(arena, &[_][]const u8{bin_basename});
4132 break :blk try directory.join(arena, &[_][]const u8{obj_basename});
40974133 } else "";
4134
40984135 if (mod.emit_h != null) {
40994136 log.warn("-femit-h is not available in the stage1 backend; no .h file will be produced", .{});
41004137 }
......@@ -4102,6 +4139,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
41024139 const emit_h_path = try stage1LocPath(arena, emit_h_loc, directory);
41034140 const emit_asm_path = try stage1LocPath(arena, comp.emit_asm, directory);
41044141 const emit_llvm_ir_path = try stage1LocPath(arena, comp.emit_llvm_ir, directory);
4142 const emit_llvm_bc_path = try stage1LocPath(arena, comp.emit_llvm_bc, directory);
41054143 const emit_analysis_path = try stage1LocPath(arena, comp.emit_analysis, directory);
41064144 const emit_docs_path = try stage1LocPath(arena, comp.emit_docs, directory);
41074145 const stage1_pkg = try createStage1Pkg(arena, "root", mod.root_pkg, null);
......@@ -4122,6 +4160,8 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
41224160 .emit_asm_len = emit_asm_path.len,
41234161 .emit_llvm_ir_ptr = emit_llvm_ir_path.ptr,
41244162 .emit_llvm_ir_len = emit_llvm_ir_path.len,
4163 .emit_bitcode_ptr = emit_llvm_bc_path.ptr,
4164 .emit_bitcode_len = emit_llvm_bc_path.len,
41254165 .emit_analysis_json_ptr = emit_analysis_path.ptr,
41264166 .emit_analysis_json_len = emit_analysis_path.len,
41274167 .emit_docs_ptr = emit_docs_path.ptr,
......@@ -4150,6 +4190,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
41504190 .valgrind_enabled = comp.bin_file.options.valgrind,
41514191 .tsan_enabled = comp.bin_file.options.tsan,
41524192 .function_sections = comp.bin_file.options.function_sections,
4193 .include_compiler_rt = include_compiler_rt,
41534194 .enable_stack_probing = comp.bin_file.options.stack_check,
41544195 .red_zone = comp.bin_file.options.red_zone,
41554196 .enable_time_report = comp.time_report,
src/codegen/llvm.zig+131-65
......@@ -72,9 +72,9 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 {
7272 .renderscript32 => "renderscript32",
7373 .renderscript64 => "renderscript64",
7474 .ve => "ve",
75 .spu_2 => return error.LLVMBackendDoesNotSupportSPUMarkII,
76 .spirv32 => return error.LLVMBackendDoesNotSupportSPIRV,
77 .spirv64 => return error.LLVMBackendDoesNotSupportSPIRV,
75 .spu_2 => return error.@"LLVM backend does not support SPU Mark II",
76 .spirv32 => return error.@"LLVM backend does not support SPIR-V",
77 .spirv64 => return error.@"LLVM backend does not support SPIR-V",
7878 };
7979
8080 const llvm_os = switch (target.os.tag) {
......@@ -114,11 +114,13 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 {
114114 .wasi => "wasi",
115115 .emscripten => "emscripten",
116116 .uefi => "windows",
117 .opencl => return error.LLVMBackendDoesNotSupportOpenCL,
118 .glsl450 => return error.LLVMBackendDoesNotSupportGLSL450,
119 .vulkan => return error.LLVMBackendDoesNotSupportVulkan,
120 .plan9 => return error.LLVMBackendDoesNotSupportPlan9,
121 .other => "unknown",
117
118 .opencl,
119 .glsl450,
120 .vulkan,
121 .plan9,
122 .other,
123 => "unknown",
122124 };
123125
124126 const llvm_abi = switch (target.abi) {
......@@ -152,84 +154,105 @@ pub const Object = struct {
152154 llvm_module: *const llvm.Module,
153155 context: *const llvm.Context,
154156 target_machine: *const llvm.TargetMachine,
155 object_pathZ: [:0]const u8,
156
157 pub fn create(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Object {
158 _ = sub_path;
159 const self = try allocator.create(Object);
160 errdefer allocator.destroy(self);
161
162 const obj_basename = try std.zig.binNameAlloc(allocator, .{
163 .root_name = options.root_name,
164 .target = options.target,
165 .output_mode = .Obj,
166 });
167 defer allocator.free(obj_basename);
168157
169 const o_directory = options.module.?.zig_cache_artifact_directory;
170 const object_path = try o_directory.join(allocator, &[_][]const u8{obj_basename});
171 defer allocator.free(object_path);
172
173 const object_pathZ = try allocator.dupeZ(u8, object_path);
174 errdefer allocator.free(object_pathZ);
158 pub fn create(gpa: *Allocator, options: link.Options) !*Object {
159 const obj = try gpa.create(Object);
160 errdefer gpa.destroy(obj);
161 obj.* = try Object.init(gpa, options);
162 return obj;
163 }
175164
165 pub fn init(gpa: *Allocator, options: link.Options) !Object {
176166 const context = llvm.Context.create();
177167 errdefer context.dispose();
178168
179169 initializeLLVMTargets();
180170
181 const root_nameZ = try allocator.dupeZ(u8, options.root_name);
182 defer allocator.free(root_nameZ);
171 const root_nameZ = try gpa.dupeZ(u8, options.root_name);
172 defer gpa.free(root_nameZ);
183173 const llvm_module = llvm.Module.createWithName(root_nameZ.ptr, context);
184174 errdefer llvm_module.dispose();
185175
186 const llvm_target_triple = try targetTriple(allocator, options.target);
187 defer allocator.free(llvm_target_triple);
176 const llvm_target_triple = try targetTriple(gpa, options.target);
177 defer gpa.free(llvm_target_triple);
188178
189179 var error_message: [*:0]const u8 = undefined;
190180 var target: *const llvm.Target = undefined;
191181 if (llvm.Target.getFromTriple(llvm_target_triple.ptr, &target, &error_message).toBool()) {
192182 defer llvm.disposeMessage(error_message);
193183
194 const stderr = std.io.getStdErr().writer();
195 try stderr.print(
196 \\Zig is expecting LLVM to understand this target: '{s}'
197 \\However LLVM responded with: "{s}"
198 \\
199 ,
200 .{ llvm_target_triple, error_message },
201 );
202 return error.InvalidLLVMTriple;
184 log.err("LLVM failed to parse '{s}': {s}", .{ llvm_target_triple, error_message });
185 return error.InvalidLlvmTriple;
203186 }
204187
205 const opt_level: llvm.CodeGenOptLevel = if (options.optimize_mode == .Debug) .None else .Aggressive;
188 const opt_level: llvm.CodeGenOptLevel = if (options.optimize_mode == .Debug)
189 .None
190 else
191 .Aggressive;
192
193 const reloc_mode: llvm.RelocMode = if (options.pic)
194 .PIC
195 else if (options.link_mode == .Dynamic)
196 llvm.RelocMode.DynamicNoPIC
197 else
198 .Static;
199
200 const code_model: llvm.CodeModel = switch (options.machine_code_model) {
201 .default => .Default,
202 .tiny => .Tiny,
203 .small => .Small,
204 .kernel => .Kernel,
205 .medium => .Medium,
206 .large => .Large,
207 };
208
209 // TODO handle float ABI better- it should depend on the ABI portion of std.Target
210 const float_abi: llvm.ABIType = .Default;
211
212 // TODO a way to override this as part of std.Target ABI?
213 const abi_name: ?[*:0]const u8 = switch (options.target.cpu.arch) {
214 .riscv32 => switch (options.target.os.tag) {
215 .linux => "ilp32d",
216 else => "ilp32",
217 },
218 .riscv64 => switch (options.target.os.tag) {
219 .linux => "lp64d",
220 else => "lp64",
221 },
222 else => null,
223 };
224
206225 const target_machine = llvm.TargetMachine.create(
207226 target,
208227 llvm_target_triple.ptr,
209 "",
210 "",
228 if (options.target.cpu.model.llvm_name) |s| s.ptr else null,
229 options.llvm_cpu_features,
211230 opt_level,
212 .Static,
213 .Default,
231 reloc_mode,
232 code_model,
233 options.function_sections,
234 float_abi,
235 abi_name,
214236 );
215237 errdefer target_machine.dispose();
216238
217 self.* = .{
239 return Object{
218240 .llvm_module = llvm_module,
219241 .context = context,
220242 .target_machine = target_machine,
221 .object_pathZ = object_pathZ,
222243 };
223 return self;
224244 }
225245
226 pub fn deinit(self: *Object, allocator: *Allocator) void {
246 pub fn deinit(self: *Object) void {
227247 self.target_machine.dispose();
228248 self.llvm_module.dispose();
229249 self.context.dispose();
250 self.* = undefined;
251 }
230252
231 allocator.free(self.object_pathZ);
232 allocator.destroy(self);
253 pub fn destroy(self: *Object, gpa: *Allocator) void {
254 self.deinit();
255 gpa.destroy(self);
233256 }
234257
235258 fn initializeLLVMTargets() void {
......@@ -240,38 +263,81 @@ pub const Object = struct {
240263 llvm.initializeAllAsmParsers();
241264 }
242265
266 fn locPath(
267 arena: *Allocator,
268 opt_loc: ?Compilation.EmitLoc,
269 cache_directory: Compilation.Directory,
270 ) !?[*:0]u8 {
271 const loc = opt_loc orelse return null;
272 const directory = loc.directory orelse cache_directory;
273 const slice = try directory.joinZ(arena, &[_][]const u8{loc.basename});
274 return slice.ptr;
275 }
276
243277 pub fn flushModule(self: *Object, comp: *Compilation) !void {
244278 if (comp.verbose_llvm_ir) {
245 const dump = self.llvm_module.printToString();
246 defer llvm.disposeMessage(dump);
247
248 const stderr = std.io.getStdErr().writer();
249 try stderr.writeAll(std.mem.spanZ(dump));
279 self.llvm_module.dump();
250280 }
251281
252 {
282 if (std.debug.runtime_safety) {
253283 var error_message: [*:0]const u8 = undefined;
254284 // verifyModule always allocs the error_message even if there is no error
255285 defer llvm.disposeMessage(error_message);
256286
257287 if (self.llvm_module.verify(.ReturnStatus, &error_message).toBool()) {
258 const stderr = std.io.getStdErr().writer();
259 try stderr.print("broken LLVM module found: {s}\nThis is a bug in the Zig compiler.", .{error_message});
260 return error.BrokenLLVMModule;
288 std.debug.print("\n{s}\n", .{error_message});
289 @panic("LLVM module verification failed");
261290 }
262291 }
263292
293 var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
294 defer arena_allocator.deinit();
295 const arena = &arena_allocator.allocator;
296
297 const mod = comp.bin_file.options.module.?;
298 const cache_dir = mod.zig_cache_artifact_directory;
299
300 const emit_bin_path: ?[*:0]const u8 = if (comp.bin_file.options.emit != null) blk: {
301 const obj_basename = try std.zig.binNameAlloc(arena, .{
302 .root_name = comp.bin_file.options.root_name,
303 .target = comp.bin_file.options.target,
304 .output_mode = .Obj,
305 });
306 if (cache_dir.joinZ(arena, &[_][]const u8{obj_basename})) |p| {
307 break :blk p.ptr;
308 } else |err| {
309 return err;
310 }
311 } else null;
312
313 const emit_asm_path = try locPath(arena, comp.emit_asm, cache_dir);
314 const emit_llvm_ir_path = try locPath(arena, comp.emit_llvm_ir, cache_dir);
315 const emit_llvm_bc_path = try locPath(arena, comp.emit_llvm_bc, cache_dir);
316
264317 var error_message: [*:0]const u8 = undefined;
265318 if (self.target_machine.emitToFile(
266319 self.llvm_module,
267 self.object_pathZ.ptr,
268 .ObjectFile,
269320 &error_message,
270 ).toBool()) {
321 comp.bin_file.options.optimize_mode == .Debug,
322 comp.bin_file.options.optimize_mode == .ReleaseSmall,
323 comp.time_report,
324 comp.bin_file.options.tsan,
325 comp.bin_file.options.lto,
326 emit_asm_path,
327 emit_bin_path,
328 emit_llvm_ir_path,
329 emit_llvm_bc_path,
330 )) {
271331 defer llvm.disposeMessage(error_message);
272332
273 const stderr = std.io.getStdErr().writer();
274 try stderr.print("LLVM failed to emit file: {s}\n", .{error_message});
333 const emit_asm_msg = emit_asm_path orelse "(none)";
334 const emit_bin_msg = emit_bin_path orelse "(none)";
335 const emit_llvm_ir_msg = emit_llvm_ir_path orelse "(none)";
336 const emit_llvm_bc_msg = emit_llvm_bc_path orelse "(none)";
337 log.err("LLVM failed to emit asm={s} bin={s} ir={s} bc={s}: {s}", .{
338 emit_asm_msg, emit_bin_msg, emit_llvm_ir_msg, emit_llvm_bc_msg,
339 error_message,
340 });
275341 return error.FailedToEmit;
276342 }
277343 }
src/codegen/llvm/bindings.zig+35-13
......@@ -123,6 +123,9 @@ pub const Module = opaque {
123123
124124 pub const getNamedGlobal = LLVMGetNamedGlobal;
125125 extern fn LLVMGetNamedGlobal(M: *const Module, Name: [*:0]const u8) ?*const Value;
126
127 pub const dump = LLVMDumpModule;
128 extern fn LLVMDumpModule(M: *const Module) void;
126129};
127130
128131pub const lookupIntrinsicID = LLVMLookupIntrinsicID;
......@@ -250,31 +253,41 @@ pub const BasicBlock = opaque {
250253};
251254
252255pub const TargetMachine = opaque {
253 pub const create = LLVMCreateTargetMachine;
254 extern fn LLVMCreateTargetMachine(
256 pub const create = ZigLLVMCreateTargetMachine;
257 extern fn ZigLLVMCreateTargetMachine(
255258 T: *const Target,
256259 Triple: [*:0]const u8,
257 CPU: [*:0]const u8,
258 Features: [*:0]const u8,
260 CPU: ?[*:0]const u8,
261 Features: ?[*:0]const u8,
259262 Level: CodeGenOptLevel,
260263 Reloc: RelocMode,
261 CodeModel: CodeMode,
264 CodeModel: CodeModel,
265 function_sections: bool,
266 float_abi: ABIType,
267 abi_name: ?[*:0]const u8,
262268 ) *const TargetMachine;
263269
264270 pub const dispose = LLVMDisposeTargetMachine;
265271 extern fn LLVMDisposeTargetMachine(T: *const TargetMachine) void;
266272
267 pub const emitToFile = LLVMTargetMachineEmitToFile;
268 extern fn LLVMTargetMachineEmitToFile(
269 *const TargetMachine,
273 pub const emitToFile = ZigLLVMTargetMachineEmitToFile;
274 extern fn ZigLLVMTargetMachineEmitToFile(
275 T: *const TargetMachine,
270276 M: *const Module,
271 Filename: [*:0]const u8,
272 codegen: CodeGenFileType,
273277 ErrorMessage: *[*:0]const u8,
274 ) Bool;
278 is_debug: bool,
279 is_small: bool,
280 time_report: bool,
281 tsan: bool,
282 lto: bool,
283 asm_filename: ?[*:0]const u8,
284 bin_filename: ?[*:0]const u8,
285 llvm_ir_filename: ?[*:0]const u8,
286 bitcode_filename: ?[*:0]const u8,
287 ) bool;
275288};
276289
277pub const CodeMode = enum(c_int) {
290pub const CodeModel = enum(c_int) {
278291 Default,
279292 JITDefault,
280293 Tiny,
......@@ -295,7 +308,7 @@ pub const RelocMode = enum(c_int) {
295308 Default,
296309 Static,
297310 PIC,
298 DynamicNoPic,
311 DynamicNoPIC,
299312 ROPI,
300313 RWPI,
301314 ROPI_RWPI,
......@@ -306,6 +319,15 @@ pub const CodeGenFileType = enum(c_int) {
306319 ObjectFile,
307320};
308321
322pub const ABIType = enum(c_int) {
323 /// Target-specific (either soft or hard depending on triple, etc).
324 Default,
325 /// Soft float.
326 Soft,
327 // Hard float.
328 Hard,
329};
330
309331pub const Target = opaque {
310332 pub const getFromTriple = LLVMGetTargetFromTriple;
311333 extern fn LLVMGetTargetFromTriple(Triple: [*:0]const u8, T: **const Target, ErrorMessage: *[*:0]const u8) Bool;
src/link.zig+10-6
......@@ -191,7 +191,7 @@ pub const File = struct {
191191 const use_stage1 = build_options.is_stage1 and options.use_stage1;
192192 if (use_stage1 or options.emit == null) {
193193 return switch (options.object_format) {
194 .coff, .pe => &(try Coff.createEmpty(allocator, options)).base,
194 .coff => &(try Coff.createEmpty(allocator, options)).base,
195195 .elf => &(try Elf.createEmpty(allocator, options)).base,
196196 .macho => &(try MachO.createEmpty(allocator, options)).base,
197197 .wasm => &(try Wasm.createEmpty(allocator, options)).base,
......@@ -206,9 +206,10 @@ pub const File = struct {
206206 const use_lld = build_options.have_llvm and options.use_lld; // comptime known false when !have_llvm
207207 const sub_path = if (use_lld) blk: {
208208 if (options.module == null) {
209 // No point in opening a file, we would not write anything to it. Initialize with empty.
209 // No point in opening a file, we would not write anything to it.
210 // Initialize with empty.
210211 return switch (options.object_format) {
211 .coff, .pe => &(try Coff.createEmpty(allocator, options)).base,
212 .coff => &(try Coff.createEmpty(allocator, options)).base,
212213 .elf => &(try Elf.createEmpty(allocator, options)).base,
213214 .macho => &(try MachO.createEmpty(allocator, options)).base,
214215 .plan9 => &(try Plan9.createEmpty(allocator, options)).base,
......@@ -219,13 +220,16 @@ pub const File = struct {
219220 .raw => return error.RawObjectFormatUnimplemented,
220221 };
221222 }
222 // Open a temporary object file, not the final output file because we want to link with LLD.
223 break :blk try std.fmt.allocPrint(allocator, "{s}{s}", .{ emit.sub_path, options.target.oFileExt() });
223 // Open a temporary object file, not the final output file because we
224 // want to link with LLD.
225 break :blk try std.fmt.allocPrint(allocator, "{s}{s}", .{
226 emit.sub_path, options.object_format.fileExt(options.target.cpu.arch),
227 });
224228 } else emit.sub_path;
225229 errdefer if (use_lld) allocator.free(sub_path);
226230
227231 const file: *File = switch (options.object_format) {
228 .coff, .pe => &(try Coff.openPath(allocator, sub_path, options)).base,
232 .coff => &(try Coff.openPath(allocator, sub_path, options)).base,
229233 .elf => &(try Elf.openPath(allocator, sub_path, options)).base,
230234 .macho => &(try MachO.openPath(allocator, sub_path, options)).base,
231235 .plan9 => &(try Plan9.openPath(allocator, sub_path, options)).base,
src/link/Coff.zig+13-12
......@@ -17,9 +17,9 @@ const link = @import("../link.zig");
1717const build_options = @import("build_options");
1818const Cache = @import("../Cache.zig");
1919const mingw = @import("../mingw.zig");
20const llvm_backend = @import("../codegen/llvm.zig");
2120const Air = @import("../Air.zig");
2221const Liveness = @import("../Liveness.zig");
22const LlvmObject = @import("../codegen/llvm.zig").Object;
2323
2424const allocation_padding = 4 / 3;
2525const minimum_text_block_size = 64 * allocation_padding;
......@@ -37,7 +37,7 @@ pub const base_tag: link.File.Tag = .coff;
3737const msdos_stub = @embedFile("msdos-stub.bin");
3838
3939/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
40llvm_object: ?*llvm_backend.Object = null,
40llvm_object: ?*LlvmObject = null,
4141
4242base: link.File,
4343ptr_width: PtrWidth,
......@@ -132,7 +132,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
132132 const self = try createEmpty(allocator, options);
133133 errdefer self.base.destroy();
134134
135 self.llvm_object = try llvm_backend.Object.create(allocator, sub_path, options);
135 self.llvm_object = try LlvmObject.create(allocator, options);
136136 return self;
137137 }
138138
......@@ -657,10 +657,7 @@ fn writeOffsetTableEntry(self: *Coff, index: usize) !void {
657657}
658658
659659pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
660 if (build_options.skip_non_native and
661 builtin.object_format != .coff and
662 builtin.object_format != .pe)
663 {
660 if (build_options.skip_non_native and builtin.object_format != .coff) {
664661 @panic("Attempted to compile for object format that was disabled by build configuration");
665662 }
666663 if (build_options.have_llvm) {
......@@ -697,7 +694,7 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live
697694}
698695
699696pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void {
700 if (build_options.skip_non_native and builtin.object_format != .coff and builtin.object_format != .pe) {
697 if (build_options.skip_non_native and builtin.object_format != .coff) {
701698 @panic("Attempted to compile for object format that was disabled by build configuration");
702699 }
703700 if (build_options.have_llvm) {
......@@ -823,8 +820,11 @@ pub fn flushModule(self: *Coff, comp: *Compilation) !void {
823820 const tracy = trace(@src());
824821 defer tracy.end();
825822
826 if (build_options.have_llvm)
827 if (self.llvm_object) |llvm_object| return try llvm_object.flushModule(comp);
823 if (build_options.have_llvm) {
824 if (self.llvm_object) |llvm_object| {
825 return try llvm_object.flushModule(comp);
826 }
827 }
828828
829829 if (self.text_section_size_dirty) {
830830 // Write the new raw size in the .text header
......@@ -1398,8 +1398,9 @@ pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !v
13981398}
13991399
14001400pub fn deinit(self: *Coff) void {
1401 if (build_options.have_llvm)
1402 if (self.llvm_object) |ir_module| ir_module.deinit(self.base.allocator);
1401 if (build_options.have_llvm) {
1402 if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator);
1403 }
14031404
14041405 self.text_block_free_list.deinit(self.base.allocator);
14051406 self.offset_table.deinit(self.base.allocator);
src/link/Elf.zig+12-8
......@@ -25,9 +25,9 @@ const target_util = @import("../target.zig");
2525const glibc = @import("../glibc.zig");
2626const musl = @import("../musl.zig");
2727const Cache = @import("../Cache.zig");
28const llvm_backend = @import("../codegen/llvm.zig");
2928const Air = @import("../Air.zig");
3029const Liveness = @import("../Liveness.zig");
30const LlvmObject = @import("../codegen/llvm.zig").Object;
3131
3232const default_entry_addr = 0x8000000;
3333
......@@ -38,7 +38,7 @@ base: File,
3838ptr_width: PtrWidth,
3939
4040/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
41llvm_object: ?*llvm_backend.Object = null,
41llvm_object: ?*LlvmObject = null,
4242
4343/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
4444/// Same order as in the file.
......@@ -235,7 +235,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
235235 const self = try createEmpty(allocator, options);
236236 errdefer self.base.destroy();
237237
238 self.llvm_object = try llvm_backend.Object.create(allocator, sub_path, options);
238 self.llvm_object = try LlvmObject.create(allocator, options);
239239 return self;
240240 }
241241
......@@ -301,9 +301,9 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Elf {
301301}
302302
303303pub fn deinit(self: *Elf) void {
304 if (build_options.have_llvm)
305 if (self.llvm_object) |ir_module|
306 ir_module.deinit(self.base.allocator);
304 if (build_options.have_llvm) {
305 if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator);
306 }
307307
308308 self.sections.deinit(self.base.allocator);
309309 self.program_headers.deinit(self.base.allocator);
......@@ -750,8 +750,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
750750 if (build_options.have_llvm)
751751 if (self.llvm_object) |llvm_object| return try llvm_object.flushModule(comp);
752752
753 // TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the
754 // Zig source code.
753 // TODO This linker code currently assumes there is only 1 compilation unit and it
754 // corresponds to the Zig source code.
755755 const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
756756
757757 const target_endian = self.base.options.target.cpu.arch.endian();
......@@ -1289,6 +1289,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
12891289 // TODO: remove when stage2 can build compiler_rt.zig
12901290 if (!build_options.is_stage1) break :blk null;
12911291
1292 // In the case of build-obj we include the compiler-rt symbols directly alongside
1293 // the symbols of the root source file, in the same compilation unit.
1294 if (is_obj) break :blk null;
1295
12921296 if (is_exe_or_dyn_lib) {
12931297 break :blk comp.compiler_rt_static_lib.?.full_object_path;
12941298 } else {
src/link/MachO.zig+9-4
......@@ -29,21 +29,22 @@ const CodeSignature = @import("MachO/CodeSignature.zig");
2929const Compilation = @import("../Compilation.zig");
3030const DebugSymbols = @import("MachO/DebugSymbols.zig");
3131const Dylib = @import("MachO/Dylib.zig");
32const File = link.File;
3233const Object = @import("MachO/Object.zig");
3334const Liveness = @import("../Liveness.zig");
35const LlvmObject = @import("../codegen/llvm.zig").Object;
3436const LoadCommand = commands.LoadCommand;
3537const Module = @import("../Module.zig");
36const File = link.File;
38const SegmentCommand = commands.SegmentCommand;
3739pub const TextBlock = @import("MachO/TextBlock.zig");
3840const Trie = @import("MachO/Trie.zig");
39const SegmentCommand = commands.SegmentCommand;
4041
4142pub const base_tag: File.Tag = File.Tag.macho;
4243
4344base: File,
4445
4546/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
46llvm_object: ?*llvm_backend.Object = null,
47llvm_object: ?*LlvmObject = null,
4748
4849/// Debug symbols bundle (or dSym).
4950d_sym: ?DebugSymbols = null,
......@@ -333,7 +334,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
333334 const self = try createEmpty(allocator, options);
334335 errdefer self.base.destroy();
335336
336 self.llvm_object = try llvm_backend.Object.create(allocator, sub_path, options);
337 self.llvm_object = try LlvmObject.create(allocator, options);
337338 return self;
338339 }
339340
......@@ -3305,6 +3306,10 @@ fn writeSymbolTable(self: *MachO) !void {
33053306}
33063307
33073308pub fn deinit(self: *MachO) void {
3309 if (build_options.have_llvm) {
3310 if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator);
3311 }
3312
33083313 if (self.d_sym) |*ds| {
33093314 ds.deinit(self.base.allocator);
33103315 }
src/link/Wasm.zig+9-4
......@@ -19,7 +19,7 @@ const build_options = @import("build_options");
1919const wasi_libc = @import("../wasi_libc.zig");
2020const Cache = @import("../Cache.zig");
2121const TypedValue = @import("../TypedValue.zig");
22const llvm_backend = @import("../codegen/llvm.zig");
22const LlvmObject = @import("../codegen/llvm.zig").Object;
2323const Air = @import("../Air.zig");
2424const Liveness = @import("../Liveness.zig");
2525
......@@ -27,7 +27,7 @@ pub const base_tag = link.File.Tag.wasm;
2727
2828base: link.File,
2929/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
30llvm_object: ?*llvm_backend.Object = null,
30llvm_object: ?*LlvmObject = null,
3131/// List of all function Decls to be written to the output file. The index of
3232/// each Decl in this list at the time of writing the binary is used as the
3333/// function index. In the event where ext_funcs' size is not 0, the index of
......@@ -121,7 +121,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
121121 const self = try createEmpty(allocator, options);
122122 errdefer self.base.destroy();
123123
124 self.llvm_object = try llvm_backend.Object.create(allocator, sub_path, options);
124 self.llvm_object = try LlvmObject.create(allocator, options);
125125 return self;
126126 }
127127
......@@ -153,6 +153,9 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Wasm {
153153}
154154
155155pub fn deinit(self: *Wasm) void {
156 if (build_options.have_llvm) {
157 if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator);
158 }
156159 for (self.symbols.items) |decl| {
157160 decl.fn_link.wasm.functype.deinit(self.base.allocator);
158161 decl.fn_link.wasm.code.deinit(self.base.allocator);
......@@ -642,7 +645,9 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
642645 break :blk full_obj_path;
643646 } else null;
644647
645 const compiler_rt_path: ?[]const u8 = if (self.base.options.include_compiler_rt)
648 const is_obj = self.base.options.output_mode == .Obj;
649
650 const compiler_rt_path: ?[]const u8 = if (self.base.options.include_compiler_rt and !is_obj)
646651 comp.compiler_rt_static_lib.?.full_object_path
647652 else
648653 null;
src/main.zig+40-22
......@@ -287,9 +287,9 @@ const usage_build_generic =
287287 \\ .s Target-specific assembly source code
288288 \\ .S Assembly with C preprocessor (requires LLVM extensions)
289289 \\ .c C source code (requires LLVM extensions)
290 \\ .cpp C++ source code (requires LLVM extensions)
291 \\ Other C++ extensions: .C .cc .cxx
290 \\ .cxx .cc .C .cpp C++ source code (requires LLVM extensions)
292291 \\ .m Objective-C source code (requires LLVM extensions)
292 \\ .bc LLVM IR Module (requires LLVM extensions)
293293 \\
294294 \\General Options:
295295 \\ -h, --help Print this help and exit
......@@ -301,6 +301,8 @@ const usage_build_generic =
301301 \\ -fno-emit-asm (default) Do not output .s (assembly code)
302302 \\ -femit-llvm-ir[=path] Produce a .ll file with LLVM IR (requires LLVM extensions)
303303 \\ -fno-emit-llvm-ir (default) Do not produce a .ll file with LLVM IR
304 \\ -femit-llvm-bc[=path] Produce a LLVM module as a .bc file (requires LLVM extensions)
305 \\ -fno-emit-llvm-bc (default) Do not produce a LLVM module as a .bc file
304306 \\ -femit-h[=path] Generate a C header file (.h)
305307 \\ -fno-emit-h (default) Do not generate a C header file (.h)
306308 \\ -femit-docs[=path] Create a docs/ dir with html documentation
......@@ -359,15 +361,14 @@ const usage_build_generic =
359361 \\ --single-threaded Code assumes it is only used single-threaded
360362 \\ -ofmt=[mode] Override target object format
361363 \\ elf Executable and Linking Format
362 \\ c Compile to C source code
364 \\ c C source code
363365 \\ wasm WebAssembly
364 \\ pe Portable Executable (Windows)
365366 \\ coff Common Object File Format (Windows)
366367 \\ macho macOS relocatables
367368 \\ spirv Standard, Portable Intermediate Representation V (SPIR-V)
368369 \\ plan9 Plan 9 from Bell Labs object format
369 \\ hex (planned) Intel IHEX
370 \\ raw (planned) Dump machine code directly
370 \\ hex (planned feature) Intel IHEX
371 \\ raw (planned feature) Dump machine code directly
371372 \\ -dirafter [dir] Add directory to AFTER include search path
372373 \\ -isystem [dir] Add directory to SYSTEM include search path
373374 \\ -I[dir] Add directory to include search path
......@@ -384,8 +385,8 @@ const usage_build_generic =
384385 \\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so)
385386 \\ --sysroot [path] Set the system root directory (usually /)
386387 \\ --version [ver] Dynamic library semver
387 \\ -fsoname[=name] (Linux) Override the default SONAME value
388 \\ -fno-soname (Linux) Disable emitting a SONAME
388 \\ -fsoname[=name] Override the default SONAME value
389 \\ -fno-soname Disable emitting a SONAME
389390 \\ -fLLD Force using LLD as the linker
390391 \\ -fno-LLD Prevent using LLD as the linker
391392 \\ -fcompiler-rt Always include compiler-rt symbols in output
......@@ -552,6 +553,7 @@ fn buildOutputType(
552553 var emit_bin: EmitBin = .yes_default_path;
553554 var emit_asm: Emit = .no;
554555 var emit_llvm_ir: Emit = .no;
556 var emit_llvm_bc: Emit = .no;
555557 var emit_docs: Emit = .no;
556558 var emit_analysis: Emit = .no;
557559 var target_arch_os_abi: []const u8 = "native";
......@@ -1011,6 +1013,12 @@ fn buildOutputType(
10111013 emit_llvm_ir = .{ .yes = arg["-femit-llvm-ir=".len..] };
10121014 } else if (mem.eql(u8, arg, "-fno-emit-llvm-ir")) {
10131015 emit_llvm_ir = .no;
1016 } else if (mem.eql(u8, arg, "-femit-llvm-bc")) {
1017 emit_llvm_bc = .yes_default_path;
1018 } else if (mem.startsWith(u8, arg, "-femit-llvm-bc=")) {
1019 emit_llvm_bc = .{ .yes = arg["-femit-llvm-bc=".len..] };
1020 } else if (mem.eql(u8, arg, "-fno-emit-llvm-bc")) {
1021 emit_llvm_bc = .no;
10141022 } else if (mem.eql(u8, arg, "-femit-docs")) {
10151023 emit_docs = .yes_default_path;
10161024 } else if (mem.startsWith(u8, arg, "-femit-docs=")) {
......@@ -1720,8 +1728,6 @@ fn buildOutputType(
17201728 break :blk .c;
17211729 } else if (mem.eql(u8, ofmt, "coff")) {
17221730 break :blk .coff;
1723 } else if (mem.eql(u8, ofmt, "pe")) {
1724 break :blk .pe;
17251731 } else if (mem.eql(u8, ofmt, "macho")) {
17261732 break :blk .macho;
17271733 } else if (mem.eql(u8, ofmt, "wasm")) {
......@@ -1765,7 +1771,7 @@ fn buildOutputType(
17651771 };
17661772
17671773 const a_out_basename = switch (object_format) {
1768 .pe, .coff => "a.exe",
1774 .coff => "a.exe",
17691775 else => "a.out",
17701776 };
17711777
......@@ -1830,10 +1836,10 @@ fn buildOutputType(
18301836 var emit_h_resolved = emit_h.resolve(default_h_basename) catch |err| {
18311837 switch (emit_h) {
18321838 .yes => {
1833 fatal("unable to open directory from argument 'femit-h', '{s}': {s}", .{ emit_h.yes, @errorName(err) });
1839 fatal("unable to open directory from argument '-femit-h', '{s}': {s}", .{ emit_h.yes, @errorName(err) });
18341840 },
18351841 .yes_default_path => {
1836 fatal("unable to open directory from arguments 'name' or 'soname', '{s}': {s}", .{ default_h_basename, @errorName(err) });
1842 fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{ default_h_basename, @errorName(err) });
18371843 },
18381844 .no => unreachable,
18391845 }
......@@ -1844,10 +1850,10 @@ fn buildOutputType(
18441850 var emit_asm_resolved = emit_asm.resolve(default_asm_basename) catch |err| {
18451851 switch (emit_asm) {
18461852 .yes => {
1847 fatal("unable to open directory from argument 'femit-asm', '{s}': {s}", .{ emit_asm.yes, @errorName(err) });
1853 fatal("unable to open directory from argument '-femit-asm', '{s}': {s}", .{ emit_asm.yes, @errorName(err) });
18481854 },
18491855 .yes_default_path => {
1850 fatal("unable to open directory from arguments 'name' or 'soname', '{s}': {s}", .{ default_asm_basename, @errorName(err) });
1856 fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{ default_asm_basename, @errorName(err) });
18511857 },
18521858 .no => unreachable,
18531859 }
......@@ -1858,16 +1864,30 @@ fn buildOutputType(
18581864 var emit_llvm_ir_resolved = emit_llvm_ir.resolve(default_llvm_ir_basename) catch |err| {
18591865 switch (emit_llvm_ir) {
18601866 .yes => {
1861 fatal("unable to open directory from argument 'femit-llvm-ir', '{s}': {s}", .{ emit_llvm_ir.yes, @errorName(err) });
1867 fatal("unable to open directory from argument '-femit-llvm-ir', '{s}': {s}", .{ emit_llvm_ir.yes, @errorName(err) });
18621868 },
18631869 .yes_default_path => {
1864 fatal("unable to open directory from arguments 'name' or 'soname', '{s}': {s}", .{ default_llvm_ir_basename, @errorName(err) });
1870 fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{ default_llvm_ir_basename, @errorName(err) });
18651871 },
18661872 .no => unreachable,
18671873 }
18681874 };
18691875 defer emit_llvm_ir_resolved.deinit();
18701876
1877 const default_llvm_bc_basename = try std.fmt.allocPrint(arena, "{s}.bc", .{root_name});
1878 var emit_llvm_bc_resolved = emit_llvm_bc.resolve(default_llvm_bc_basename) catch |err| {
1879 switch (emit_llvm_bc) {
1880 .yes => {
1881 fatal("unable to open directory from argument '-femit-llvm-bc', '{s}': {s}", .{ emit_llvm_bc.yes, @errorName(err) });
1882 },
1883 .yes_default_path => {
1884 fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{ default_llvm_bc_basename, @errorName(err) });
1885 },
1886 .no => unreachable,
1887 }
1888 };
1889 defer emit_llvm_bc_resolved.deinit();
1890
18711891 const default_analysis_basename = try std.fmt.allocPrint(arena, "{s}-analysis.json", .{root_name});
18721892 var emit_analysis_resolved = emit_analysis.resolve(default_analysis_basename) catch |err| {
18731893 switch (emit_analysis) {
......@@ -2003,6 +2023,7 @@ fn buildOutputType(
20032023 .emit_h = emit_h_resolved.data,
20042024 .emit_asm = emit_asm_resolved.data,
20052025 .emit_llvm_ir = emit_llvm_ir_resolved.data,
2026 .emit_llvm_bc = emit_llvm_bc_resolved.data,
20062027 .emit_docs = emit_docs_resolved.data,
20072028 .emit_analysis = emit_analysis_resolved.data,
20082029 .link_mode = link_mode,
......@@ -2408,11 +2429,8 @@ fn updateModule(gpa: *Allocator, comp: *Compilation, hook: AfterUpdateHook) !voi
24082429
24092430 // If a .pdb file is part of the expected output, we must also copy
24102431 // it into place here.
2411 const coff_or_pe = switch (comp.bin_file.options.object_format) {
2412 .coff, .pe => true,
2413 else => false,
2414 };
2415 const have_pdb = coff_or_pe and !comp.bin_file.options.strip;
2432 const is_coff = comp.bin_file.options.object_format == .coff;
2433 const have_pdb = is_coff and !comp.bin_file.options.strip;
24162434 if (have_pdb) {
24172435 // Replace `.out` or `.exe` with `.pdb` on both the source and destination
24182436 const src_bin_ext = fs.path.extension(bin_sub_path);
src/stage1.zig+3-1
......@@ -21,7 +21,6 @@ comptime {
2121 assert(build_options.is_stage1);
2222 assert(build_options.have_llvm);
2323 if (!builtin.is_test) {
24 _ = @import("compiler_rt");
2524 @export(main, .{ .name = "main" });
2625 }
2726}
......@@ -95,6 +94,8 @@ pub const Module = extern struct {
9594 emit_asm_len: usize,
9695 emit_llvm_ir_ptr: [*]const u8,
9796 emit_llvm_ir_len: usize,
97 emit_bitcode_ptr: [*]const u8,
98 emit_bitcode_len: usize,
9899 emit_analysis_json_ptr: [*]const u8,
99100 emit_analysis_json_len: usize,
100101 emit_docs_ptr: [*]const u8,
......@@ -124,6 +125,7 @@ pub const Module = extern struct {
124125 valgrind_enabled: bool,
125126 tsan_enabled: bool,
126127 function_sections: bool,
128 include_compiler_rt: bool,
127129 enable_stack_probing: bool,
128130 red_zone: bool,
129131 enable_time_report: bool,
src/stage1/all_types.hpp+2
......@@ -2090,6 +2090,7 @@ struct CodeGen {
20902090 Buf h_file_output_path;
20912091 Buf asm_file_output_path;
20922092 Buf llvm_ir_file_output_path;
2093 Buf bitcode_file_output_path;
20932094 Buf analysis_json_output_path;
20942095 Buf docs_output_path;
20952096
......@@ -2149,6 +2150,7 @@ struct CodeGen {
21492150 bool have_stack_probing;
21502151 bool red_zone;
21512152 bool function_sections;
2153 bool include_compiler_rt;
21522154 bool test_is_evented;
21532155 bool valgrind_enabled;
21542156 bool tsan_enabled;
src/stage1/codegen.cpp+27-6
......@@ -8506,19 +8506,22 @@ static void zig_llvm_emit_output(CodeGen *g) {
85068506 const char *asm_filename = nullptr;
85078507 const char *bin_filename = nullptr;
85088508 const char *llvm_ir_filename = nullptr;
8509 const char *bitcode_filename = nullptr;
85098510
85108511 if (buf_len(&g->o_file_output_path) != 0) bin_filename = buf_ptr(&g->o_file_output_path);
85118512 if (buf_len(&g->asm_file_output_path) != 0) asm_filename = buf_ptr(&g->asm_file_output_path);
85128513 if (buf_len(&g->llvm_ir_file_output_path) != 0) llvm_ir_filename = buf_ptr(&g->llvm_ir_file_output_path);
8514 if (buf_len(&g->bitcode_file_output_path) != 0) bitcode_filename = buf_ptr(&g->bitcode_file_output_path);
85138515
8514 // Unfortunately, LLVM shits the bed when we ask for both binary and assembly. So we call the entire
8515 // pipeline multiple times if this is requested.
8516 // Unfortunately, LLVM shits the bed when we ask for both binary and assembly.
8517 // So we call the entire pipeline multiple times if this is requested.
85168518 if (asm_filename != nullptr && bin_filename != nullptr) {
85178519 if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg,
85188520 g->build_mode == BuildModeDebug, is_small, g->enable_time_report, g->tsan_enabled,
8519 g->have_lto, nullptr, bin_filename, llvm_ir_filename))
8521 g->have_lto, nullptr, bin_filename, llvm_ir_filename, nullptr))
85208522 {
8521 fprintf(stderr, "LLVM failed to emit file: %s\n", err_msg);
8523 fprintf(stderr, "LLVM failed to emit bin=%s, ir=%s: %s\n",
8524 bin_filename, llvm_ir_filename, err_msg);
85228525 exit(1);
85238526 }
85248527 bin_filename = nullptr;
......@@ -8527,9 +8530,11 @@ static void zig_llvm_emit_output(CodeGen *g) {
85278530
85288531 if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg,
85298532 g->build_mode == BuildModeDebug, is_small, g->enable_time_report, g->tsan_enabled,
8530 g->have_lto, asm_filename, bin_filename, llvm_ir_filename))
8533 g->have_lto, asm_filename, bin_filename, llvm_ir_filename, bitcode_filename))
85318534 {
8532 fprintf(stderr, "LLVM failed to emit file: %s\n", err_msg);
8535 fprintf(stderr, "LLVM failed to emit asm=%s, bin=%s, ir=%s, bc=%s: %s\n",
8536 asm_filename, bin_filename, llvm_ir_filename, bitcode_filename,
8537 err_msg);
85338538 exit(1);
85348539 }
85358540
......@@ -9537,6 +9542,22 @@ static void gen_root_source(CodeGen *g) {
95379542 g->panic_fn = panic_fn_val->data.x_ptr.data.fn.fn_entry;
95389543 assert(g->panic_fn != nullptr);
95399544
9545 if (g->include_compiler_rt) {
9546 Buf *import_target_path;
9547 Buf full_path = BUF_INIT;
9548 ZigType *compiler_rt_import;
9549 if ((err = analyze_import(g, std_import, buf_create_from_str("./special/compiler_rt.zig"),
9550 &compiler_rt_import, &import_target_path, &full_path)))
9551 {
9552 if (err == ErrorFileNotFound) {
9553 fprintf(stderr, "unable to find '%s'", buf_ptr(import_target_path));
9554 } else {
9555 fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(&full_path), err_str(err));
9556 }
9557 exit(1);
9558 }
9559 }
9560
95409561 if (!g->error_during_imports) {
95419562 semantic_analyze(g);
95429563 }
src/stage1/stage1.cpp+2
......@@ -73,6 +73,7 @@ void zig_stage1_build_object(struct ZigStage1 *stage1) {
7373 buf_init_from_mem(&g->h_file_output_path, stage1->emit_h_ptr, stage1->emit_h_len);
7474 buf_init_from_mem(&g->asm_file_output_path, stage1->emit_asm_ptr, stage1->emit_asm_len);
7575 buf_init_from_mem(&g->llvm_ir_file_output_path, stage1->emit_llvm_ir_ptr, stage1->emit_llvm_ir_len);
76 buf_init_from_mem(&g->bitcode_file_output_path, stage1->emit_bitcode_ptr, stage1->emit_bitcode_len);
7677 buf_init_from_mem(&g->analysis_json_output_path, stage1->emit_analysis_json_ptr, stage1->emit_analysis_json_len);
7778 buf_init_from_mem(&g->docs_output_path, stage1->emit_docs_ptr, stage1->emit_docs_len);
7879
......@@ -100,6 +101,7 @@ void zig_stage1_build_object(struct ZigStage1 *stage1) {
100101 g->link_libc = stage1->link_libc;
101102 g->link_libcpp = stage1->link_libcpp;
102103 g->function_sections = stage1->function_sections;
104 g->include_compiler_rt = stage1->include_compiler_rt;
103105
104106 g->subsystem = stage1->subsystem;
105107
src/stage1/stage1.h+4
......@@ -157,6 +157,9 @@ struct ZigStage1 {
157157 const char *emit_llvm_ir_ptr;
158158 size_t emit_llvm_ir_len;
159159
160 const char *emit_bitcode_ptr;
161 size_t emit_bitcode_len;
162
160163 const char *emit_analysis_json_ptr;
161164 size_t emit_analysis_json_len;
162165
......@@ -193,6 +196,7 @@ struct ZigStage1 {
193196 bool valgrind_enabled;
194197 bool tsan_enabled;
195198 bool function_sections;
199 bool include_compiler_rt;
196200 bool enable_stack_probing;
197201 bool red_zone;
198202 bool enable_time_report;
src/stage1/zig0.cpp+5
......@@ -39,6 +39,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
3939 " --color [auto|off|on] enable or disable colored error messages\n"
4040 " --name [name] override output name\n"
4141 " -femit-bin=[path] Output machine code\n"
42 " -fcompiler-rt Always include compiler-rt symbols in output\n"
4243 " --pkg-begin [name] [path] make pkg available to import and push current pkg\n"
4344 " --pkg-end pop current pkg\n"
4445 " -ODebug build with optimizations off and safety on\n"
......@@ -266,6 +267,7 @@ int main(int argc, char **argv) {
266267 const char *mcpu = nullptr;
267268 bool single_threaded = false;
268269 bool is_test_build = false;
270 bool include_compiler_rt = false;
269271
270272 for (int i = 1; i < argc; i += 1) {
271273 char *arg = argv[i];
......@@ -334,6 +336,8 @@ int main(int argc, char **argv) {
334336 mcpu = arg + strlen("-mcpu=");
335337 } else if (str_starts_with(arg, "-femit-bin=")) {
336338 emit_bin_path = arg + strlen("-femit-bin=");
339 } else if (strcmp(arg, "-fcompiler-rt") == 0) {
340 include_compiler_rt = true;
337341 } else if (i + 1 >= argc) {
338342 fprintf(stderr, "Expected another argument after %s\n", arg);
339343 return print_error_usage(arg0);
......@@ -468,6 +472,7 @@ int main(int argc, char **argv) {
468472 stage1->subsystem = subsystem;
469473 stage1->pic = true;
470474 stage1->is_single_threaded = single_threaded;
475 stage1->include_compiler_rt = include_compiler_rt;
471476
472477 zig_stage1_build_object(stage1);
473478
src/translate_c.zig+257-15
......@@ -2,6 +2,7 @@
22//! and stage2.
33
44const std = @import("std");
5const testing = std.testing;
56const assert = std.debug.assert;
67const clang = @import("clang.zig");
78const ctok = std.c.tokenizer;
......@@ -18,6 +19,7 @@ const CallingConvention = std.builtin.CallingConvention;
1819pub const ClangErrMsg = clang.Stage2ErrorMsg;
1920
2021pub const Error = std.mem.Allocator.Error;
22const MacroProcessingError = Error || error{UnexpectedMacroToken};
2123const TypeError = Error || error{UnsupportedType};
2224const TransError = TypeError || error{UnsupportedTranslation};
2325
......@@ -27,6 +29,10 @@ const AliasList = std.ArrayList(struct {
2729 name: []const u8,
2830});
2931
32// Maps macro parameter names to token position, for determining if different
33// identifiers refer to the same positional argument in different macros.
34const ArgsPositionMap = std.StringArrayHashMapUnmanaged(usize);
35
3036const Scope = struct {
3137 id: Id,
3238 parent: ?*Scope,
......@@ -322,6 +328,8 @@ pub const Context = struct {
322328 /// up front in a pre-processing step.
323329 global_names: std.StringArrayHashMapUnmanaged(void) = .{},
324330
331 pattern_list: PatternList,
332
325333 fn getMangle(c: *Context) u32 {
326334 c.mangle_count += 1;
327335 return c.mangle_count;
......@@ -375,6 +383,7 @@ pub fn translate(
375383 .alias_list = AliasList.init(gpa),
376384 .global_scope = try arena.allocator.create(Scope.Root),
377385 .clang_context = ast_unit.getASTContext(),
386 .pattern_list = try PatternList.init(gpa),
378387 };
379388 context.global_scope.* = Scope.Root.init(&context);
380389 defer {
......@@ -385,6 +394,7 @@ pub fn translate(
385394 context.unnamed_typedefs.deinit(gpa);
386395 context.typedefs.deinit(gpa);
387396 context.global_scope.deinit();
397 context.pattern_list.deinit(gpa);
388398 }
389399
390400 try context.global_scope.nodes.append(Tag.usingnamespace_builtins.init());
......@@ -4829,6 +4839,220 @@ fn isZigPrimitiveType(name: []const u8) bool {
48294839 return @import("AstGen.zig").simple_types.has(name);
48304840}
48314841
4842const PatternList = struct {
4843 patterns: []Pattern,
4844
4845 /// Templates must be function-like macros
4846 /// first element is macro source, second element is the name of the function
4847 /// in std.lib.zig.c_translation.Macros which implements it
4848 const templates = [_][2][]const u8{
4849 [2][]const u8{ "f_SUFFIX(X) (X ## f)", "F_SUFFIX" },
4850 [2][]const u8{ "F_SUFFIX(X) (X ## F)", "F_SUFFIX" },
4851
4852 [2][]const u8{ "u_SUFFIX(X) (X ## u)", "U_SUFFIX" },
4853 [2][]const u8{ "U_SUFFIX(X) (X ## U)", "U_SUFFIX" },
4854
4855 [2][]const u8{ "l_SUFFIX(X) (X ## l)", "L_SUFFIX" },
4856 [2][]const u8{ "L_SUFFIX(X) (X ## L)", "L_SUFFIX" },
4857
4858 [2][]const u8{ "ul_SUFFIX(X) (X ## ul)", "UL_SUFFIX" },
4859 [2][]const u8{ "uL_SUFFIX(X) (X ## uL)", "UL_SUFFIX" },
4860 [2][]const u8{ "Ul_SUFFIX(X) (X ## Ul)", "UL_SUFFIX" },
4861 [2][]const u8{ "UL_SUFFIX(X) (X ## UL)", "UL_SUFFIX" },
4862
4863 [2][]const u8{ "ll_SUFFIX(X) (X ## ll)", "LL_SUFFIX" },
4864 [2][]const u8{ "LL_SUFFIX(X) (X ## LL)", "LL_SUFFIX" },
4865
4866 [2][]const u8{ "ull_SUFFIX(X) (X ## ull)", "ULL_SUFFIX" },
4867 [2][]const u8{ "uLL_SUFFIX(X) (X ## uLL)", "ULL_SUFFIX" },
4868 [2][]const u8{ "Ull_SUFFIX(X) (X ## Ull)", "ULL_SUFFIX" },
4869 [2][]const u8{ "ULL_SUFFIX(X) (X ## ULL)", "ULL_SUFFIX" },
4870
4871 [2][]const u8{ "CAST_OR_CALL(X, Y) (X)(Y)", "CAST_OR_CALL" },
4872
4873 [2][]const u8{
4874 \\wl_container_of(ptr, sample, member) \
4875 \\(__typeof__(sample))((char *)(ptr) - \
4876 \\ offsetof(__typeof__(*sample), member))
4877 ,
4878 "WL_CONTAINER_OF",
4879 },
4880 };
4881
4882 /// Assumes that `ms` represents a tokenized function-like macro.
4883 fn buildArgsHash(allocator: *mem.Allocator, ms: MacroSlicer, hash: *ArgsPositionMap) MacroProcessingError!void {
4884 assert(ms.tokens.len > 2);
4885 assert(ms.tokens[0].id == .Identifier);
4886 assert(ms.tokens[1].id == .LParen);
4887
4888 var i: usize = 2;
4889 while (true) : (i += 1) {
4890 const token = ms.tokens[i];
4891 switch (token.id) {
4892 .RParen => break,
4893 .Comma => continue,
4894 .Identifier => {
4895 const identifier = ms.slice(token);
4896 try hash.put(allocator, identifier, i);
4897 },
4898 else => return error.UnexpectedMacroToken,
4899 }
4900 }
4901 }
4902
4903 const Pattern = struct {
4904 tokens: []const CToken,
4905 source: []const u8,
4906 impl: []const u8,
4907 args_hash: ArgsPositionMap,
4908
4909 fn init(self: *Pattern, allocator: *mem.Allocator, template: [2][]const u8) Error!void {
4910 const source = template[0];
4911 const impl = template[1];
4912
4913 var tok_list = std.ArrayList(CToken).init(allocator);
4914 defer tok_list.deinit();
4915 try tokenizeMacro(source, &tok_list);
4916 const tokens = try allocator.dupe(CToken, tok_list.items);
4917
4918 self.* = .{
4919 .tokens = tokens,
4920 .source = source,
4921 .impl = impl,
4922 .args_hash = .{},
4923 };
4924 const ms = MacroSlicer{ .source = source, .tokens = tokens };
4925 buildArgsHash(allocator, ms, &self.args_hash) catch |err| switch (err) {
4926 error.UnexpectedMacroToken => unreachable,
4927 else => |e| return e,
4928 };
4929 }
4930
4931 fn deinit(self: *Pattern, allocator: *mem.Allocator) void {
4932 self.args_hash.deinit(allocator);
4933 allocator.free(self.tokens);
4934 }
4935
4936 /// This function assumes that `ms` has already been validated to contain a function-like
4937 /// macro, and that the parsed template macro in `self` also contains a function-like
4938 /// macro. Please review this logic carefully if changing that assumption. Two
4939 /// function-like macros are considered equivalent if and only if they contain the same
4940 /// list of tokens, modulo parameter names.
4941 fn isEquivalent(self: Pattern, ms: MacroSlicer, args_hash: ArgsPositionMap) bool {
4942 if (self.tokens.len != ms.tokens.len) return false;
4943 if (args_hash.count() != self.args_hash.count()) return false;
4944
4945 var i: usize = 2;
4946 while (self.tokens[i].id != .RParen) : (i += 1) {}
4947
4948 const pattern_slicer = MacroSlicer{ .source = self.source, .tokens = self.tokens };
4949 while (i < self.tokens.len) : (i += 1) {
4950 const pattern_token = self.tokens[i];
4951 const macro_token = ms.tokens[i];
4952 if (meta.activeTag(pattern_token.id) != meta.activeTag(macro_token.id)) return false;
4953
4954 const pattern_bytes = pattern_slicer.slice(pattern_token);
4955 const macro_bytes = ms.slice(macro_token);
4956 switch (pattern_token.id) {
4957 .Identifier => {
4958 const pattern_arg_index = self.args_hash.get(pattern_bytes);
4959 const macro_arg_index = args_hash.get(macro_bytes);
4960
4961 if (pattern_arg_index == null and macro_arg_index == null) {
4962 if (!mem.eql(u8, pattern_bytes, macro_bytes)) return false;
4963 } else if (pattern_arg_index != null and macro_arg_index != null) {
4964 if (pattern_arg_index.? != macro_arg_index.?) return false;
4965 } else {
4966 return false;
4967 }
4968 },
4969 .MacroString, .StringLiteral, .CharLiteral, .IntegerLiteral, .FloatLiteral => {
4970 if (!mem.eql(u8, pattern_bytes, macro_bytes)) return false;
4971 },
4972 else => {
4973 // other tags correspond to keywords and operators that do not contain a "payload"
4974 // that can vary
4975 },
4976 }
4977 }
4978 return true;
4979 }
4980 };
4981
4982 fn init(allocator: *mem.Allocator) Error!PatternList {
4983 const patterns = try allocator.alloc(Pattern, templates.len);
4984 for (templates) |template, i| {
4985 try patterns[i].init(allocator, template);
4986 }
4987 return PatternList{ .patterns = patterns };
4988 }
4989
4990 fn deinit(self: *PatternList, allocator: *mem.Allocator) void {
4991 for (self.patterns) |*pattern| pattern.deinit(allocator);
4992 allocator.free(self.patterns);
4993 }
4994
4995 fn match(self: PatternList, allocator: *mem.Allocator, ms: MacroSlicer) Error!?Pattern {
4996 var args_hash: ArgsPositionMap = .{};
4997 defer args_hash.deinit(allocator);
4998
4999 buildArgsHash(allocator, ms, &args_hash) catch |err| switch (err) {
5000 error.UnexpectedMacroToken => return null,
5001 else => |e| return e,
5002 };
5003
5004 for (self.patterns) |pattern| if (pattern.isEquivalent(ms, args_hash)) return pattern;
5005 return null;
5006 }
5007};
5008
5009const MacroSlicer = struct {
5010 source: []const u8,
5011 tokens: []const CToken,
5012 fn slice(self: MacroSlicer, token: CToken) []const u8 {
5013 return self.source[token.start..token.end];
5014 }
5015};
5016
5017// Testing here instead of test/translate_c.zig allows us to also test that the
5018// mapped function exists in `std.zig.c_translation.Macros`
5019test "Macro matching" {
5020 const helper = struct {
5021 const MacroFunctions = @import("std").zig.c_translation.Macros;
5022 fn checkMacro(allocator: *mem.Allocator, pattern_list: PatternList, source: []const u8, comptime expected_match: ?[]const u8) !void {
5023 var tok_list = std.ArrayList(CToken).init(allocator);
5024 defer tok_list.deinit();
5025 try tokenizeMacro(source, &tok_list);
5026 const macro_slicer = MacroSlicer{ .source = source, .tokens = tok_list.items };
5027 const matched = try pattern_list.match(allocator, macro_slicer);
5028 if (expected_match) |expected| {
5029 try testing.expectEqualStrings(expected, matched.?.impl);
5030 try testing.expect(@hasDecl(MacroFunctions, expected));
5031 } else {
5032 try testing.expectEqual(@as(@TypeOf(matched), null), matched);
5033 }
5034 }
5035 };
5036 const allocator = std.testing.allocator;
5037 var pattern_list = try PatternList.init(allocator);
5038 defer pattern_list.deinit(allocator);
5039
5040 try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## F)", "F_SUFFIX");
5041 try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## U)", "U_SUFFIX");
5042 try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## L)", "L_SUFFIX");
5043 try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## LL)", "LL_SUFFIX");
5044 try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## UL)", "UL_SUFFIX");
5045 try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## ULL)", "ULL_SUFFIX");
5046 try helper.checkMacro(allocator, pattern_list,
5047 \\container_of(a, b, c) \
5048 \\(__typeof__(b))((char *)(a) - \
5049 \\ offsetof(__typeof__(*b), c))
5050 , "WL_CONTAINER_OF");
5051
5052 try helper.checkMacro(allocator, pattern_list, "NO_MATCH(X, Y) (X + Y)", null);
5053 try helper.checkMacro(allocator, pattern_list, "CAST_OR_CALL(X, Y) (X)(Y)", "CAST_OR_CALL");
5054}
5055
48325056const MacroCtx = struct {
48335057 source: []const u8,
48345058 list: []const CToken,
......@@ -4855,8 +5079,30 @@ const MacroCtx = struct {
48555079 fn fail(self: *MacroCtx, c: *Context, comptime fmt: []const u8, args: anytype) !void {
48565080 return failDecl(c, self.loc, self.name, fmt, args);
48575081 }
5082
5083 fn makeSlicer(self: *const MacroCtx) MacroSlicer {
5084 return MacroSlicer{ .source = self.source, .tokens = self.list };
5085 }
48585086};
48595087
5088fn tokenizeMacro(source: []const u8, tok_list: *std.ArrayList(CToken)) Error!void {
5089 var tokenizer = std.c.Tokenizer{
5090 .buffer = source,
5091 };
5092 while (true) {
5093 const tok = tokenizer.next();
5094 switch (tok.id) {
5095 .Nl, .Eof => {
5096 try tok_list.append(tok);
5097 break;
5098 },
5099 .LineComment, .MultiLineComment => continue,
5100 else => {},
5101 }
5102 try tok_list.append(tok);
5103 }
5104}
5105
48605106fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void {
48615107 // TODO if we see #undef, delete it from the table
48625108 var it = unit.getLocalPreprocessingEntities_begin();
......@@ -4888,21 +5134,7 @@ fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void {
48885134 const slice_len = @ptrToInt(end_c) - @ptrToInt(begin_c);
48895135 const slice = begin_c[0..slice_len];
48905136
4891 var tokenizer = std.c.Tokenizer{
4892 .buffer = slice,
4893 };
4894 while (true) {
4895 const tok = tokenizer.next();
4896 switch (tok.id) {
4897 .Nl, .Eof => {
4898 try tok_list.append(tok);
4899 break;
4900 },
4901 .LineComment, .MultiLineComment => continue,
4902 else => {},
4903 }
4904 try tok_list.append(tok);
4905 }
5137 try tokenizeMacro(slice, &tok_list);
49065138
49075139 var macro_ctx = MacroCtx{
49085140 .source = slice,
......@@ -4960,6 +5192,16 @@ fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void {
49605192}
49615193
49625194fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
5195 const macro_slicer = m.makeSlicer();
5196 if (try c.pattern_list.match(c.gpa, macro_slicer)) |pattern| {
5197 const decl = try Tag.pub_var_simple.create(c.arena, .{
5198 .name = m.name,
5199 .init = try Tag.helpers_macro.create(c.arena, pattern.impl),
5200 });
5201 try c.global_scope.macro_table.put(m.name, decl);
5202 return;
5203 }
5204
49635205 var block_scope = try Scope.Block.init(c, &c.global_scope.base, false);
49645206 defer block_scope.deinit();
49655207 const scope = &block_scope.base;
src/translate_c/ast.zig+14
......@@ -193,6 +193,8 @@ pub const Node = extern union {
193193 helpers_flexible_array_type,
194194 /// @import("std").zig.c_translation.shuffleVectorIndex(lhs, rhs)
195195 helpers_shuffle_vector_index,
196 /// @import("std").zig.c_translation.Macro.<operand>
197 helpers_macro,
196198 /// @import("std").meta.Vector(lhs, rhs)
197199 std_meta_vector,
198200 /// @import("std").mem.zeroes(operand)
......@@ -339,6 +341,7 @@ pub const Node = extern union {
339341 .identifier,
340342 .warning,
341343 .type,
344 .helpers_macro,
342345 => Payload.Value,
343346 .discard => Payload.Discard,
344347 .@"if" => Payload.If,
......@@ -1112,6 +1115,16 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
11121115 .data = undefined,
11131116 });
11141117 },
1118 .helpers_macro => {
1119 const payload = node.castTag(.helpers_macro).?.data;
1120 const chain = [_][]const u8{
1121 "zig",
1122 "c_translation",
1123 "Macros",
1124 payload,
1125 };
1126 return renderStdImport(c, &chain);
1127 },
11151128 .string_slice => {
11161129 const payload = node.castTag(.string_slice).?.data;
11171130
......@@ -2310,6 +2323,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
23102323 .bit_or_assign,
23112324 .bit_xor_assign,
23122325 .assign,
2326 .helpers_macro,
23132327 => {
23142328 // these should never appear in places where grouping might be needed.
23152329 unreachable;
src/zig_llvm.cpp+17-2
......@@ -229,12 +229,14 @@ struct TimeTracerRAII {
229229bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
230230 char **error_message, bool is_debug,
231231 bool is_small, bool time_report, bool tsan, bool lto,
232 const char *asm_filename, const char *bin_filename, const char *llvm_ir_filename)
232 const char *asm_filename, const char *bin_filename,
233 const char *llvm_ir_filename, const char *bitcode_filename)
233234{
234235 TimePassesIsEnabled = time_report;
235236
236237 raw_fd_ostream *dest_asm_ptr = nullptr;
237238 raw_fd_ostream *dest_bin_ptr = nullptr;
239 raw_fd_ostream *dest_bitcode_ptr = nullptr;
238240
239241 if (asm_filename) {
240242 std::error_code EC;
......@@ -252,9 +254,19 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
252254 return true;
253255 }
254256 }
257 if (bitcode_filename) {
258 std::error_code EC;
259 dest_bitcode_ptr = new(std::nothrow) raw_fd_ostream(bitcode_filename, EC, sys::fs::F_None);
260 if (EC) {
261 *error_message = strdup((const char *)StringRef(EC.message()).bytes_begin());
262 return true;
263 }
264 }
255265
256266 std::unique_ptr<raw_fd_ostream> dest_asm(dest_asm_ptr),
257 dest_bin(dest_bin_ptr);
267 dest_bin(dest_bin_ptr),
268 dest_bitcode(dest_bitcode_ptr);
269
258270
259271 auto PID = sys::Process::getProcessId();
260272 std::string ProcName = "zig-";
......@@ -389,6 +401,9 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
389401 if (dest_bin && lto) {
390402 WriteBitcodeToFile(module, *dest_bin);
391403 }
404 if (dest_bitcode) {
405 WriteBitcodeToFile(module, *dest_bitcode);
406 }
392407
393408 if (time_report) {
394409 TimerGroup::printAll(errs());
src/zig_llvm.h+2-1
......@@ -49,7 +49,8 @@ ZIG_EXTERN_C char *ZigLLVMGetNativeFeatures(void);
4949ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
5050 char **error_message, bool is_debug,
5151 bool is_small, bool time_report, bool tsan, bool lto,
52 const char *asm_filename, const char *bin_filename, const char *llvm_ir_filename);
52 const char *asm_filename, const char *bin_filename,
53 const char *llvm_ir_filename, const char *bitcode_filename);
5354
5455
5556enum ZigLLVMABIType {
test/cases.zig+70
......@@ -1065,6 +1065,76 @@ pub fn addCases(ctx: *TestContext) !void {
10651065 ":5:19: error: redeclaration of local constant 'c'",
10661066 ":4:19: note: previous declaration here",
10671067 });
1068 case.addError(
1069 \\pub fn main() void {
1070 \\ var i = 0;
1071 \\ for (n) |_, i| {
1072 \\ }
1073 \\}
1074 , &[_][]const u8{
1075 ":3:17: error: redeclaration of local variable 'i'",
1076 ":2:9: note: previous declaration here",
1077 });
1078 case.addError(
1079 \\pub fn main() void {
1080 \\ var i = 0;
1081 \\ for (n) |i| {
1082 \\ }
1083 \\}
1084 , &[_][]const u8{
1085 ":3:14: error: redeclaration of local variable 'i'",
1086 ":2:9: note: previous declaration here",
1087 });
1088 case.addError(
1089 \\pub fn main() void {
1090 \\ var i = 0;
1091 \\ while (n) |i| {
1092 \\ }
1093 \\}
1094 , &[_][]const u8{
1095 ":3:16: error: redeclaration of local variable 'i'",
1096 ":2:9: note: previous declaration here",
1097 });
1098 case.addError(
1099 \\pub fn main() void {
1100 \\ var i = 0;
1101 \\ while (n) |bruh| {
1102 \\ _ = bruh;
1103 \\ } else |i| {
1104 \\
1105 \\ }
1106 \\}
1107 , &[_][]const u8{
1108 ":5:13: error: redeclaration of local variable 'i'",
1109 ":2:9: note: previous declaration here",
1110 });
1111 case.addError(
1112 \\pub fn main() void {
1113 \\ var i = 0;
1114 \\ if (true) |i| {}
1115 \\}
1116 , &[_][]const u8{
1117 ":3:16: error: redeclaration of local variable 'i'",
1118 ":2:9: note: previous declaration here",
1119 });
1120 case.addError(
1121 \\pub fn main() void {
1122 \\ var i = 0;
1123 \\ if (true) |i| {} else |e| {}
1124 \\}
1125 , &[_][]const u8{
1126 ":3:16: error: redeclaration of local variable 'i'",
1127 ":2:9: note: previous declaration here",
1128 });
1129 case.addError(
1130 \\pub fn main() void {
1131 \\ var i = 0;
1132 \\ if (true) |_| {} else |i| {}
1133 \\}
1134 , &[_][]const u8{
1135 ":3:28: error: redeclaration of local variable 'i'",
1136 ":2:9: note: previous declaration here",
1137 });
10681138 }
10691139
10701140 {
test/translate_c.zig+6
......@@ -3624,4 +3624,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
36243624 ,
36253625 \\pub export var @"_": c_int = 42;
36263626 });
3627
3628 cases.add("Macro matching",
3629 \\#define FOO(X) (X ## U)
3630 , &[_][]const u8{
3631 \\pub const FOO = @import("std").zig.c_translation.Macros.U_SUFFIX;
3632 });
36273633}