From 28ae5d4158b084d17e55e73aec4c37e22885bd78 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 2 Apr 2026 03:13:07 -0400 Subject: [PATCH] llvm: fix missing return attributes Closes #31636 --- src/codegen/llvm.zig | 11 +++++------ src/codegen/llvm/FuncGen.zig | 10 ++++++++-- test/behavior/call.zig | 30 ++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index bd3be4f96f..1ba3b272da 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1259,11 +1259,6 @@ pub const Object = struct { defer if (deinit_wip) wip.deinit(); wip.cursor = .{ .block = try wip.block(0, "Entry") }; - if (ccAbiPromoteInt(fn_info.cc, zcu, Type.fromInterned(fn_info.return_type))) |s| switch (s) { - .signed => try attributes.addRetAttr(.signext, &o.builder), - .unsigned => try attributes.addRetAttr(.zeroext, &o.builder), - }; - // This is the list of args we will use that correspond directly to the AIR arg // instructions. Depending on the calling convention, this list is not necessarily // a bijection with the actual LLVM parameters of the function. @@ -2812,7 +2807,11 @@ pub const Object = struct { const raw_llvm_ret_ty = try o.lowerType(.fromInterned(fn_info.return_type)); try attributes.addParamAttr(it.llvm_index, .{ .sret = raw_llvm_ret_ty }, &o.builder); it.llvm_index += 1; - } + } else if (ccAbiPromoteInt(fn_info.cc, zcu, Type.fromInterned(fn_info.return_type))) |s| switch (s) { + .signed => try attributes.addRetAttr(.signext, &o.builder), + .unsigned => try attributes.addRetAttr(.zeroext, &o.builder), + }; + const err_return_tracing = fn_info.cc == .auto and zcu.comp.config.any_error_tracing; if (err_return_tracing) { try attributes.addParamAttr(it.llvm_index, .nonnull, &o.builder); diff --git a/src/codegen/llvm/FuncGen.zig b/src/codegen/llvm/FuncGen.zig index 0c7e794c78..685cd06d89 100644 --- a/src/codegen/llvm/FuncGen.zig +++ b/src/codegen/llvm/FuncGen.zig @@ -611,14 +611,20 @@ fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif .no_suspend, .always_inline, .compile_time => unreachable, } - const ret_ptr = if (!sret) null else blk: { + const ret_ptr = if (sret) ret_ptr: { const llvm_ret_ty = try o.lowerType(return_type); try attributes.addParamAttr(0, .{ .sret = llvm_ret_ty }, &o.builder); const alignment = return_type.abiAlignment(zcu).toLlvm(); const ret_ptr = try self.buildAlloca(llvm_ret_ty, alignment); try llvm_args.append(ret_ptr); - break :blk ret_ptr; + break :ret_ptr ret_ptr; + } else ret_ptr: { + if (ccAbiPromoteInt(fn_info.cc, zcu, Type.fromInterned(fn_info.return_type))) |s| switch (s) { + .signed => try attributes.addRetAttr(.signext, &o.builder), + .unsigned => try attributes.addRetAttr(.zeroext, &o.builder), + }; + break :ret_ptr null; }; const err_return_tracing = fn_info.cc == .auto and zcu.comp.config.any_error_tracing; diff --git a/test/behavior/call.zig b/test/behavior/call.zig index fd13449943..1cad4c3164 100644 --- a/test/behavior/call.zig +++ b/test/behavior/call.zig @@ -731,3 +731,33 @@ test "tail call function pointer" { S.foo(100); } + +test "tail call with potentially extended types" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + if (builtin.zig_backend == .stage2_llvm) { + if (builtin.cpu.arch.isMIPS() or builtin.cpu.arch.isPowerPC() or builtin.cpu.arch.isWasm()) { + return error.SkipZigTest; + } + } + + if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support always tail calls + + const S = struct { + fn Test(comptime Return: type) type { + return struct { + fn callee(@"u8": u8, @"i8": i8, @"u16": u16, @"i16": i16) callconv(.c) Return { + return @intCast(@as(i32, @"u8") + @as(i32, @"i8") + @as(i32, @"u16") + @as(i32, @"i16")); + } + fn caller(@"u8": u8, @"i8": i8, @"u16": u16, @"i16": i16) callconv(.c) Return { + return @call(.always_tail, callee, .{ @"u8", @"i8", @"u16", @"i16" }); + } + }; + } + }; + try std.testing.expect(S.Test(u8).caller(1, -2, 3, 4) == 6); + try std.testing.expect(S.Test(i8).caller(5, -6, 7, -8) == -2); + try std.testing.expect(S.Test(u16).caller(9, 10, 11, 12) == 42); + try std.testing.expect(S.Test(i16).caller(13, 14, 15, -16) == 26); +}