| author | |
| committer | |
| log | f78f9388fe79f084d5ea028e6270a410eacfc316 |
| tree | 40b3eeb986f9fdc8866a9d41134b41801a5adc84 |
| parent | 15fe99957226aa27da838812ec891f11b299453d |
Resolves: #224744 files changed, 110 insertions(+), 74 deletions(-)
src/Sema.zig+7-1| ... | @@ -7983,7 +7983,13 @@ fn analyzeCall( | ... | @@ -7983,7 +7983,13 @@ fn analyzeCall( |
| 7983 | } | 7983 | } |
| 7984 | 7984 | ||
| 7985 | if (call_tag == .call_always_tail) { | 7985 | if (call_tag == .call_always_tail) { |
| 7986 | return sema.handleTailCall(block, call_src, sema.typeOf(runtime_func), result); | 7986 | const func_or_ptr_ty = sema.typeOf(runtime_func); |
| 7987 | const runtime_func_ty = switch (func_or_ptr_ty.zigTypeTag(zcu)) { | ||
| 7988 | .@"fn" => func_or_ptr_ty, | ||
| 7989 | .pointer => func_or_ptr_ty.childType(zcu), | ||
| 7990 | else => unreachable, | ||
| 7991 | }; | ||
| 7992 | return sema.handleTailCall(block, call_src, runtime_func_ty, result); | ||
| 7987 | } | 7993 | } |
| 7988 | 7994 | ||
| 7989 | if (resolved_ret_ty.toIntern() == .noreturn_type) { | 7995 | if (resolved_ret_ty.toIntern() == .noreturn_type) { |
test/behavior.zig-1| ... | @@ -17,7 +17,6 @@ test { | ... | @@ -17,7 +17,6 @@ test { |
| 17 | _ = @import("behavior/byteswap.zig"); | 17 | _ = @import("behavior/byteswap.zig"); |
| 18 | _ = @import("behavior/byval_arg_var.zig"); | 18 | _ = @import("behavior/byval_arg_var.zig"); |
| 19 | _ = @import("behavior/call.zig"); | 19 | _ = @import("behavior/call.zig"); |
| 20 | _ = @import("behavior/call_tail.zig"); | ||
| 21 | _ = @import("behavior/cast.zig"); | 20 | _ = @import("behavior/cast.zig"); |
| 22 | _ = @import("behavior/cast_int.zig"); | 21 | _ = @import("behavior/cast_int.zig"); |
| 23 | _ = @import("behavior/comptime_memory.zig"); | 22 | _ = @import("behavior/comptime_memory.zig"); |
test/behavior/call.zig+103| ... | @@ -651,3 +651,106 @@ test "function call with cast to anyopaque pointer" { | ... | @@ -651,3 +651,106 @@ test "function call with cast to anyopaque pointer" { |
| 651 | }; | 651 | }; |
| 652 | Foo.bar(Foo.t); | 652 | Foo.bar(Foo.t); |
| 653 | } | 653 | } |
| 654 | |||
| 655 | test "arguments pointed to on stack into tailcall" { | ||
| 656 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | ||
| 657 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 658 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | ||
| 659 | if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support always tail calls | ||
| 660 | |||
| 661 | switch (builtin.cpu.arch) { | ||
| 662 | .wasm32, | ||
| 663 | .mips, | ||
| 664 | .mipsel, | ||
| 665 | .mips64, | ||
| 666 | .mips64el, | ||
| 667 | .powerpc, | ||
| 668 | .powerpcle, | ||
| 669 | .powerpc64, | ||
| 670 | .powerpc64le, | ||
| 671 | => return error.SkipZigTest, | ||
| 672 | else => {}, | ||
| 673 | } | ||
| 674 | |||
| 675 | const S = struct { | ||
| 676 | var base: usize = undefined; | ||
| 677 | var result_off: [7]usize = undefined; | ||
| 678 | var result_len: [7]usize = undefined; | ||
| 679 | var result_index: usize = 0; | ||
| 680 | |||
| 681 | noinline fn insertionSort(data: []u64) void { | ||
| 682 | result_off[result_index] = @intFromPtr(data.ptr) - base; | ||
| 683 | result_len[result_index] = data.len; | ||
| 684 | result_index += 1; | ||
| 685 | if (data.len > 1) { | ||
| 686 | var least_i: usize = 0; | ||
| 687 | var i: usize = 1; | ||
| 688 | while (i < data.len) : (i += 1) { | ||
| 689 | if (data[i] < data[least_i]) | ||
| 690 | least_i = i; | ||
| 691 | } | ||
| 692 | std.mem.swap(u64, &data[0], &data[least_i]); | ||
| 693 | |||
| 694 | // there used to be a bug where | ||
| 695 | // `data[1..]` is created on the stack | ||
| 696 | // and pointed to by the first argument register | ||
| 697 | // then stack is invalidated by the tailcall and | ||
| 698 | // overwritten by callee | ||
| 699 | // https://github.com/ziglang/zig/issues/9703 | ||
| 700 | return @call(.always_tail, insertionSort, .{data[1..]}); | ||
| 701 | } | ||
| 702 | } | ||
| 703 | }; | ||
| 704 | |||
| 705 | var data = [_]u64{ 1, 6, 2, 7, 1, 9, 3 }; | ||
| 706 | S.base = @intFromPtr(&data); | ||
| 707 | S.insertionSort(data[0..]); | ||
| 708 | try expect(S.result_len[0] == 7); | ||
| 709 | try expect(S.result_len[1] == 6); | ||
| 710 | try expect(S.result_len[2] == 5); | ||
| 711 | try expect(S.result_len[3] == 4); | ||
| 712 | try expect(S.result_len[4] == 3); | ||
| 713 | try expect(S.result_len[5] == 2); | ||
| 714 | try expect(S.result_len[6] == 1); | ||
| 715 | |||
| 716 | try expect(S.result_off[0] == 0); | ||
| 717 | try expect(S.result_off[1] == 8); | ||
| 718 | try expect(S.result_off[2] == 16); | ||
| 719 | try expect(S.result_off[3] == 24); | ||
| 720 | try expect(S.result_off[4] == 32); | ||
| 721 | try expect(S.result_off[5] == 40); | ||
| 722 | try expect(S.result_off[6] == 48); | ||
| 723 | } | ||
| 724 | |||
| 725 | test "tail call function pointer" { | ||
| 726 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 727 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | ||
| 728 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | ||
| 729 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 730 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | ||
| 731 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | ||
| 732 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | ||
| 733 | |||
| 734 | if (builtin.zig_backend == .stage2_llvm) { | ||
| 735 | if (builtin.cpu.arch.isMIPS() or builtin.cpu.arch.isPowerPC() or builtin.cpu.arch.isWasm()) { | ||
| 736 | return error.SkipZigTest; | ||
| 737 | } | ||
| 738 | } | ||
| 739 | |||
| 740 | if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support always tail calls | ||
| 741 | |||
| 742 | const S = struct { | ||
| 743 | fn foo(n: u8) void { | ||
| 744 | if (n == 0) return; | ||
| 745 | const other: *const fn (u8) void = &bar; | ||
| 746 | return @call(.always_tail, other, .{n - 1}); | ||
| 747 | } | ||
| 748 | fn bar(n: u8) void { | ||
| 749 | var other: *const fn (u8) void = undefined; | ||
| 750 | other = &foo; // runtime-known pointer | ||
| 751 | return @call(.always_tail, other, .{n}); | ||
| 752 | } | ||
| 753 | }; | ||
| 754 | |||
| 755 | S.foo(100); | ||
| 756 | } |
test/behavior/call_tail.zig deleted-72| ... | @@ -1,72 +0,0 @@ | ||
| 1 | const builtin = @import("builtin"); | ||
| 2 | const std = @import("std"); | ||
| 3 | const expect = std.testing.expect; | ||
| 4 | |||
| 5 | var base: usize = undefined; | ||
| 6 | var result_off: [7]usize = undefined; | ||
| 7 | var result_len: [7]usize = undefined; | ||
| 8 | var result_index: usize = 0; | ||
| 9 | |||
| 10 | noinline fn insertionSort(data: []u64) void { | ||
| 11 | result_off[result_index] = @intFromPtr(data.ptr) - base; | ||
| 12 | result_len[result_index] = data.len; | ||
| 13 | result_index += 1; | ||
| 14 | if (data.len > 1) { | ||
| 15 | var least_i: usize = 0; | ||
| 16 | var i: usize = 1; | ||
| 17 | while (i < data.len) : (i += 1) { | ||
| 18 | if (data[i] < data[least_i]) | ||
| 19 | least_i = i; | ||
| 20 | } | ||
| 21 | std.mem.swap(u64, &data[0], &data[least_i]); | ||
| 22 | |||
| 23 | // there used to be a bug where | ||
| 24 | // `data[1..]` is created on the stack | ||
| 25 | // and pointed to by the first argument register | ||
| 26 | // then stack is invalidated by the tailcall and | ||
| 27 | // overwritten by callee | ||
| 28 | // https://github.com/ziglang/zig/issues/9703 | ||
| 29 | return @call(.always_tail, insertionSort, .{data[1..]}); | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | test "arguments pointed to on stack into tailcall" { | ||
| 34 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | ||
| 35 | |||
| 36 | switch (builtin.cpu.arch) { | ||
| 37 | .wasm32, | ||
| 38 | .mips, | ||
| 39 | .mipsel, | ||
| 40 | .mips64, | ||
| 41 | .mips64el, | ||
| 42 | .powerpc, | ||
| 43 | .powerpcle, | ||
| 44 | .powerpc64, | ||
| 45 | .powerpc64le, | ||
| 46 | => return error.SkipZigTest, | ||
| 47 | else => {}, | ||
| 48 | } | ||
| 49 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 50 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | ||
| 51 | |||
| 52 | if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support always tail calls | ||
| 53 | |||
| 54 | var data = [_]u64{ 1, 6, 2, 7, 1, 9, 3 }; | ||
| 55 | base = @intFromPtr(&data); | ||
| 56 | insertionSort(data[0..]); | ||
| 57 | try expect(result_len[0] == 7); | ||
| 58 | try expect(result_len[1] == 6); | ||
| 59 | try expect(result_len[2] == 5); | ||
| 60 | try expect(result_len[3] == 4); | ||
| 61 | try expect(result_len[4] == 3); | ||
| 62 | try expect(result_len[5] == 2); | ||
| 63 | try expect(result_len[6] == 1); | ||
| 64 | |||
| 65 | try expect(result_off[0] == 0); | ||
| 66 | try expect(result_off[1] == 8); | ||
| 67 | try expect(result_off[2] == 16); | ||
| 68 | try expect(result_off[3] == 24); | ||
| 69 | try expect(result_off[4] == 32); | ||
| 70 | try expect(result_off[5] == 40); | ||
| 71 | try expect(result_off[6] == 48); | ||
| 72 | } | ||