authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-04-02 03:13:07-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-04-02 14:31:12+02:00
log28ae5d4158b084d17e55e73aec4c37e22885bd78
treeac2e5760c328e300f471d1faa919fa403718db55
parent4059242f44e28dc2c8f9837c2033060974fd253f

llvm: fix missing return attributes

Closes #31636

3 files changed, 43 insertions(+), 8 deletions(-)

src/codegen/llvm.zig+5-6
......@@ -1259,11 +1259,6 @@ pub const Object = struct {
12591259 defer if (deinit_wip) wip.deinit();
12601260 wip.cursor = .{ .block = try wip.block(0, "Entry") };
12611261
1262 if (ccAbiPromoteInt(fn_info.cc, zcu, Type.fromInterned(fn_info.return_type))) |s| switch (s) {
1263 .signed => try attributes.addRetAttr(.signext, &o.builder),
1264 .unsigned => try attributes.addRetAttr(.zeroext, &o.builder),
1265 };
1266
12671262 // This is the list of args we will use that correspond directly to the AIR arg
12681263 // instructions. Depending on the calling convention, this list is not necessarily
12691264 // a bijection with the actual LLVM parameters of the function.
......@@ -2812,7 +2807,11 @@ pub const Object = struct {
28122807 const raw_llvm_ret_ty = try o.lowerType(.fromInterned(fn_info.return_type));
28132808 try attributes.addParamAttr(it.llvm_index, .{ .sret = raw_llvm_ret_ty }, &o.builder);
28142809 it.llvm_index += 1;
2815 }
2810 } else if (ccAbiPromoteInt(fn_info.cc, zcu, Type.fromInterned(fn_info.return_type))) |s| switch (s) {
2811 .signed => try attributes.addRetAttr(.signext, &o.builder),
2812 .unsigned => try attributes.addRetAttr(.zeroext, &o.builder),
2813 };
2814
28162815 const err_return_tracing = fn_info.cc == .auto and zcu.comp.config.any_error_tracing;
28172816 if (err_return_tracing) {
28182817 try attributes.addParamAttr(it.llvm_index, .nonnull, &o.builder);
src/codegen/llvm/FuncGen.zig+8-2
......@@ -611,14 +611,20 @@ fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
611611 .no_suspend, .always_inline, .compile_time => unreachable,
612612 }
613613
614 const ret_ptr = if (!sret) null else blk: {
614 const ret_ptr = if (sret) ret_ptr: {
615615 const llvm_ret_ty = try o.lowerType(return_type);
616616 try attributes.addParamAttr(0, .{ .sret = llvm_ret_ty }, &o.builder);
617617
618618 const alignment = return_type.abiAlignment(zcu).toLlvm();
619619 const ret_ptr = try self.buildAlloca(llvm_ret_ty, alignment);
620620 try llvm_args.append(ret_ptr);
621 break :blk ret_ptr;
621 break :ret_ptr ret_ptr;
622 } else ret_ptr: {
623 if (ccAbiPromoteInt(fn_info.cc, zcu, Type.fromInterned(fn_info.return_type))) |s| switch (s) {
624 .signed => try attributes.addRetAttr(.signext, &o.builder),
625 .unsigned => try attributes.addRetAttr(.zeroext, &o.builder),
626 };
627 break :ret_ptr null;
622628 };
623629
624630 const err_return_tracing = fn_info.cc == .auto and zcu.comp.config.any_error_tracing;
test/behavior/call.zig+30
......@@ -731,3 +731,33 @@ test "tail call function pointer" {
731731
732732 S.foo(100);
733733}
734
735test "tail call with potentially extended types" {
736 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
737 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
738
739 if (builtin.zig_backend == .stage2_llvm) {
740 if (builtin.cpu.arch.isMIPS() or builtin.cpu.arch.isPowerPC() or builtin.cpu.arch.isWasm()) {
741 return error.SkipZigTest;
742 }
743 }
744
745 if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support always tail calls
746
747 const S = struct {
748 fn Test(comptime Return: type) type {
749 return struct {
750 fn callee(@"u8": u8, @"i8": i8, @"u16": u16, @"i16": i16) callconv(.c) Return {
751 return @intCast(@as(i32, @"u8") + @as(i32, @"i8") + @as(i32, @"u16") + @as(i32, @"i16"));
752 }
753 fn caller(@"u8": u8, @"i8": i8, @"u16": u16, @"i16": i16) callconv(.c) Return {
754 return @call(.always_tail, callee, .{ @"u8", @"i8", @"u16", @"i16" });
755 }
756 };
757 }
758 };
759 try std.testing.expect(S.Test(u8).caller(1, -2, 3, 4) == 6);
760 try std.testing.expect(S.Test(i8).caller(5, -6, 7, -8) == -2);
761 try std.testing.expect(S.Test(u16).caller(9, 10, 11, 12) == 42);
762 try std.testing.expect(S.Test(i16).caller(13, 14, 15, -16) == 26);
763}