Commit 87961237cf

Andrew Kelley <andrew@ziglang.org>
2023-07-27 02:21:33
add behavior test for tail calls
closes #9703
1 parent 00d6a4d
Changed files (2)
test/behavior/call_tail.zig
@@ -0,0 +1,59 @@
+const builtin = @import("builtin");
+const std = @import("std");
+const expect = std.testing.expect;
+
+var base: usize = undefined;
+var result_off: [7]usize = undefined;
+var result_len: [7]usize = undefined;
+var result_index: usize = 0;
+
+noinline fn insertionSort(data: []u64) void {
+    result_off[result_index] = @intFromPtr(data.ptr) - base;
+    result_len[result_index] = data.len;
+    result_index += 1;
+    if (data.len > 1) {
+        var least_i: usize = 0;
+        var i: usize = 1;
+        while (i < data.len) : (i += 1) {
+            if (data[i] < data[least_i])
+                least_i = i;
+        }
+        std.mem.swap(u64, &data[0], &data[least_i]);
+
+        // there used to be a bug where
+        // `data[1..]` is created on the stack
+        // and pointed to by the first argument register
+        // then stack is invalidated by the tailcall and
+        // overwritten by callee
+        // https://github.com/ziglang/zig/issues/9703
+        return @call(.always_tail, insertionSort, .{data[1..]});
+    }
+}
+
+test "arguments pointed to on stack into tailcall" {
+    switch (builtin.cpu.arch) {
+        .wasm32, .mips, .mipsel, .powerpc, .powerpcle, .powerpc64le => return error.SkipZigTest,
+        else => {},
+    }
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
+    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
+
+    var data = [_]u64{ 1, 6, 2, 7, 1, 9, 3 };
+    base = @intFromPtr(&data);
+    insertionSort(data[0..]);
+    try expect(result_len[0] == 7);
+    try expect(result_len[1] == 6);
+    try expect(result_len[2] == 5);
+    try expect(result_len[3] == 4);
+    try expect(result_len[4] == 3);
+    try expect(result_len[5] == 2);
+    try expect(result_len[6] == 1);
+
+    try expect(result_off[0] == 0);
+    try expect(result_off[1] == 8);
+    try expect(result_off[2] == 16);
+    try expect(result_off[3] == 24);
+    try expect(result_off[4] == 32);
+    try expect(result_off[5] == 40);
+    try expect(result_off[6] == 48);
+}
test/behavior.zig
@@ -149,6 +149,7 @@ test {
     _ = @import("behavior/byval_arg_var.zig");
     _ = @import("behavior/c_char_signedness.zig");
     _ = @import("behavior/call.zig");
+    _ = @import("behavior/call_tail.zig");
     _ = @import("behavior/cast.zig");
     _ = @import("behavior/cast_int.zig");
     _ = @import("behavior/comptime_memory.zig");