FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Fix mono handling of SwiftError by vcsjones · Pull Request #131634 · dotnet/runtime · GitHub

Repository navigation

Fix mono handling of SwiftError - #131634

Merged
vcsjones merged 3 commits into
dotnet:mainfrom
vcsjones:mono-swift-error-fix
Aug 4, 2026
Merged

vcsjones merged 3 commits into
dotnet:mainfrom
vcsjones:mono-swift-error-fix

Conversation

vcsjones commented Jul 31, 2026 •
edited
Loading

Copy link
Copy Markdown
Member

There appear to be two problems with the Swift calling convention implementation in Mono for how it handles SwiftError.

On x64, structs can spill to the stack while a later scalar argument still has an available register. Consequently, SwiftError can be register passed while carrying a nonzero accumulated stack offset. Mono mistakes that offset for a stack location and reads an invalid pointer.

On ARM64, the first argument placed on the stack naturally has offset zero. Mono mistakes that stack location for a register passed argument and uses the wrong value. offset == 0  is ambiguous: it can mean either "passed in a register" or "passed in the first stack slot." Right now it doesn't look like it can tell the difference.

The fix records whether SwiftError was originally passed in a register before replacing its argument classification with ArgSwiftError, rather than inferring its location from the stack offset. On x64, stack-passed SwiftError arguments also disable frame-pointer omission so their incoming stack slots remain addressable.

The tests that have been added would fail without the fixes, and I confirmed this locally on an x64 macOS 26 machine.

This was originally found in #131630, where changes at the p/invoke boundary caused the SwiftError to be identified as register passed but carried a nonzero accumulated stack offset because preceding two word buffer structs had spilled.

This is a follow up to #103043, where it appears the fix was incomplete.

vcsjones self-assigned this Jul 31, 2026
Copilot AI review requested due to automatic review settings July 31, 2026 10:06
vcsjones added NO-REVIEW Experimental/testing PR, do NOT review it area-Codegen-JIT-mono labels Jul 31, 2026

Copy link
Copy Markdown
Member Author

/azp run runtime-extra-platforms

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @steveisok, @vitek-karas
See info in area-owners.md if you want to be subscribed.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Pull request overview

This PR expands the existing Swift interop test asset to add additional ABI/calling-convention stress cases around SwiftError placement, and wires them up from C# via Swift-callconv P/Invokes with new xUnit tests to exercise those scenarios on Apple platforms.

Changes:

  • Add a new Swift exported throwing function with argument shaping intended to place SwiftError in the first stack slot scenario.
  • Add a new Swift exported throwing function that takes multiple 2-word struct-like arguments (Swift UnsafeBufferPointer) plus a 1-word scalar argument to stress mixed register/stack argument passing.
  • Add corresponding C# interop declarations and new xUnit tests validating both “no throw” behavior and ABI correctness via a shared hash computation.
Show a summary per file
File Description
src/tests/Interop/Swift/SwiftErrorHandling/SwiftErrorHandling.swift Adds new exported Swift entry points and hashing helpers to exercise ABI argument placement while still returning a deterministic value.
src/tests/Interop/Swift/SwiftErrorHandling/SwiftErrorHandling.cs Adds marshaling types, new Swift-callconv P/Invokes, and new xUnit tests to validate the ABI scenarios end-to-end.

Copilot's findings

  • Files reviewed: 2/2 changed files
  • Comments generated: 2

Copilot AI review requested due to automatic review settings July 31, 2026 12:02

Copy link
Copy Markdown
Member Author

/azp run runtime-extra-platforms

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Copilot's findings

Suppressed comments (2)

src/tests/Interop/Swift/SwiftErrorHandling/SwiftErrorHandling.cs:173

  • This new test only covers the non-throwing path. To better stress/validate SwiftError placement and propagation, it should also exercise the throwing path (matching the existing pattern of having both thrown and not-thrown coverage for the other entrypoints). Converting this to a [Theory] over willThrow keeps the file compact while covering both behaviors.
    [Fact]
    public static unsafe void TestSwiftErrorInFirstStackSlotNotThrown()
    {
        SwiftError error = new SwiftError();

        int i = 0;
        int result = (int)conditionallyThrowErrorInFirstStackSlot(0, i + 1, i + 2, i + 3, i + 4, i + 5, i + 6, i + 7, ref error);

        Assert.True(error.Value == null, "No Swift error was expected to be thrown.");
        Assert.True(result == 42, "The result from Swift does not match the expected value.");
    }

src/tests/Interop/Swift/SwiftErrorHandling/SwiftErrorHandling.cs:178

  • BufferPointer instances are created with count values up to 66, but the backing stackalloc buffer is only 6 bytes. Even though the current Swift implementation only hashes the pointer value and count, UnsafeBufferPointer semantically represents a range of count elements starting at baseAddress, so passing an out-of-bounds range risks UB/traps if the Swift side changes (or if the optimizer/runtime assumes the range is valid). Allocate enough bytes so all (baseAddress,count) pairs describe valid ranges.
        byte* values = stackalloc byte[6];
  • Files reviewed: 2/2 changed files
  • Comments generated: 0 new

Copilot AI review requested due to automatic review settings July 31, 2026 20:16
vcsjones changed the title Add tests to stress Mono's Swift calling conventions Fix mono handling of SwiftError Jul 31, 2026
vcsjones removed the NO-REVIEW Experimental/testing PR, do NOT review it label Jul 31, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Copilot's findings

Suppressed comments (3)

src/tests/Interop/Swift/SwiftErrorHandling/SwiftErrorHandling.cs:177

  • This new scenario is only validated for the non-throwing path. Since the purpose of the test is to stress SwiftError handling/calling convention edge cases, it should also cover the throwing path (non-null SwiftError + message retrieval) to ensure the error propagation logic is exercised.
    [Fact]
    public static unsafe void TestSwiftErrorInFirstStackSlotNotThrown()
    {
        SwiftError error = new SwiftError();

        int i = 0;
        int result = (int)conditionallyThrowErrorInFirstStackSlot(0, i + 1, i + 2, i + 3, i + 4, i + 5, i + 6, i + 7, ref error);

        Assert.True(error.Value == null, "No Swift error was expected to be thrown.");
        Assert.True(result == 42, "The result from Swift does not match the expected value.");
    }

src/tests/Interop/Swift/SwiftErrorHandling/SwiftErrorHandling.cs:210

  • The test constructs Swift-style buffer pointers whose Count values exceed the actual stackalloced buffer size (e.g. values + 5 with count 66). Even though the current Swift implementation doesn’t dereference the buffers, this violates the contract of UnsafeBufferPointer and can become undefined behavior if the Swift side changes (or if the optimizer introduces unexpected accesses). Allocate enough stack space to cover the largest (base + count) range used by the test data.
        byte* values = stackalloc byte[6];

src/mono/mono/mini/mini-amd64.c:1134

  • The PR description frames this as adding stress tests to reproduce Mono Swift calling-convention issues, but this diff also changes Mono’s argument classification/stack allocation behavior for SwiftError on amd64 (and similarly on arm64). If the intent is tests-only, these runtime changes should be moved to a follow-up PR; otherwise, please update the title/description to reflect that this PR includes a functional fix as well.
				if (sig->pinvoke) {
					ainfo->reg = GINT32_TO_UINT8 (AMD64_R12);
					ainfo->swift_error_in_reg = TRUE;
				} else {
					add_general (&gr, &stack_size, ainfo);
					ainfo->swift_error_in_reg = ainfo->storage == ArgInIReg;
				}
				ainfo->storage = ArgSwiftError;
  • Files reviewed: 6/6 changed files
  • Comments generated: 0 new

vcsjones marked this pull request as ready for review July 31, 2026 20:25

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 4 pipeline(s).
12 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

Copy link
Copy Markdown
Member

@BrzVlad @matouskozak can one you please review?

matouskozak left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Overall looking good to me, just one question around the new ArgInfo field.

I've run it locally on arm64 macOS and without this change I'm getting NullReferenceException and passes with this change.

fyi: @dalexsoto since you're now working on this domain

vcsjones merged commit ac85c68 into dotnet:main Aug 4, 2026
133 checks passed
vcsjones deleted the mono-swift-error-fix branch August 4, 2026 15:42
dotnet-milestone-bot Bot added this to the 11.0-rc1 milestone Aug 5, 2026
github-actions Bot locked and limited conversation to collaborators Sep 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants


Back | FazBrowse Home | New Git URL