| 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 | 712 | .skip_libc = skip_libc, |
| 713 | 713 | })); |
| 714 | 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 | 716 | try addWasiUpdateStep(b, version); |
| 722 | 717 | |
| ... | ... | @@ -822,6 +817,10 @@ pub fn build(b: *std.Build) !void { |
| 822 | 817 | const test_incremental_step = b.step("test-incremental", "Run the incremental compilation tests"); |
| 823 | 818 | try tests.addRunnerTests(b, runner, test_incremental_step, "test/incremental", @import("test/incremental/matrix.zon"), runner_opts); |
| 824 | 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 | 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 | 797 | const dl = @import("dynamic_library.zig"); |
| 798 | 798 | |
| 799 | 799 | switch (builtin.object_format) { |
| 800 | .elf, .c => {}, | |
| 800 | .c, .elf, .llvmbc, .llvmir => {}, | |
| 801 | 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 | 917 | .arch_os_abi = triple, |
| 918 | 918 | .object_format = switch (backend) { |
| 919 | 919 | .sema, .selfhosted, .llvm => null, |
| 920 | .llvmir => "llvmir", | |
| 920 | 921 | .cbe => "c", |
| 921 | 922 | }, |
| 922 | 923 | }) catch |err| switch (err) { |
| ... | ... | @@ -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 | 1080 | eb.renderToStderr(runner.io, .{}, .auto) catch |err| switch (err) { |
| 1080 | 1081 | error.Canceled => {}, |
| 1081 | 1082 | else => {}, |
| ... | ... | @@ -1146,6 +1147,7 @@ fn spawnCompiler( |
| 1146 | 1147 | .sema => try argv.append(gpa, "-fno-emit-bin"), |
| 1147 | 1148 | .selfhosted => try argv.append(gpa, "-fno-llvm"), |
| 1148 | 1149 | .llvm => try argv.append(gpa, "-fllvm"), |
| 1150 | .llvmir => try argv.append(gpa, "-ofmt=llvmir"), | |
| 1149 | 1151 | .cbe => try argv.append(gpa, "-ofmt=c"), |
| 1150 | 1152 | } else try argv.appendSlice(gpa, &.{ "-I", path_buffer[0 .. (runner.args.lib_dir orelse |
| 1151 | 1153 | return runner.fail("\"--lib=path/to/lib\" arg required with cbe", .{})) |
| ... | ... | @@ -1287,6 +1289,8 @@ const Compiler = struct { |
| 1287 | 1289 | /// Use the LLVM backend. |
| 1288 | 1290 | /// Corresponds to `-fllvm -flld`. |
| 1289 | 1291 | llvm, |
| 1292 | /// Use the LLVM backend without libLLVM. | |
| 1293 | llvmir, | |
| 1290 | 1294 | /// Use the C backend. The output is compiled with `zig cc`. |
| 1291 | 1295 | /// Corresponds to `-ofmt=c`. |
| 1292 | 1296 | cbe, |
| ... | ... | @@ -1325,7 +1329,7 @@ const Compiler = struct { |
| 1325 | 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 | 1333 | fn checkSuccess( |
| 1330 | 1334 | comp: *Compiler, |
| 1331 | 1335 | update: *const Update, |
| ... | ... | @@ -1361,7 +1365,7 @@ const Compiler = struct { |
| 1361 | 1365 | var script_buffer: [512]u8 = undefined; |
| 1362 | 1366 | const script_path, const script_file, var script_fw = script: switch (check) { |
| 1363 | 1367 | .errors => return, |
| 1364 | .stdout, .exit => .{ undefined, null, undefined }, | |
| 1368 | .stdout, .exit, .llvm => .{ undefined, null, undefined }, | |
| 1365 | 1369 | .gdb => { |
| 1366 | 1370 | const script_path = tmp_dir_path ++ std.Io.Dir.path.sep_str ++ "gdb"; |
| 1367 | 1371 | argv.appendSliceAssumeCapacity(&.{ "gdb", "--batch", "--command", script_path, "--args" }); |
| ... | ... | @@ -1425,7 +1429,7 @@ const Compiler = struct { |
| 1425 | 1429 | const out_path = try std.Io.Dir.path.join(arena, &.{ out_dir_path, comp.out_name }); |
| 1426 | 1430 | const bin_path = switch (comp.target.backend.?) { |
| 1427 | 1431 | .sema => return, |
| 1428 | .selfhosted, .llvm => out_path, | |
| 1432 | .selfhosted, .llvm, .llvmir => out_path, | |
| 1429 | 1433 | .cbe => try comp.compileC(update.src_dir, out_path), |
| 1430 | 1434 | }; |
| 1431 | 1435 | |
| ... | ... | @@ -1527,6 +1531,23 @@ const Compiler = struct { |
| 1527 | 1531 | .exe = exe: switch (check) { |
| 1528 | 1532 | .errors => unreachable, |
| 1529 | 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 | 1551 | .gdb, .lldb => { |
| 1531 | 1552 | if (use_executor) { |
| 1532 | 1553 | if (!runner.args.quiet) std.log.warn( |
| ... | ... | @@ -1603,7 +1624,7 @@ const Compiler = struct { |
| 1603 | 1624 | } |
| 1604 | 1625 | switch (result.term) { |
| 1605 | 1626 | .exited => |code| switch (check) { |
| 1606 | .errors => unreachable, | |
| 1627 | .errors, .llvm => unreachable, | |
| 1607 | 1628 | .stdout => { |
| 1608 | 1629 | if (code != 0) return update.fail( |
| 1609 | 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 | 6 | const Step = std.Build.Step; |
| 7 | 7 | |
| 8 | 8 | // Cases |
| 9 | const llvm_ir = @import("llvm_ir.zig"); | |
| 10 | 9 | const libc = @import("libc.zig"); |
| 11 | 10 | const link = @import("link.zig"); |
| 12 | 11 | |
| 13 | 12 | // Implementations |
| 14 | 13 | pub const ErrorTracesContext = @import("src/ErrorTrace.zig"); |
| 15 | 14 | pub const StackTracesContext = @import("src/StackTrace.zig"); |
| 16 | pub const LlvmIrContext = @import("src/LlvmIr.zig"); | |
| 17 | 15 | pub const LibcContext = @import("src/Libc.zig"); |
| 18 | 16 | pub const LinkContext = @import("src/Link.zig"); |
| 19 | 17 | |
| ... | ... | @@ -2196,7 +2194,7 @@ const link_targets = blk: { |
| 2196 | 2194 | const RunnerTarget = struct { |
| 2197 | 2195 | target: std.Target.Query, |
| 2198 | 2196 | cache_mode: enum { whole, incremental }, |
| 2199 | backend: enum { selfhosted, llvm, cbe }, | |
| 2197 | backend: enum { selfhosted, llvm, llvmir, cbe }, | |
| 2200 | 2198 | linker: enum { lld, old, new }, |
| 2201 | 2199 | pic: enum { nopic, pic, pie }, |
| 2202 | 2200 | }; |
| ... | ... | @@ -3175,25 +3173,6 @@ fn isEditorFileName(name: []const u8) bool { |
| 3175 | 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 | 3176 | const libc_test_nsz_targets: []const std.Target.Query = &.{ |
| 3198 | 3177 | .{ |
| 3199 | 3178 | .cpu_arch = .arm, |