| author | |
| committer | |
| log | 1157dd95714e57a699f06066ccfc7ed511ebf43f |
| tree | 69f3c45315c31aa6cd14bf77498e3d0570c7733f |
| parent | 809fcd4910552b57e0a61567fd5706719315bb22 |
13 files changed, 331 insertions(+), 338 deletions(-)
build.zig+4-5| ... | @@ -712,11 +712,6 @@ pub fn build(b: *std.Build) !void { | ... | @@ -712,11 +712,6 @@ pub fn build(b: *std.Build) !void { |
| 712 | .skip_libc = skip_libc, | 712 | .skip_libc = skip_libc, |
| 713 | })); | 713 | })); |
| 714 | test_step.dependOn(tests.addCliTests(b)); | 714 | test_step.dependOn(tests.addCliTests(b)); |
| 715 | if (tests.addLlvmIrTests(b, .{ | ||
| 716 | .enable_llvm = enable_llvm, | ||
| 717 | .test_filters = test_filters, | ||
| 718 | .test_target_filters = test_target_filters, | ||
| 719 | })) |test_llvm_ir_step| test_step.dependOn(test_llvm_ir_step); | ||
| 720 | 715 | ||
| 721 | try addWasiUpdateStep(b, version); | 716 | try addWasiUpdateStep(b, version); |
| 722 | 717 | ||
| ... | @@ -822,6 +817,10 @@ pub fn build(b: *std.Build) !void { | ... | @@ -822,6 +817,10 @@ pub fn build(b: *std.Build) !void { |
| 822 | const test_incremental_step = b.step("test-incremental", "Run the incremental compilation tests"); | 817 | const test_incremental_step = b.step("test-incremental", "Run the incremental compilation tests"); |
| 823 | try tests.addRunnerTests(b, runner, test_incremental_step, "test/incremental", @import("test/incremental/matrix.zon"), runner_opts); | 818 | try tests.addRunnerTests(b, runner, test_incremental_step, "test/incremental", @import("test/incremental/matrix.zon"), runner_opts); |
| 824 | test_step.dependOn(test_incremental_step); | 819 | test_step.dependOn(test_incremental_step); |
| 820 | |||
| 821 | const test_llvm_ir_step = b.step("test-llvm-ir", "Run the LLVM IR tests"); | ||
| 822 | try tests.addRunnerTests(b, runner, test_llvm_ir_step, "test/llvm-ir", @import("test/llvm-ir/matrix.zon"), runner_opts); | ||
| 823 | test_step.dependOn(test_llvm_ir_step); | ||
| 825 | } | 824 | } |
| 826 | 825 | ||
| 827 | fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void { | 826 | fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void { |
lib/std/posix.zig+1-1| ... | @@ -797,7 +797,7 @@ pub fn dl_iterate_phdr( | ... | @@ -797,7 +797,7 @@ pub fn dl_iterate_phdr( |
| 797 | const dl = @import("dynamic_library.zig"); | 797 | const dl = @import("dynamic_library.zig"); |
| 798 | 798 | ||
| 799 | switch (builtin.object_format) { | 799 | switch (builtin.object_format) { |
| 800 | .elf, .c => {}, | 800 | .c, .elf, .llvmbc, .llvmir => {}, |
| 801 | else => @compileError("dl_iterate_phdr is not available for this target"), | 801 | else => @compileError("dl_iterate_phdr is not available for this target"), |
| 802 | } | 802 | } |
| 803 | 803 |
test/Runner.zig+26-5| ... | @@ -917,6 +917,7 @@ fn handleCommand( | ... | @@ -917,6 +917,7 @@ fn handleCommand( |
| 917 | .arch_os_abi = triple, | 917 | .arch_os_abi = triple, |
| 918 | .object_format = switch (backend) { | 918 | .object_format = switch (backend) { |
| 919 | .sema, .selfhosted, .llvm => null, | 919 | .sema, .selfhosted, .llvm => null, |
| 920 | .llvmir => "llvmir", | ||
| 920 | .cbe => "c", | 921 | .cbe => "c", |
| 921 | }, | 922 | }, |
| 922 | }) catch |err| switch (err) { | 923 | }) catch |err| switch (err) { |
| ... | @@ -1075,7 +1076,7 @@ fn handleCommand( | ... | @@ -1075,7 +1076,7 @@ fn handleCommand( |
| 1075 | ), | 1076 | ), |
| 1076 | }; | 1077 | }; |
| 1077 | }, | 1078 | }, |
| 1078 | .stdout, .exit, .gdb, .lldb => if (eb.errorMessageCount() > 0) { | 1079 | .stdout, .exit, .llvm, .gdb, .lldb => if (eb.errorMessageCount() > 0) { |
| 1079 | eb.renderToStderr(runner.io, .{}, .auto) catch |err| switch (err) { | 1080 | eb.renderToStderr(runner.io, .{}, .auto) catch |err| switch (err) { |
| 1080 | error.Canceled => {}, | 1081 | error.Canceled => {}, |
| 1081 | else => {}, | 1082 | else => {}, |
| ... | @@ -1146,6 +1147,7 @@ fn spawnCompiler( | ... | @@ -1146,6 +1147,7 @@ fn spawnCompiler( |
| 1146 | .sema => try argv.append(gpa, "-fno-emit-bin"), | 1147 | .sema => try argv.append(gpa, "-fno-emit-bin"), |
| 1147 | .selfhosted => try argv.append(gpa, "-fno-llvm"), | 1148 | .selfhosted => try argv.append(gpa, "-fno-llvm"), |
| 1148 | .llvm => try argv.append(gpa, "-fllvm"), | 1149 | .llvm => try argv.append(gpa, "-fllvm"), |
| 1150 | .llvmir => try argv.append(gpa, "-ofmt=llvmir"), | ||
| 1149 | .cbe => try argv.append(gpa, "-ofmt=c"), | 1151 | .cbe => try argv.append(gpa, "-ofmt=c"), |
| 1150 | } else try argv.appendSlice(gpa, &.{ "-I", path_buffer[0 .. (runner.args.lib_dir orelse | 1152 | } else try argv.appendSlice(gpa, &.{ "-I", path_buffer[0 .. (runner.args.lib_dir orelse |
| 1151 | return runner.fail("\"--lib=path/to/lib\" arg required with cbe", .{})) | 1153 | return runner.fail("\"--lib=path/to/lib\" arg required with cbe", .{})) |
| ... | @@ -1287,6 +1289,8 @@ const Compiler = struct { | ... | @@ -1287,6 +1289,8 @@ const Compiler = struct { |
| 1287 | /// Use the LLVM backend. | 1289 | /// Use the LLVM backend. |
| 1288 | /// Corresponds to `-fllvm -flld`. | 1290 | /// Corresponds to `-fllvm -flld`. |
| 1289 | llvm, | 1291 | llvm, |
| 1292 | /// Use the LLVM backend without libLLVM. | ||
| 1293 | llvmir, | ||
| 1290 | /// Use the C backend. The output is compiled with `zig cc`. | 1294 | /// Use the C backend. The output is compiled with `zig cc`. |
| 1291 | /// Corresponds to `-ofmt=c`. | 1295 | /// Corresponds to `-ofmt=c`. |
| 1292 | cbe, | 1296 | cbe, |
| ... | @@ -1325,7 +1329,7 @@ const Compiler = struct { | ... | @@ -1325,7 +1329,7 @@ const Compiler = struct { |
| 1325 | return .{ .tag = header.tag, .body = body }; | 1329 | return .{ .tag = header.tag, .body = body }; |
| 1326 | } | 1330 | } |
| 1327 | 1331 | ||
| 1328 | const Check = enum { errors, stdout, exit, gdb, lldb }; | 1332 | const Check = enum { errors, stdout, exit, llvm, gdb, lldb }; |
| 1329 | fn checkSuccess( | 1333 | fn checkSuccess( |
| 1330 | comp: *Compiler, | 1334 | comp: *Compiler, |
| 1331 | update: *const Update, | 1335 | update: *const Update, |
| ... | @@ -1361,7 +1365,7 @@ const Compiler = struct { | ... | @@ -1361,7 +1365,7 @@ const Compiler = struct { |
| 1361 | var script_buffer: [512]u8 = undefined; | 1365 | var script_buffer: [512]u8 = undefined; |
| 1362 | const script_path, const script_file, var script_fw = script: switch (check) { | 1366 | const script_path, const script_file, var script_fw = script: switch (check) { |
| 1363 | .errors => return, | 1367 | .errors => return, |
| 1364 | .stdout, .exit => .{ undefined, null, undefined }, | 1368 | .stdout, .exit, .llvm => .{ undefined, null, undefined }, |
| 1365 | .gdb => { | 1369 | .gdb => { |
| 1366 | const script_path = tmp_dir_path ++ std.Io.Dir.path.sep_str ++ "gdb"; | 1370 | const script_path = tmp_dir_path ++ std.Io.Dir.path.sep_str ++ "gdb"; |
| 1367 | argv.appendSliceAssumeCapacity(&.{ "gdb", "--batch", "--command", script_path, "--args" }); | 1371 | argv.appendSliceAssumeCapacity(&.{ "gdb", "--batch", "--command", script_path, "--args" }); |
| ... | @@ -1425,7 +1429,7 @@ const Compiler = struct { | ... | @@ -1425,7 +1429,7 @@ const Compiler = struct { |
| 1425 | const out_path = try std.Io.Dir.path.join(arena, &.{ out_dir_path, comp.out_name }); | 1429 | const out_path = try std.Io.Dir.path.join(arena, &.{ out_dir_path, comp.out_name }); |
| 1426 | const bin_path = switch (comp.target.backend.?) { | 1430 | const bin_path = switch (comp.target.backend.?) { |
| 1427 | .sema => return, | 1431 | .sema => return, |
| 1428 | .selfhosted, .llvm => out_path, | 1432 | .selfhosted, .llvm, .llvmir => out_path, |
| 1429 | .cbe => try comp.compileC(update.src_dir, out_path), | 1433 | .cbe => try comp.compileC(update.src_dir, out_path), |
| 1430 | }; | 1434 | }; |
| 1431 | 1435 | ||
| ... | @@ -1527,6 +1531,23 @@ const Compiler = struct { | ... | @@ -1527,6 +1531,23 @@ const Compiler = struct { |
| 1527 | .exe = exe: switch (check) { | 1531 | .exe = exe: switch (check) { |
| 1528 | .errors => unreachable, | 1532 | .errors => unreachable, |
| 1529 | .stdout, .exit => if (use_executor) .search else .{ .file = bin_file }, | 1533 | .stdout, .exit => if (use_executor) .search else .{ .file = bin_file }, |
| 1534 | .llvm => { | ||
| 1535 | var bin_fr = bin_file.reader(io, &.{}); | ||
| 1536 | const actual = bin_fr.interface.readAllocAll(arena, @intCast( | ||
| 1537 | bin_fr.getSize() catch |err| switch (err) { | ||
| 1538 | error.Canceled => |e| return e, | ||
| 1539 | else => |e| return runner.fail("unable to get size of {q}: {t}", .{ bin_path, e }), | ||
| 1540 | }, | ||
| 1541 | )) catch |err| switch (switch (err) { | ||
| 1542 | error.OutOfMemory => |e| return e, | ||
| 1543 | error.EndOfStream => |e| e, | ||
| 1544 | error.ReadFailed => bin_fr.err.?, | ||
| 1545 | }) { | ||
| 1546 | error.Canceled => |e| return e, | ||
| 1547 | else => |e| return runner.fail("unable to read {q}: {t}", .{ bin_path, e }), | ||
| 1548 | }; | ||
| 1549 | return update.checkMatches("LLVM IR", expected, actual); | ||
| 1550 | }, | ||
| 1530 | .gdb, .lldb => { | 1551 | .gdb, .lldb => { |
| 1531 | if (use_executor) { | 1552 | if (use_executor) { |
| 1532 | if (!runner.args.quiet) std.log.warn( | 1553 | if (!runner.args.quiet) std.log.warn( |
| ... | @@ -1603,7 +1624,7 @@ const Compiler = struct { | ... | @@ -1603,7 +1624,7 @@ const Compiler = struct { |
| 1603 | } | 1624 | } |
| 1604 | switch (result.term) { | 1625 | switch (result.term) { |
| 1605 | .exited => |code| switch (check) { | 1626 | .exited => |code| switch (check) { |
| 1606 | .errors => unreachable, | 1627 | .errors, .llvm => unreachable, |
| 1607 | .stdout => { | 1628 | .stdout => { |
| 1608 | if (code != 0) return update.fail( | 1629 | if (code != 0) return update.fail( |
| 1609 | "generated executable {q} failed with code {d}", | 1630 | "generated executable {q} failed with code {d}", |
test/llvm-ir/allowzero created+72| ... | @@ -0,0 +1,72 @@ | ||
| 1 | #obj { | ||
| 2 | --name test | ||
| 3 | -Mroot=test.zig | ||
| 4 | #} | ||
| 5 | |||
| 6 | #write test.zig { | ||
| 7 | export fn entry(ptr: *allowzero i16) i16 { | ||
| 8 | return ptr.*; | ||
| 9 | } | ||
| 10 | #} | ||
| 11 | #update "load" | ||
| 12 | #check llvm { | ||
| 13 | define #...@test.entry(ptr #...%0) #...#0 #...{ | ||
| 14 | #...load i16, ptr %0#... | ||
| 15 | } | ||
| 16 | |||
| 17 | #... | ||
| 18 | attributes #0 = {#... null_pointer_is_valid #...} | ||
| 19 | #} | ||
| 20 | |||
| 21 | #write test.zig { | ||
| 22 | export fn entry(ptr: *allowzero i16) void { | ||
| 23 | ptr.* = 42; | ||
| 24 | } | ||
| 25 | #} | ||
| 26 | #update "store" | ||
| 27 | #check llvm { | ||
| 28 | define #...@test.entry(ptr #...%0) #...#0 #...{ | ||
| 29 | #...store i16 42, ptr %0#... | ||
| 30 | } | ||
| 31 | |||
| 32 | #... | ||
| 33 | attributes #0 = {#... null_pointer_is_valid #...} | ||
| 34 | #} | ||
| 35 | |||
| 36 | #write test.zig { | ||
| 37 | export fn entry(ptr: *allowzero i16) i16 { | ||
| 38 | return entry2(ptr[0..1]); | ||
| 39 | } | ||
| 40 | fn entry2(slice: []allowzero i16) i16 { | ||
| 41 | @setRuntimeSafety(false); | ||
| 42 | return slice[0]; | ||
| 43 | } | ||
| 44 | #} | ||
| 45 | #update "slice load" | ||
| 46 | #check llvm { | ||
| 47 | define #...@test.entry2(#..., ptr #...%1, #...) #...#2 #...{ | ||
| 48 | #...load i16, ptr %#... | ||
| 49 | } | ||
| 50 | |||
| 51 | #... | ||
| 52 | attributes #2 = {#... null_pointer_is_valid #...} | ||
| 53 | #} | ||
| 54 | |||
| 55 | #write test.zig { | ||
| 56 | export fn entry(ptr: *allowzero i16) void { | ||
| 57 | entry2(ptr[0..1]); | ||
| 58 | } | ||
| 59 | fn entry2(slice: []allowzero i16) void { | ||
| 60 | @setRuntimeSafety(false); | ||
| 61 | slice[0] = 42; | ||
| 62 | } | ||
| 63 | #} | ||
| 64 | #update "slice store" | ||
| 65 | #check llvm { | ||
| 66 | define #...@test.entry2(#..., ptr #...%1, #...) #...#2 #...{ | ||
| 67 | #...store i16 42, ptr %#... | ||
| 68 | } | ||
| 69 | |||
| 70 | #... | ||
| 71 | attributes #2 = {#... null_pointer_is_valid #...} | ||
| 72 | #} | ||
test/llvm-ir/array_to_vector created+38| ... | @@ -0,0 +1,38 @@ | ||
| 1 | #obj { | ||
| 2 | --name test | ||
| 3 | -Mroot=test.zig | ||
| 4 | #} | ||
| 5 | |||
| 6 | #write test.zig { | ||
| 7 | export fn entry(arr: *const [16]u8) @Vector(16, u8) { | ||
| 8 | return arr.*; | ||
| 9 | } | ||
| 10 | #} | ||
| 11 | #update "one vector load" | ||
| 12 | #check llvm { | ||
| 13 | define #...<16 x i8> @test.entry(#...) #...{ | ||
| 14 | #... | ||
| 15 | %1 = alloca [16 x i8], align 1#... | ||
| 16 | %3 = load <16 x i8>, ptr %1, align 1#... | ||
| 17 | ret <16 x i8> %3#... | ||
| 18 | #} | ||
| 19 | |||
| 20 | #write test.zig { | ||
| 21 | export fn entry(a: *@Vector(8, u8), b: *@Vector(8, u8), src: [*]const u8) void { | ||
| 22 | const q: @Vector(16, u8) = src[0..16].*; | ||
| 23 | a.* = @shuffle(u8, q, undefined, @Vector(8, i32){ 0, 1, 2, 3, 4, 5, 6, 7 }); | ||
| 24 | b.* = @shuffle(u8, q, undefined, @Vector(8, i32){ 7, 8, 9, 10, 11, 12, 13, 14 }); | ||
| 25 | } | ||
| 26 | #} | ||
| 27 | #update "feeding overlapping shuffles" | ||
| 28 | #check llvm { | ||
| 29 | define #...void @test.entry(ptr #...%0, ptr #...%1, #...) #...{ | ||
| 30 | #... | ||
| 31 | %11 = load <16 x i8>, ptr %#... | ||
| 32 | %12 = shufflevector <16 x i8> %11, #... | ||
| 33 | store <8 x i8> %12, ptr %0, align 8#... | ||
| 34 | %13 = shufflevector <16 x i8> %11, #... | ||
| 35 | store <8 x i8> %13, ptr %1, align 8#... | ||
| 36 | ret void#... | ||
| 37 | } | ||
| 38 | #} | ||
test/llvm-ir/dead_store created+43| ... | @@ -0,0 +1,43 @@ | ||
| 1 | #obj { | ||
| 2 | --name test | ||
| 3 | -Mroot=test.zig | ||
| 4 | #} | ||
| 5 | |||
| 6 | #write test.zig { | ||
| 7 | export fn entry(ptr: *volatile i16) void { | ||
| 8 | ptr.* = 123; | ||
| 9 | ptr.* = 321; | ||
| 10 | } | ||
| 11 | #} | ||
| 12 | #update "volatile" | ||
| 13 | #check llvm { | ||
| 14 | define #...@test.entry(ptr #...%0) #...{ | ||
| 15 | #...store volatile i16 123, ptr %0#... | ||
| 16 | #...store volatile i16 321, ptr %0#... | ||
| 17 | } | ||
| 18 | |||
| 19 | #} | ||
| 20 | |||
| 21 | #write test.zig { | ||
| 22 | export fn entry(ptr: *volatile i16) void { | ||
| 23 | entry2(ptr[0..1]); | ||
| 24 | } | ||
| 25 | fn entry2(slice: []volatile i16) void { | ||
| 26 | @setRuntimeSafety(false); | ||
| 27 | slice[0] = 123; | ||
| 28 | slice[0] = 321; | ||
| 29 | } | ||
| 30 | #} | ||
| 31 | #update "volatile slice" | ||
| 32 | #check llvm { | ||
| 33 | define #...@test.entry(#...) #...{ | ||
| 34 | #... | ||
| 35 | } | ||
| 36 | |||
| 37 | #... | ||
| 38 | define #...@test.entry2(#...) #...{ | ||
| 39 | #...store volatile i16 123, ptr %#... | ||
| 40 | #...store volatile i16 321, ptr %#... | ||
| 41 | } | ||
| 42 | |||
| 43 | #} | ||
test/llvm-ir/load_store_bool created+28| ... | @@ -0,0 +1,28 @@ | ||
| 1 | #obj { | ||
| 2 | --name test | ||
| 3 | -Mroot=test.zig | ||
| 4 | #} | ||
| 5 | |||
| 6 | #write test.zig { | ||
| 7 | export fn foo(a: *bool, b: *align(2) bool) void { | ||
| 8 | const tmp = a.*; | ||
| 9 | a.* = b.*; | ||
| 10 | b.* = tmp; | ||
| 11 | } | ||
| 12 | #} | ||
| 13 | #update | ||
| 14 | #check llvm { | ||
| 15 | define #...void @test.foo(ptr #...%0, ptr #...%1) #...{ | ||
| 16 | #... | ||
| 17 | %5 = load i8, ptr %0, align 1#... | ||
| 18 | %6 = trunc nuw i8 %5 to i1#... | ||
| 19 | %8 = load i8, ptr %1, align 2#... | ||
| 20 | %9 = trunc nuw i8 %8 to i1#... | ||
| 21 | %10 = zext i1 %9 to i8#... | ||
| 22 | store i8 %10, ptr %0, align 1#... | ||
| 23 | %11 = zext i1 %6 to i8#... | ||
| 24 | store i8 %11, ptr %1, align 2#... | ||
| 25 | ret void#... | ||
| 26 | } | ||
| 27 | |||
| 28 | #} | ||
test/llvm-ir/matrix.zon created+9| ... | @@ -0,0 +1,9 @@ | ||
| 1 | .{ | ||
| 2 | .{ | ||
| 3 | .target = .{}, | ||
| 4 | .cache_mode = .whole, | ||
| 5 | .backend = .llvmir, | ||
| 6 | .linker = .old, | ||
| 7 | .pic = .pic, | ||
| 8 | }, | ||
| 9 | } | ||
test/llvm-ir/nonnull created+30| ... | @@ -0,0 +1,30 @@ | ||
| 1 | #obj { | ||
| 2 | --name test | ||
| 3 | -Mroot=test.zig | ||
| 4 | #} | ||
| 5 | |||
| 6 | #write test.zig { | ||
| 7 | export fn entry(ptr: *i16) i16 { | ||
| 8 | return ptr.*; | ||
| 9 | } | ||
| 10 | #} | ||
| 11 | #update "store" | ||
| 12 | #check llvm { | ||
| 13 | define #...@test.entry(ptr #...nonnull #...%0) #...{ | ||
| 14 | #...load i16, ptr %0#... | ||
| 15 | } | ||
| 16 | |||
| 17 | #} | ||
| 18 | |||
| 19 | #write test.zig { | ||
| 20 | export fn entry(ptr: *i16) void { | ||
| 21 | ptr.* = 42; | ||
| 22 | } | ||
| 23 | #} | ||
| 24 | #update "load" | ||
| 25 | #check llvm { | ||
| 26 | define #...@test.entry(ptr #...nonnull #...%0) #...{ | ||
| 27 | #...store i16 42, ptr %0#... | ||
| 28 | } | ||
| 29 | |||
| 30 | #} | ||
test/llvm-ir/unused_load created+79| ... | @@ -0,0 +1,79 @@ | ||
| 1 | #obj { | ||
| 2 | --name test | ||
| 3 | -Mroot=test.zig | ||
| 4 | #} | ||
| 5 | |||
| 6 | #write test.zig { | ||
| 7 | export fn entry(ptr: *i16) void { | ||
| 8 | _ = @atomicLoad(i16, ptr, .acquire); | ||
| 9 | } | ||
| 10 | #} | ||
| 11 | #update "atomic acquire" | ||
| 12 | #check llvm { | ||
| 13 | define #...@test.entry(ptr #...%0) #...{ | ||
| 14 | #...load atomic i16, ptr %0 acquire#... | ||
| 15 | } | ||
| 16 | |||
| 17 | #} | ||
| 18 | |||
| 19 | #write test.zig { | ||
| 20 | export fn entry(ptr: *volatile i16) void { | ||
| 21 | _ = @atomicLoad(i16, ptr, .unordered); | ||
| 22 | } | ||
| 23 | #} | ||
| 24 | #update "atomic volatile unordered" | ||
| 25 | #check llvm { | ||
| 26 | define #...@test.entry(ptr #...%0) #...{ | ||
| 27 | #...load atomic volatile i16, ptr %0 unordered#... | ||
| 28 | } | ||
| 29 | |||
| 30 | #} | ||
| 31 | |||
| 32 | #write test.zig { | ||
| 33 | export fn entry(ptr: *volatile i16) void { | ||
| 34 | _ = ptr.*; | ||
| 35 | } | ||
| 36 | #} | ||
| 37 | #update "volatile" | ||
| 38 | #check llvm { | ||
| 39 | define #...@test.entry(ptr #...%0) #...{ | ||
| 40 | #...load volatile i16, ptr %0#... | ||
| 41 | } | ||
| 42 | |||
| 43 | #} | ||
| 44 | |||
| 45 | #write test.zig { | ||
| 46 | export fn entry(ptr: *volatile i16) void { | ||
| 47 | entry2(ptr[0..1]); | ||
| 48 | } | ||
| 49 | fn entry2(slice: []volatile i16) void { | ||
| 50 | @setRuntimeSafety(false); | ||
| 51 | _ = slice[0]; | ||
| 52 | } | ||
| 53 | #} | ||
| 54 | #update "volatile slice" | ||
| 55 | #check llvm { | ||
| 56 | define #...@test.entry(#...) #...{ | ||
| 57 | #... | ||
| 58 | } | ||
| 59 | |||
| 60 | #... | ||
| 61 | define #...@test.entry2(#...) #...{ | ||
| 62 | #...load volatile i16, ptr %#... | ||
| 63 | } | ||
| 64 | |||
| 65 | #} | ||
| 66 | |||
| 67 | #write test.zig { | ||
| 68 | const S = packed struct(u16) { a: u2, b: u8, c: u6 }; | ||
| 69 | export fn entry(ptr: *volatile S) void { | ||
| 70 | _ = ptr.b; | ||
| 71 | } | ||
| 72 | #} | ||
| 73 | #update "volatile packed" | ||
| 74 | #check llvm { | ||
| 75 | define #...@test.entry(ptr #...%0) #...{ | ||
| 76 | #...load volatile i16, ptr %#... | ||
| 77 | } | ||
| 78 | |||
| 79 | #} | ||
test/llvm_ir.zig deleted-170| ... | @@ -1,170 +0,0 @@ | ||
| 1 | pub fn addCases(cases: *tests.LlvmIrContext) void { | ||
| 2 | cases.addMatches("nonnull ptr load", | ||
| 3 | \\export fn entry(ptr: *i16) i16 { | ||
| 4 | \\ return ptr.*; | ||
| 5 | \\} | ||
| 6 | , &.{ | ||
| 7 | "ptr nonnull", | ||
| 8 | "load i16, ptr %0", | ||
| 9 | }, .{}); | ||
| 10 | |||
| 11 | cases.addMatches("nonnull ptr store", | ||
| 12 | \\export fn entry(ptr: *i16) void { | ||
| 13 | \\ ptr.* = 42; | ||
| 14 | \\} | ||
| 15 | , &.{ | ||
| 16 | "ptr nonnull", | ||
| 17 | "store i16 42, ptr %0", | ||
| 18 | }, .{}); | ||
| 19 | |||
| 20 | cases.addMatches("unused acquire atomic ptr load", | ||
| 21 | \\export fn entry(ptr: *i16) void { | ||
| 22 | \\ _ = @atomicLoad(i16, ptr, .acquire); | ||
| 23 | \\} | ||
| 24 | , &.{ | ||
| 25 | "load atomic i16, ptr %0 acquire", | ||
| 26 | }, .{}); | ||
| 27 | |||
| 28 | cases.addMatches("unused unordered atomic volatile ptr load", | ||
| 29 | \\export fn entry(ptr: *volatile i16) void { | ||
| 30 | \\ _ = @atomicLoad(i16, ptr, .unordered); | ||
| 31 | \\} | ||
| 32 | , &.{ | ||
| 33 | "load atomic volatile i16, ptr %0 unordered", | ||
| 34 | }, .{}); | ||
| 35 | |||
| 36 | cases.addMatches("unused volatile ptr load", | ||
| 37 | \\export fn entry(ptr: *volatile i16) void { | ||
| 38 | \\ _ = ptr.*; | ||
| 39 | \\} | ||
| 40 | , &.{ | ||
| 41 | "load volatile i16, ptr %0", | ||
| 42 | }, .{}); | ||
| 43 | |||
| 44 | cases.addMatches("dead volatile ptr store", | ||
| 45 | \\export fn entry(ptr: *volatile i16) void { | ||
| 46 | \\ ptr.* = 123; | ||
| 47 | \\ ptr.* = 321; | ||
| 48 | \\} | ||
| 49 | , &.{ | ||
| 50 | "store volatile i16 123, ptr %0", | ||
| 51 | "store volatile i16 321, ptr %0", | ||
| 52 | }, .{}); | ||
| 53 | |||
| 54 | cases.addMatches("unused volatile slice load", | ||
| 55 | \\export fn entry(ptr: *volatile i16) void { | ||
| 56 | \\ entry2(ptr[0..1]); | ||
| 57 | \\} | ||
| 58 | \\fn entry2(ptr: []volatile i16) void { | ||
| 59 | \\ _ = ptr[0]; | ||
| 60 | \\} | ||
| 61 | , &.{ | ||
| 62 | "load volatile i16, ptr", | ||
| 63 | }, .{}); | ||
| 64 | |||
| 65 | cases.addMatches("dead volatile slice store", | ||
| 66 | \\export fn entry(ptr: *volatile i16) void { | ||
| 67 | \\ entry2(ptr[0..1]); | ||
| 68 | \\} | ||
| 69 | \\fn entry2(ptr: []volatile i16) void { | ||
| 70 | \\ ptr[0] = 123; | ||
| 71 | \\ ptr[0] = 321; | ||
| 72 | \\} | ||
| 73 | , &.{ | ||
| 74 | "store volatile i16 123, ptr", | ||
| 75 | "store volatile i16 321, ptr", | ||
| 76 | }, .{}); | ||
| 77 | |||
| 78 | cases.addMatches("unused volatile packed pointer load", | ||
| 79 | \\const S = packed struct(u16) { a: u2, b: u8, c: u6 }; | ||
| 80 | \\export fn entry(ptr: *volatile S) void { | ||
| 81 | \\ _ = ptr.b; | ||
| 82 | \\} | ||
| 83 | , &.{ | ||
| 84 | "load volatile i16, ptr", | ||
| 85 | }, .{}); | ||
| 86 | |||
| 87 | cases.addMatches("allowzero ptr load", | ||
| 88 | \\export fn entry(ptr: *allowzero i16) i16 { | ||
| 89 | \\ return ptr.*; | ||
| 90 | \\} | ||
| 91 | , &.{ | ||
| 92 | "null_pointer_is_valid", | ||
| 93 | "load i16, ptr %0", | ||
| 94 | }, .{}); | ||
| 95 | |||
| 96 | cases.addMatches("allowzero ptr store", | ||
| 97 | \\export fn entry(ptr: *allowzero i16) void { | ||
| 98 | \\ ptr.* = 42; | ||
| 99 | \\} | ||
| 100 | , &.{ | ||
| 101 | "null_pointer_is_valid", | ||
| 102 | "store i16 42, ptr %0", | ||
| 103 | }, .{}); | ||
| 104 | |||
| 105 | cases.addMatches("allowzero slice load", | ||
| 106 | \\export fn entry(ptr: *allowzero i16) i16 { | ||
| 107 | \\ return entry2(ptr[0..1]); | ||
| 108 | \\} | ||
| 109 | \\fn entry2(ptr: []allowzero i16) i16 { | ||
| 110 | \\ return ptr[0]; | ||
| 111 | \\} | ||
| 112 | , &.{ | ||
| 113 | "null_pointer_is_valid", | ||
| 114 | "load i16, ptr", | ||
| 115 | }, .{}); | ||
| 116 | |||
| 117 | cases.addMatches("allowzero slice store", | ||
| 118 | \\export fn entry(ptr: *allowzero i16) void { | ||
| 119 | \\ entry2(ptr[0..1]); | ||
| 120 | \\} | ||
| 121 | \\fn entry2(ptr: []allowzero i16) void { | ||
| 122 | \\ ptr[0] = 42; | ||
| 123 | \\} | ||
| 124 | , &.{ | ||
| 125 | "null_pointer_is_valid", | ||
| 126 | "store i16 42, ptr", | ||
| 127 | }, .{}); | ||
| 128 | |||
| 129 | cases.addMatches("load and store bool", | ||
| 130 | \\export fn foo(a: *bool, b: *align(2) bool) void { | ||
| 131 | \\ const tmp = a.*; | ||
| 132 | \\ a.* = b.*; | ||
| 133 | \\ b.* = tmp; | ||
| 134 | \\} | ||
| 135 | , &.{ | ||
| 136 | // TODO: this should all be one multiline string literal, but `-femit-llvm-ir` is currently | ||
| 137 | // emitting CRLF on Windows, which is a pain to handle here. In future that option will emit | ||
| 138 | // unoptimized LLVM IR emitted directly from Zig, so that bug will go away. | ||
| 139 | " %3 = load i8, ptr %0, align 1", | ||
| 140 | " %4 = trunc nuw i8 %3 to i1", | ||
| 141 | " %5 = load i8, ptr %1, align 2", | ||
| 142 | " %6 = trunc nuw i8 %5 to i1", | ||
| 143 | " %7 = zext i1 %6 to i8", | ||
| 144 | " store i8 %7, ptr %0, align 1", | ||
| 145 | " %8 = zext i1 %4 to i8", | ||
| 146 | " store i8 %8, ptr %1, align 2", | ||
| 147 | }, .{ .strip = true }); | ||
| 148 | |||
| 149 | cases.addMatches("array to vector coercion is one vector load", | ||
| 150 | \\export fn entry(arr: *const [16]u8) @Vector(16, u8) { | ||
| 151 | \\ return arr.*; | ||
| 152 | \\} | ||
| 153 | , &.{ | ||
| 154 | "load <16 x i8>, ptr", | ||
| 155 | }, .{}); | ||
| 156 | |||
| 157 | cases.addMatches("array to vector coercion feeding overlapping shuffles", | ||
| 158 | \\export fn entry(a: *@Vector(8, u8), b: *@Vector(8, u8), src: [*]const u8) void { | ||
| 159 | \\ const q: @Vector(16, u8) = src[0..16].*; | ||
| 160 | \\ a.* = @shuffle(u8, q, undefined, @Vector(8, i32){ 0, 1, 2, 3, 4, 5, 6, 7 }); | ||
| 161 | \\ b.* = @shuffle(u8, q, undefined, @Vector(8, i32){ 7, 8, 9, 10, 11, 12, 13, 14 }); | ||
| 162 | \\} | ||
| 163 | , &.{ | ||
| 164 | "load <16 x i8>, ptr", | ||
| 165 | "shufflevector <16 x i8>", | ||
| 166 | }, .{}); | ||
| 167 | } | ||
| 168 | |||
| 169 | const std = @import("std"); | ||
| 170 | const tests = @import("tests.zig"); | ||
test/src/LlvmIr.zig deleted-135| ... | @@ -1,135 +0,0 @@ | ||
| 1 | b: *std.Build, | ||
| 2 | options: Options, | ||
| 3 | root_step: *std.Build.Step, | ||
| 4 | |||
| 5 | pub const Options = struct { | ||
| 6 | enable_llvm: bool, | ||
| 7 | test_filters: []const []const u8, | ||
| 8 | test_target_filters: []const []const u8, | ||
| 9 | }; | ||
| 10 | |||
| 11 | const TestCase = struct { | ||
| 12 | name: []const u8, | ||
| 13 | source: []const u8, | ||
| 14 | check: union(enum) { | ||
| 15 | matches: []const []const u8, | ||
| 16 | exact: []const u8, | ||
| 17 | }, | ||
| 18 | params: Params, | ||
| 19 | |||
| 20 | pub const Params = struct { | ||
| 21 | code_model: std.builtin.CodeModel = .default, | ||
| 22 | dll_export_fns: ?bool = null, | ||
| 23 | dwarf_format: ?std.dwarf.Format = null, | ||
| 24 | error_tracing: ?bool = null, | ||
| 25 | no_builtin: ?bool = null, | ||
| 26 | omit_frame_pointer: ?bool = null, | ||
| 27 | // For most cases, we want to test the LLVM IR that we output; we don't want to be in the | ||
| 28 | // business of testing LLVM's optimization passes. `Debug` gets us the closest to that as it | ||
| 29 | // disables the vast majority of passes in LLVM. | ||
| 30 | optimize: std.builtin.Optimize = .debug, | ||
| 31 | pic: ?bool = null, | ||
| 32 | pie: ?bool = null, | ||
| 33 | red_zone: ?bool = null, | ||
| 34 | sanitize_thread: ?bool = null, | ||
| 35 | single_threaded: ?bool = null, | ||
| 36 | stack_check: ?bool = null, | ||
| 37 | stack_protector: ?bool = null, | ||
| 38 | strip: ?bool = null, | ||
| 39 | target: std.Target.Query = .{}, | ||
| 40 | unwind_tables: ?std.builtin.UnwindTables = null, | ||
| 41 | valgrind: ?bool = null, | ||
| 42 | }; | ||
| 43 | }; | ||
| 44 | |||
| 45 | pub fn addMatches( | ||
| 46 | self: *LlvmIr, | ||
| 47 | name: []const u8, | ||
| 48 | source: []const u8, | ||
| 49 | matches: []const []const u8, | ||
| 50 | params: TestCase.Params, | ||
| 51 | ) void { | ||
| 52 | self.addCase(.{ | ||
| 53 | .name = name, | ||
| 54 | .source = source, | ||
| 55 | .check = .{ .matches = matches }, | ||
| 56 | .params = params, | ||
| 57 | }); | ||
| 58 | } | ||
| 59 | |||
| 60 | pub fn addExact( | ||
| 61 | self: *LlvmIr, | ||
| 62 | name: []const u8, | ||
| 63 | source: []const u8, | ||
| 64 | expected: []const []const u8, | ||
| 65 | params: TestCase.Params, | ||
| 66 | ) void { | ||
| 67 | self.addCase(.{ | ||
| 68 | .name = name, | ||
| 69 | .source = source, | ||
| 70 | .check = .{ .exact = expected }, | ||
| 71 | .params = params, | ||
| 72 | }); | ||
| 73 | } | ||
| 74 | |||
| 75 | pub fn addCase(self: *LlvmIr, case: TestCase) void { | ||
| 76 | const target = self.b.resolveTargetQuery(case.params.target); | ||
| 77 | if (self.options.test_target_filters.len > 0) { | ||
| 78 | const triple_txt = target.query.zigTriple(self.b.allocator) catch @panic("OOM"); | ||
| 79 | for (self.options.test_target_filters) |filter| { | ||
| 80 | if (std.mem.find(u8, triple_txt, filter) != null) break; | ||
| 81 | } else return; | ||
| 82 | } | ||
| 83 | |||
| 84 | const name = std.fmt.allocPrint(self.b.allocator, "check llvm-ir {s}", .{case.name}) catch @panic("OOM"); | ||
| 85 | if (self.options.test_filters.len > 0) { | ||
| 86 | for (self.options.test_filters) |filter| { | ||
| 87 | if (std.mem.find(u8, name, filter) != null) break; | ||
| 88 | } else return; | ||
| 89 | } | ||
| 90 | |||
| 91 | const obj = self.b.addObject(.{ | ||
| 92 | .name = "test", | ||
| 93 | .root_module = self.b.createModule(.{ | ||
| 94 | .root_source_file = self.b.addWriteFiles().add("test.zig", case.source), | ||
| 95 | |||
| 96 | .code_model = case.params.code_model, | ||
| 97 | .error_tracing = case.params.error_tracing, | ||
| 98 | .omit_frame_pointer = case.params.omit_frame_pointer, | ||
| 99 | .optimize = case.params.optimize, | ||
| 100 | .pic = case.params.pic, | ||
| 101 | .sanitize_thread = case.params.sanitize_thread, | ||
| 102 | .single_threaded = case.params.single_threaded, | ||
| 103 | .strip = case.params.strip, | ||
| 104 | .target = target, | ||
| 105 | .unwind_tables = case.params.unwind_tables, | ||
| 106 | }), | ||
| 107 | .use_llvm = true, | ||
| 108 | }); | ||
| 109 | |||
| 110 | obj.dll_export_fns = case.params.dll_export_fns; | ||
| 111 | obj.pie = case.params.pie; | ||
| 112 | |||
| 113 | obj.root_module.dwarf_format = case.params.dwarf_format; | ||
| 114 | obj.root_module.no_builtin = case.params.no_builtin; | ||
| 115 | obj.root_module.red_zone = case.params.red_zone; | ||
| 116 | obj.root_module.stack_check = case.params.stack_check; | ||
| 117 | obj.root_module.stack_protector = case.params.stack_protector; | ||
| 118 | obj.root_module.valgrind = case.params.valgrind; | ||
| 119 | |||
| 120 | // This is not very sophisticated at the moment. Eventually, we should move towards something | ||
| 121 | // like LLVM's `FileCheck` utility (https://llvm.org/docs/CommandGuide/FileCheck.html), though | ||
| 122 | // likely a more simplified version as we probably don't want a full-blown regex engine in the | ||
| 123 | // standard library... | ||
| 124 | const check = self.b.addCheckFile(obj.getEmittedLlvmIr(), switch (case.check) { | ||
| 125 | .matches => |m| .{ .expected_matches = m }, | ||
| 126 | .exact => |e| .{ .expected_exact = e }, | ||
| 127 | }); | ||
| 128 | check.setName(name); | ||
| 129 | check.max_bytes = 64 * 1024 * 1024; // allow fairly big LLVM IR files | ||
| 130 | |||
| 131 | self.root_step.dependOn(&check.step); | ||
| 132 | } | ||
| 133 | |||
| 134 | const LlvmIr = @This(); | ||
| 135 | const std = @import("std"); | ||
test/tests.zig+1-22| ... | @@ -6,14 +6,12 @@ const OptimizeMode = std.builtin.Optimize; | ... | @@ -6,14 +6,12 @@ const OptimizeMode = std.builtin.Optimize; |
| 6 | const Step = std.Build.Step; | 6 | const Step = std.Build.Step; |
| 7 | 7 | ||
| 8 | // Cases | 8 | // Cases |
| 9 | const llvm_ir = @import("llvm_ir.zig"); | ||
| 10 | const libc = @import("libc.zig"); | 9 | const libc = @import("libc.zig"); |
| 11 | const link = @import("link.zig"); | 10 | const link = @import("link.zig"); |
| 12 | 11 | ||
| 13 | // Implementations | 12 | // Implementations |
| 14 | pub const ErrorTracesContext = @import("src/ErrorTrace.zig"); | 13 | pub const ErrorTracesContext = @import("src/ErrorTrace.zig"); |
| 15 | pub const StackTracesContext = @import("src/StackTrace.zig"); | 14 | pub const StackTracesContext = @import("src/StackTrace.zig"); |
| 16 | pub const LlvmIrContext = @import("src/LlvmIr.zig"); | ||
| 17 | pub const LibcContext = @import("src/Libc.zig"); | 15 | pub const LibcContext = @import("src/Libc.zig"); |
| 18 | pub const LinkContext = @import("src/Link.zig"); | 16 | pub const LinkContext = @import("src/Link.zig"); |
| 19 | 17 | ||
| ... | @@ -2196,7 +2194,7 @@ const link_targets = blk: { | ... | @@ -2196,7 +2194,7 @@ const link_targets = blk: { |
| 2196 | const RunnerTarget = struct { | 2194 | const RunnerTarget = struct { |
| 2197 | target: std.Target.Query, | 2195 | target: std.Target.Query, |
| 2198 | cache_mode: enum { whole, incremental }, | 2196 | cache_mode: enum { whole, incremental }, |
| 2199 | backend: enum { selfhosted, llvm, cbe }, | 2197 | backend: enum { selfhosted, llvm, llvmir, cbe }, |
| 2200 | linker: enum { lld, old, new }, | 2198 | linker: enum { lld, old, new }, |
| 2201 | pic: enum { nopic, pic, pie }, | 2199 | pic: enum { nopic, pic, pie }, |
| 2202 | }; | 2200 | }; |
| ... | @@ -3175,25 +3173,6 @@ fn isEditorFileName(name: []const u8) bool { | ... | @@ -3175,25 +3173,6 @@ fn isEditorFileName(name: []const u8) bool { |
| 3175 | std.mem.endsWith(u8, name, "~") or std.mem.endsWith(u8, name, ".swp"); | 3173 | std.mem.endsWith(u8, name, "~") or std.mem.endsWith(u8, name, ".swp"); |
| 3176 | } | 3174 | } |
| 3177 | 3175 | ||
| 3178 | pub fn addLlvmIrTests(b: *std.Build, options: LlvmIrContext.Options) ?*Step { | ||
| 3179 | const step = b.step("test-llvm-ir", "Run the LLVM IR tests"); | ||
| 3180 | |||
| 3181 | if (!options.enable_llvm) { | ||
| 3182 | step.dependOn(&b.addFail("test-llvm-ir requires -Denable-llvm").step); | ||
| 3183 | return null; | ||
| 3184 | } | ||
| 3185 | |||
| 3186 | var context: LlvmIrContext = .{ | ||
| 3187 | .b = b, | ||
| 3188 | .options = options, | ||
| 3189 | .root_step = step, | ||
| 3190 | }; | ||
| 3191 | |||
| 3192 | llvm_ir.addCases(&context); | ||
| 3193 | |||
| 3194 | return step; | ||
| 3195 | } | ||
| 3196 | |||
| 3197 | const libc_test_nsz_targets: []const std.Target.Query = &.{ | 3176 | const libc_test_nsz_targets: []const std.Target.Query = &.{ |
| 3198 | .{ | 3177 | .{ |
| 3199 | .cpu_arch = .arm, | 3178 | .cpu_arch = .arm, |