From 7b0c25c84d0b2c138849e427ed8d7cb4da365adf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 26 Jul 2023 17:21:33 -0700 Subject: [PATCH] add behavior test for tail calls closes #9703 --- test/behavior.zig | 1 + test/behavior/call_tail.zig | 51 +++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 test/behavior/call_tail.zig diff --git a/test/behavior.zig b/test/behavior.zig index 6ec59fa22df7..249d3191e65d 100644 --- a/test/behavior.zig +++ b/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"); diff --git a/test/behavior/call_tail.zig b/test/behavior/call_tail.zig new file mode 100644 index 000000000000..69bdaf864615 --- /dev/null +++ b/test/behavior/call_tail.zig @@ -0,0 +1,51 @@ +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" { + 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); +}