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 {...@@ -1259,11 +1259,6 @@ pub const Object = struct {
1259 defer if (deinit_wip) wip.deinit();1259 defer if (deinit_wip) wip.deinit();
1260 wip.cursor = .{ .block = try wip.block(0, "Entry") };1260 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
1267 // This is the list of args we will use that correspond directly to the AIR arg1262 // This is the list of args we will use that correspond directly to the AIR arg
1268 // instructions. Depending on the calling convention, this list is not necessarily1263 // instructions. Depending on the calling convention, this list is not necessarily
1269 // a bijection with the actual LLVM parameters of the function.1264 // a bijection with the actual LLVM parameters of the function.
...@@ -2812,7 +2807,11 @@ pub const Object = struct {...@@ -2812,7 +2807,11 @@ pub const Object = struct {
2812 const raw_llvm_ret_ty = try o.lowerType(.fromInterned(fn_info.return_type));2807 const raw_llvm_ret_ty = try o.lowerType(.fromInterned(fn_info.return_type));
2813 try attributes.addParamAttr(it.llvm_index, .{ .sret = raw_llvm_ret_ty }, &o.builder);2808 try attributes.addParamAttr(it.llvm_index, .{ .sret = raw_llvm_ret_ty }, &o.builder);
2814 it.llvm_index += 1;2809 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
2816 const err_return_tracing = fn_info.cc == .auto and zcu.comp.config.any_error_tracing;2815 const err_return_tracing = fn_info.cc == .auto and zcu.comp.config.any_error_tracing;
2817 if (err_return_tracing) {2816 if (err_return_tracing) {
2818 try attributes.addParamAttr(it.llvm_index, .nonnull, &o.builder);2817 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...@@ -611,14 +611,20 @@ fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
611 .no_suspend, .always_inline, .compile_time => unreachable,611 .no_suspend, .always_inline, .compile_time => unreachable,
612 }612 }
613613
614 const ret_ptr = if (!sret) null else blk: {614 const ret_ptr = if (sret) ret_ptr: {
615 const llvm_ret_ty = try o.lowerType(return_type);615 const llvm_ret_ty = try o.lowerType(return_type);
616 try attributes.addParamAttr(0, .{ .sret = llvm_ret_ty }, &o.builder);616 try attributes.addParamAttr(0, .{ .sret = llvm_ret_ty }, &o.builder);
617617
618 const alignment = return_type.abiAlignment(zcu).toLlvm();618 const alignment = return_type.abiAlignment(zcu).toLlvm();
619 const ret_ptr = try self.buildAlloca(llvm_ret_ty, alignment);619 const ret_ptr = try self.buildAlloca(llvm_ret_ty, alignment);
620 try llvm_args.append(ret_ptr);620 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;
622 };628 };
623629
624 const err_return_tracing = fn_info.cc == .auto and zcu.comp.config.any_error_tracing;630 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" {...@@ -731,3 +731,33 @@ test "tail call function pointer" {
731731
732 S.foo(100);732 S.foo(100);
733}733}
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}