Skip to content

[cdac] HFA / HVA support: enable ARGITER + zero-known-issues on Windows ARM64 + Linux ARM/ARM64 - #130090

Merged
max-charlamb merged 8 commits into
dotnet:mainfrom
max-charlamb:cdac-argiter-more-platforms
Jul 2, 2026
Merged

[cdac] HFA / HVA support: enable ARGITER + zero-known-issues on Windows ARM64 + Linux ARM/ARM64#130090
max-charlamb merged 8 commits into
dotnet:mainfrom
max-charlamb:cdac-argiter-more-platforms

Conversation

@max-charlamb

Copy link
Copy Markdown
Member

Fills in the HFA / HVA classification paths in the cDAC's shared ArgIterator port so ICallingConvention.TryComputeArgGCRefMapBlob produces correct output on FEATURE_HFA targets, then lights up Windows ARM64, Linux ARM, and Linux ARM64 for both the ARGITER stress sub-check and the zero-known-issues GCREFS assertion.

Follow-up to #129769 and #129858. Advances the platform matrix tracked in #130008.

What lights up in CI

PlatformARGITER stressGCREFS 0-known-issues
Windows x64✅ pre-existing✅ pre-existing
Windows x86✅ pre-existing✅ pre-existing
Windows ARM64newnew
Linux ARMnewnew
Linux ARM64newnew
Linux / macOS x64⏭ still deferred (SystemV-AMD64 eightbyte classifier)⏭ tolerated
RISC-V, LoongArch, WASM⏭ still deferred⏭ tolerated

The Helix stress matrix in runtime-diagnostics.yml already covers all six platforms; this PR is purely lifting the xunit-level and contract-level gates.

Contract addition

Single new API on IRuntimeTypeSystem:

boolTryGetHFAElementSize(TypeHandletypeHandle,outintelementSize);

Returns 4 / 8 / 16 for HFA / HVA types, false otherwise. Self-gates on target architecture (FEATURE_HFA = ARM/ARM64) so callers don't need to. Mirrors MethodTable::GetHFAType (src/coreclr/vm/class.cpp:1867):

  • At each recursion level, first checks GetVectorHFAElementSize for an intrinsic Vector shape (Vector64<T> / Vector128<T> / System.Numerics.Vector<T>)
  • Then walks the first non-static instance field. R4 -> 4, R8 -> 8, ValueType -> recurse via the existing GetFieldDescApproxTypeHandle
  • HVA detection uses enum_flag_IsIntrinsicType (WFLAGS2_ENUM.IsIntrinsicType = 0x0020) plus TypeDef name+namespace match, with T-must-be-numerical verification (CorIsNumericalType: I1..R8 || I || U)

MethodTable flag WFLAGS_LOW.IsHFA = 0x00000800 is added to MethodTableFlags_1.cs. That bit is repurposed on UNIX_AMD64_ABI as enum_flag_IsRegStructPassed, hence the arch self-gate baked into the API.

CdacTypeHandle (thin adapter)

publicboolIsHomogeneousAggregate()=>!_typeHandle.IsNull&&Rts.TryGetHFAElementSize(_typeHandle,out_);publicintGetHomogeneousAggregateElementSize(){if(Arch==RuntimeInfoArchitecture.Arm)returnRequiresAlign8()?8:4;// ARM has no HVA; shortcut avoids the field walkreturn_typeHandle.IsNull?0:(Rts.TryGetHFAElementSize(_typeHandle,outintsize)?size:0);}

The ARM shortcut avoids a field walk: on ARM there's no HVA, and RequiresAlign8 mirrors CheckForHFA's element-type-driven alignment choice.

Test coverage additions

CallSignatures debuggee gains an HfaCategory (~100 LOC) exercising:

  • Scalar HFAs: Float2/3/4, Double2/3/4 (all legal HFA arities)
  • Nested HFAs: NestedFloat3, NestedDouble4 (first-field recursion)
  • Mixed args: HfaThenRef / RefThenHfa / TwoFloatHfas — FP-reg + int-reg interaction; validates GCRefMap tokens for trailing ref args after FP-reg consumption
  • HFA returns: ReturnFloat3, ReturnDouble2 (FP-register return path, no HasRetBufArg)
  • HVA args: Vector64<float>, Vector128<float>, System.Numerics.Vector<float> (ARM64-only classification; benign on other targets)
  • Negative shapes: Float5 (>4 fields), MixedR4R8 (mixed FP) — runtime correctly declines to flag as HFA, cDAC matches

Same debuggee compiles/passes everywhere; only exercises the new HFA walker on ARM/ARM64.

Gate expansions

Three gates now all match: Windows {x86, x64, ARM64} + Linux {ARM, ARM64}

  1. CdacStressTests.ArgIterStress_AllVerificationsPass: admits the new ARM/ARM64 platforms.
  2. CdacStressTestBase.AssertAllPassed.requiresZeroKnownIssues: extended from Windows x86/x64 to include the same set. Any deferred Frame on these platforms is now a hard failure.
  3. GcScanner.PromoteCallerStack.supportedByCallingConvention: lifted to match, so on ARM/ARM64 caller-stack refs are enumerated via TryComputeArgGCRefMapBlob instead of falling through to RecordDeferredFrame.

Local validation

  • dotnet build src/native/managed/cdac/cdac.slnx -c Release -- clean
  • dotnet test .../UnitTests/Microsoft.Diagnostics.DataContractReader.Tests.csproj -- 2611 pass / 0 fail / 16 skipped
  • No local ARM hardware; the ARM/ARM64 paths are validated by CI. If any HFA/HVA shape produces a divergence, the ARGITER assertion will surface the exact MethodDesc + slot mismatch.

Known gaps (follow-ups, tracked in #130008)

  • Linux / macOS x64: SystemV-AMD64 eightbyte struct classifier not ported (CdacTypeHandle.GetSystemVAmd64PassStructInRegisterDescriptor throws)
  • RISC-V64 / LoongArch64: FP struct classifier not ported (GetFpStructInRegistersInfo throws)
  • WASM32: GetFieldAlignment not ported

Note

This change was authored with assistance from GitHub Copilot.

…RM64
Fills in the CdacTypeHandle.IsHomogeneousAggregate() and
GetHomogeneousAggregateElementSize() paths so the shared ArgIterator
port produces correct GCRefMap blobs on FEATURE_HFA targets (ARM, ARM64),
then wires the ARGITER stress sub-check and the zero-known-issues GCREFS
assertion on Windows ARM64 + Linux ARM/ARM64.
## Contract additions (IRuntimeTypeSystem)
Single new API:
bool TryGetHFAElementSize(TypeHandle typeHandle, out int elementSize);
Returns 4 / 8 / 16 for HFA / HVA types, false otherwise. Self-gates on
target arch (FEATURE_HFA = ARM/ARM64) so callers don't need to. Mirrors
MethodTable::GetHFAType (src/coreclr/vm/class.cpp:1867):
- At each recursion level, first checks GetVectorHFAElementSize for an
intrinsic Vector shape (Vector64<T>/Vector128<T>/System.Numerics.Vector<T>).
- Then walks the first non-static instance field. R4 -> 4, R8 -> 8,
ValueType -> recurse via existing GetFieldDescApproxTypeHandle.
- HVA detection uses enum_flag_IsIntrinsicType (WFLAGS2_ENUM.IsIntrinsicType
= 0x0020) plus TypeDef name+namespace match, with T-must-be-numerical
verification (CorIsNumericalType: I1..R8 || I || U).
The MethodTable flag WFLAGS_LOW.IsHFA = 0x00000800 is added to
MethodTableFlags_1.cs. That bit is repurposed on UNIX_AMD64_ABI as
enum_flag_IsRegStructPassed, hence the arch self-gate in the API.
## CdacTypeHandle (thin adapter)
IsHomogeneousAggregate() -> Rts.TryGetHFAElementSize(_, out _)
GetHomogeneousAggregateElementSize() -> Arm shortcut: RequiresAlign8()?8:4
Else: Rts.TryGetHFAElementSize
(returns size)
The ARM shortcut avoids a field walk: on ARM there is no HVA, and
RequiresAlign8 mirrors CheckForHFA's element-type-driven alignment choice.
## Test coverage
CallSignatures debuggee gains an HfaCategory (~100 LOC) exercising:
- Scalar HFAs: Float2/3/4, Double2/3/4 (all legal HFA arities)
- Nested HFAs: NestedFloat3, NestedDouble4 (first-field recursion)
- Mixed args: HfaThenRef / RefThenHfa / TwoFloatHfas (FP-reg + int-reg
interaction; validates GCRefMap tokens for trailing ref args after
FP-reg consumption)
- HFA returns: ReturnFloat3, ReturnDouble2 (FP-register return path,
no HasRetBufArg)
- HVA args: Vector64<float>, Vector128<float>, System.Numerics.Vector<float>
(ARM64-only classification; benign on other targets)
- Negative shapes: Float5 (>4 fields), MixedR4R8 (mixed FP) -- runtime
correctly declines to flag as HFA, cDAC matches
Same test compiles/passes everywhere; only exercises the new HFA walker
on ARM/ARM64.
## Test-gate expansion
Both gates now match: Windows {x86, x64, ARM64} + Linux {ARM, ARM64}
- CdacStressTests.ArgIterStress_AllVerificationsPass: admits the new
ARM/ARM64 platforms.
- CdacStressTestBase.AssertAllPassed.requiresZeroKnownIssues: extended
from Windows x86/x64 to include the same set. Any deferred Frame on
these platforms is now a hard failure.
- GcScanner.PromoteCallerStack: supportedByCallingConvention gate lifted
to match, so on ARM/ARM64 the caller-stack refs are enumerated via
ICallingConvention.TryComputeArgGCRefMapBlob instead of falling
through to RecordDeferredFrame.
Still deferred: Linux/macOS x64 (SystemV-AMD64 eightbyte classifier),
RISC-V/LoongArch64 (FP struct classifier), WASM32 (GetFieldAlignment).
Tracked under dotnet#130008.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

CopilotAI 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.

Pull request overview

Adds FEATURE_HFA (ARM/ARM64) support to the cDAC RuntimeTypeSystem / calling-convention pipeline so ArgIterator-derived GCRefMap synthesis can correctly classify HFA/HVA structs, then expands the cdacstress ARGITER + GCREFS “0 known issues” enforcement gates to Windows ARM64 and Linux ARM/ARM64. Also extends the CallSignatures debuggee to exercise the new HFA/HVA paths.

Changes:

  • Introduces IRuntimeTypeSystem.TryGetHFAElementSize and implements an HFA/HVA element-size walker in RuntimeTypeSystem_1 (plus new MethodTable flag bits to support it).
  • Updates cDAC stack-walk GC scanning and xUnit stress-test gating to treat Windows ARM64 + Linux ARM/ARM64 as supported for calling-convention-based caller-stack scanning and strict KnownIssues==0 enforcement.
  • Adds HFA/HVA argument/return scenarios to the CallSignatures stress debuggee.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.

Show a summary per file
FileDescription
src/native/managed/cdac/tests/StressTests/Debuggees/CallSignatures/Program.csAdds an HFA/HVA category (float/double HFAs + intrinsic vector HVAs + negative shapes) to exercise the new classifier paths.
src/native/managed/cdac/tests/StressTests/CdacStressTests.csExpands the ARGITER stress-test platform gate to include Windows ARM64 and Linux ARM/ARM64.
src/native/managed/cdac/tests/StressTests/CdacStressTestBase.csExpands the strict “KnownIssues must be zero” assertion to include Windows ARM64 and Linux ARM/ARM64.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/RuntimeTypeSystemHelpers/MethodTableFlags_1.csAdds MethodTable flag definitions/properties for HFA and intrinsic-type detection used by the new walker.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/GC/GcScanner.csExpands calling-convention-based PromoteCallerStack gating to include Windows ARM64 and Unix ARM/ARM64.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.csImplements FEATURE_HFA gating + HFA/HVA element-size discovery (including vector shape matching and numeric-arg validation).
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/CallingConvention/CdacTypeHandle.csWires IsHomogeneousAggregate / element-size queries to the new RTS API (with an ARM32 shortcut).
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.csAdds the new TryGetHFAElementSize method to the contract interface.
docs/design/datacontracts/RuntimeTypeSystem.mdDocuments the new contract API and associated MethodTable flag bits.

Comment threadsrc/native/managed/cdac/tests/StressTests/CdacStressTests.cs Outdated
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag
See info in area-owners.md if you want to be subscribed.

- GetVectorHFAElementSize: defensively wrap the metadata decode in
try/catch and validate HandleKind before casting the token, mirroring
GetFieldDescApproxTypeHandle. A bad TypeDef token or malformed metadata
now returns 0 (treated as "not an HVA") instead of throwing during
GC-scan / stack-walking.
- CdacStressTests ArgIter skip message: mention the Windows ARM32 ABI
port gap alongside the SystemV / RISC-V / LoongArch / WASM classifiers,
since the gate now also skips ARM32.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The full 4/8/16 element-size breakdown belonged in the impl-section`ndoc, not on every declaration. Interface comment now just says`n'HFA (or HVA on ARM64), returns 4/8/16 element size'; RTS.md`nimpl section grows a pseudocode block covering the recursive`nfield-walk + GetVectorHFAElementSize intrinsic name match.`n`nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 1, 2026 20:18
…dTableFlags_1.cs
Algorithm now lives in RuntimeTypeSystem.md pseudocode; the impl`nfiles just cross-reference the runtime source. Removed re-explanations`nof:`n- FEATURE_HFA arch-gate rationale (now a 1-line note)`n- TryGetHFAElementSize algorithm walkthrough (see RTS.md)`n- Duplicate GetVectorHFA comment block (accidental)`n- Redundant IsHFA bit note (already in the enum value line)`n- CdacTypeHandle wrapper explanations (self-evident from Rts. calls)`n`nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

CopilotAI 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.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

CopilotAI review requested due to automatic review settings July 1, 2026 20:24

CopilotAI 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.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Max Charlamband others added 2 commits July 1, 2026 18:17
…nvariant
Mirrors the Debug.Assert(IsHomogeneousAggregate()) check in the shared
ReadyToRun TypeHandle implementation. Catches a class of call-site
mistakes on the ARM shortcut path where a non-HFA type would otherwise
silently receive a bogus 4/8 result from `RequiresAlign8() ? 8 : 4`.
Addresses Copilot PR review feedback on dotnet#130090.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CdacTypeHandle.GetHomogeneousAggregateElementSize now unconditionally
delegates to Rts.TryGetHFAElementSize, becoming a pure adapter.
The RequiresAlign8-based ARM shortcut (which avoids the field walk by
reading the alignment flag that CheckForHFA sets based on the resolved
R4/R8 element type) is now baked into TryGetHFAElementSize itself, so
any future consumer of the RTS API automatically benefits without
having to know the trick.
IsFeatureHfaTarget now returns the resolved architecture via `out` so
the shortcut branch can dispatch without re-querying RuntimeInfo.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 1, 2026 22:31
Max Charlamband others added 2 commits July 1, 2026 18:32
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

CopilotAI 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.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.

CopilotAI review requested due to automatic review settings July 1, 2026 22:38

CopilotAI 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.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.

@max-charlamb

Copy link
Copy Markdown
MemberAuthor

/ba-g OSX infra timeout

@max-charlamb
max-charlamb merged commit 4f6a0ae into dotnet:mainJul 2, 2026
76 of 78 checks passed
@max-charlamb
max-charlamb deleted the cdac-argiter-more-platforms branch July 2, 2026 14:13
@dotnet-milestone-botdotnet-milestone-botBot added this to the 11.0-preview7 milestone Jul 3, 2026
max-charlamb pushed a commit to max-charlamb/runtime that referenced this pull request Jul 6, 2026
…zero-known-issues on Linux x64
Fills in the SystemV-AMD64 struct-in-register classification path in
the cDAC's ArgIterator port so `ICallingConvention.TryComputeArgGCRefMapBlob`
produces correct output on Unix x64. Lights up **Linux x64** for both
the ARGITER stress sub-check and the zero-known-issues GCREFS assertion.
The classifier itself is target-OS-and-arch aware (`Unix or Apple` +
`X64`), so macOS x64 works too if run locally, but we do not add
`osx_x64` to the Helix stress matrix in this PR -- macOS x64 Helix
machines are comparatively slow and the marginal signal (same SystemV
algorithm across Linux and macOS) doesn't justify the extra CI wall
time.
Follow-up to dotnet#130090. Advances the platform matrix tracked in dotnet#130008.
## Gates now flipped to an inverse list
All three coupled gates (`ArgIterStress_AllVerificationsPass` xunit
gate, `AssertAllPassed.requiresZeroKnownIssues`, and
`GcScanner.PromoteCallerStack.platformUnsupported`) now enumerate only
what is **not** yet supported by the cDAC ArgIterator port rather than
maintaining a growing allow-list. As of this PR the only remaining
unsupported architectures are:
- `RiscV64` (FP struct classifier not ported)
- `LoongArch64` (FP struct classifier not ported)
- `Wasm` (`GetFieldAlignment` not ported)
Everything else -- Windows/Linux/macOS on x86/x64/ARM/ARM64 -- runs the
full stress. (Windows ARM32 isn't a CoreCLR target and can't be run
even if it slipped through the gate.)
## What lights up in CI
| Platform | ARGITER | GCREFS zero-known-issues |
|---|---|---|
| Windows x86/x64/ARM64 | pre-existing (dotnet#130090) | pre-existing (dotnet#130090) |
| Linux ARM/ARM64 | pre-existing (dotnet#130090) | pre-existing (dotnet#130090) |
| Linux x64 | **new** | **new** |
| macOS x64 | supported by classifier; not in Helix matrix | same |
| RISC-V, LoongArch, WASM | skipped (still deferred) | skipped |
## Classifier design
New file: `Contracts/CallingConvention/SystemVStructClassifier.cs` (~430
LOC). Static helper class, called by
`CdacTypeHandle.GetSystemVAmd64PassStructInRegisterDescriptor`.
Structured so that:
1. The JitInterface descriptor type
`SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR` never leaks
into the cDAC abstractions layer. `SystemVAmd64PassingDescriptor.cs`
is file-linked only into the Contracts assembly, not into
Abstractions.
2. The classifier is NOT on `IRuntimeTypeSystem`. SysV struct
classification is a calling-convention concern, not a type-system
query. `CdacTypeHandle` calls the static classifier directly.
3. The only new API on the contract is a general-purpose flag
accessor `bool IsIntrinsicType(TypeHandle)`, matching the existing
`IsByRefLike` / `RequiresAlign8` / `ContainsGCPointers` pattern.
Algorithm mirrors `MethodTable::ClassifyEightBytes` in
`src/coreclr/vm/methodtable.cpp` with the same shape as the crossgen2
port in `SystemVStructClassificator.cs`:
- Self-gates on target OS+arch: returns false unless Unix x64
(`Unix or Apple`).
- Rejects value types > 16 bytes, intrinsic SIMD Vector wrappers
(`Vector64/128/256/512`, `System.Numerics.Vector<T>`), and
`Int128`/`UInt128` (all handled specially by the JIT).
- Recursive per-field walker with natural-alignment enforcement, union
merging via `ReClassifyField`, and nested value-type recursion via
the existing `GetFieldDescApproxTypeHandle`.
- Second pass (`AssignClassifiedEightByteTypes`) walks the struct
byte-by-byte from `0..StructSize` and assigns unique-offset fields
(plus their padding) to eightbytes with the SysV merge rules
(`Integer+IntegerReference -> IntegerReference`, `SSE+anything -> Integer`,
etc).
- Also folds in the previously-private intrinsic-Vector-name detector
into a reusable `TryGetIntrinsicVectorKind` helper shared between the
HFA/HVA path (in `RuntimeTypeSystem_1`) and the SysV classifier's
rejection filter.
## HFA recursion cap bump
`TryGetHFAElementSize` recursion cap goes from 16 to 128 as an aside.
16 was arbitrary and conservative; 128 is comfortable headroom while
still bounded well below any plausible nested-value-type depth (real
HFAs top out at 4 elements per level).
## Test coverage
`CallSignatures` debuggee gains a `SysVCategory` (~80 LOC) exercising:
- Single-eightbyte shapes: `IntPair` (Integer), `FloatPair` (SSE),
`IntFloat` (merged Integer), `SingleRef` (IntegerReference)
- Two-eightbyte shapes: `LongLong`, `LongDouble`, `DoubleDouble`, `RefInt`
- Nested value type
- ByRefLike (`Span<byte>`: IntegerByRef + Integer)
- Rejection cases: 24-byte 3-ref struct (>16 bytes), `Vector128<int>`
(intrinsic), `Int128Wrapper`, empty struct
- Sub-eightbyte trailer / single-field wrapper
Same debuggee runs on all platforms; only exercises the new classifier
on Unix x64 targets.
## Local validation
- `dotnet build src/native/managed/cdac/cdac.slnx -c Release`: clean.
- `dotnet test .../UnitTests/Microsoft.Diagnostics.DataContractReader.Tests.csproj`:
2630 pass / 0 fail / 16 skipped.
- Windows x64 local stress smoke (`RunStressTests.ps1`):
* GCREFS `BasicAlloc`: 5024 / 5024 pass, 0 known-issue.
* ARGITER `CallSignatures` (incl. new `SysVCategory`): 379 pass /
0 fail / 0 skip / 0 error.
* GCREFS `CallSignatures`: 6134 / 6134 pass, 0 known-issue.
- The SysV code self-gates on Windows; no regression on any
pre-existing platform. CI will validate `linux-x64` end-to-end via
the byte-for-byte ARGITER comparison against runtime
`ComputeCallRefMap`.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit to max-charlamb/runtime that referenced this pull request Jul 6, 2026
…zero-known-issues on Linux x64
Fills in the SystemV-AMD64 struct-in-register classification path in
the cDAC's ArgIterator port so `ICallingConvention.TryComputeArgGCRefMapBlob`
produces correct output on Unix x64. Lights up **Linux x64** for both
the ARGITER stress sub-check and the zero-known-issues GCREFS assertion.
The classifier itself is target-OS-and-arch aware (`Unix or Apple` +
`X64`), so macOS x64 works too if run locally, but we do not add
`osx_x64` to the Helix stress matrix in this PR -- macOS x64 Helix
machines are comparatively slow and the marginal signal (same SystemV
algorithm across Linux and macOS) doesn't justify the extra CI wall
time.
Follow-up to dotnet#130090. Advances the platform matrix tracked in dotnet#130008.
## Gates now flipped to an inverse list
All three coupled gates (`ArgIterStress_AllVerificationsPass` xunit
gate, `AssertAllPassed.requiresZeroKnownIssues`, and
`GcScanner.PromoteCallerStack.platformUnsupported`) now enumerate only
what is **not** yet supported by the cDAC ArgIterator port rather than
maintaining a growing allow-list. As of this PR the only remaining
unsupported architectures are:
- `RiscV64` (FP struct classifier not ported)
- `LoongArch64` (FP struct classifier not ported)
- `Wasm` (`GetFieldAlignment` not ported)
Everything else -- Windows/Linux/macOS on x86/x64/ARM/ARM64 -- runs the
full stress. (Windows ARM32 isn't a CoreCLR target and can't be run
even if it slipped through the gate.)
## What lights up in CI
| Platform | ARGITER | GCREFS zero-known-issues |
|---|---|---|
| Windows x86/x64/ARM64 | pre-existing (dotnet#130090) | pre-existing (dotnet#130090) |
| Linux ARM/ARM64 | pre-existing (dotnet#130090) | pre-existing (dotnet#130090) |
| Linux x64 | **new** | **new** |
| macOS x64 | supported by classifier; not in Helix matrix | same |
| RISC-V, LoongArch, WASM | skipped (still deferred) | skipped |
## Classifier design
New file: `Contracts/CallingConvention/SystemVStructClassifier.cs` (~430
LOC). Static helper class, called by
`CdacTypeHandle.GetSystemVAmd64PassStructInRegisterDescriptor`.
Structured so that:
1. The JitInterface descriptor type
`SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR` never leaks
into the cDAC abstractions layer. `SystemVAmd64PassingDescriptor.cs`
is file-linked only into the Contracts assembly, not into
Abstractions.
2. The classifier is NOT on `IRuntimeTypeSystem`. SysV struct
classification is a calling-convention concern, not a type-system
query. `CdacTypeHandle` calls the static classifier directly.
3. The only new API on the contract is a general-purpose flag
accessor `bool IsIntrinsicType(TypeHandle)`, matching the existing
`IsByRefLike` / `RequiresAlign8` / `ContainsGCPointers` pattern.
Algorithm mirrors `MethodTable::ClassifyEightBytes` in
`src/coreclr/vm/methodtable.cpp` with the same shape as the crossgen2
port in `SystemVStructClassificator.cs`:
- Self-gates on target OS+arch: returns false unless Unix x64
(`Unix or Apple`).
- Rejects value types > 16 bytes, intrinsic SIMD Vector wrappers
(`Vector64/128/256/512`, `System.Numerics.Vector<T>`), and
`Int128`/`UInt128` (all handled specially by the JIT).
- Recursive per-field walker with natural-alignment enforcement, union
merging via `ReClassifyField`, and nested value-type recursion via
the existing `GetFieldDescApproxTypeHandle`.
- Second pass (`AssignClassifiedEightByteTypes`) walks the struct
byte-by-byte from `0..StructSize` and assigns unique-offset fields
(plus their padding) to eightbytes with the SysV merge rules
(`Integer+IntegerReference -> IntegerReference`, `SSE+anything -> Integer`,
etc).
- Also folds in the previously-private intrinsic-Vector-name detector
into a reusable `TryGetIntrinsicVectorKind` helper shared between the
HFA/HVA path (in `RuntimeTypeSystem_1`) and the SysV classifier's
rejection filter.
## HFA recursion cap bump
`TryGetHFAElementSize` recursion cap goes from 16 to 128 as an aside.
16 was arbitrary and conservative; 128 is comfortable headroom while
still bounded well below any plausible nested-value-type depth (real
HFAs top out at 4 elements per level).
## Test coverage
`CallSignatures` debuggee gains a `SysVCategory` (~80 LOC) exercising:
- Single-eightbyte shapes: `IntPair` (Integer), `FloatPair` (SSE),
`IntFloat` (merged Integer), `SingleRef` (IntegerReference)
- Two-eightbyte shapes: `LongLong`, `LongDouble`, `DoubleDouble`, `RefInt`
- Nested value type
- ByRefLike (`Span<byte>`: IntegerByRef + Integer)
- Rejection cases: 24-byte 3-ref struct (>16 bytes), `Vector128<int>`
(intrinsic), `Int128Wrapper`, empty struct
- Sub-eightbyte trailer / single-field wrapper
Same debuggee runs on all platforms; only exercises the new classifier
on Unix x64 targets.
## Local validation
- `dotnet build src/native/managed/cdac/cdac.slnx -c Release`: clean.
- `dotnet test .../UnitTests/Microsoft.Diagnostics.DataContractReader.Tests.csproj`:
2630 pass / 0 fail / 16 skipped.
- Windows x64 local stress smoke (`RunStressTests.ps1`):
* GCREFS `BasicAlloc`: 5024 / 5024 pass, 0 known-issue.
* ARGITER `CallSignatures` (incl. new `SysVCategory`): 379 pass /
0 fail / 0 skip / 0 error.
* GCREFS `CallSignatures`: 6134 / 6134 pass, 0 known-issue.
- The SysV code self-gates on Windows; no regression on any
pre-existing platform. CI will validate `linux-x64` end-to-end via
the byte-for-byte ARGITER comparison against runtime
`ComputeCallRefMap`.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit to max-charlamb/runtime that referenced this pull request Jul 6, 2026
…zero-known-issues on Linux x64
Fills in the SystemV-AMD64 struct-in-register classification path in
the cDAC's ArgIterator port so `ICallingConvention.TryComputeArgGCRefMapBlob`
produces correct output on Unix x64. Lights up **Linux x64** for both
the ARGITER stress sub-check and the zero-known-issues GCREFS assertion.
The classifier itself is target-OS-and-arch aware (`Unix or Apple` +
`X64`), so macOS x64 works too if run locally, but we do not add
`osx_x64` to the Helix stress matrix in this PR -- macOS x64 Helix
machines are comparatively slow and the marginal signal (same SystemV
algorithm across Linux and macOS) doesn't justify the extra CI wall
time.
Follow-up to dotnet#130090. Advances the platform matrix tracked in dotnet#130008.
## Gates now flipped to an inverse list
All three coupled gates (`ArgIterStress_AllVerificationsPass` xunit
gate, `AssertAllPassed.requiresZeroKnownIssues`, and
`GcScanner.PromoteCallerStack.platformUnsupported`) now enumerate only
what is **not** yet supported by the cDAC ArgIterator port rather than
maintaining a growing allow-list. As of this PR the only remaining
unsupported architectures are:
- `RiscV64` (FP struct classifier not ported)
- `LoongArch64` (FP struct classifier not ported)
- `Wasm` (`GetFieldAlignment` not ported)
Everything else -- Windows/Linux/macOS on x86/x64/ARM/ARM64 -- runs the
full stress. (Windows ARM32 isn't a CoreCLR target and can't be run
even if it slipped through the gate.)
## What lights up in CI
| Platform | ARGITER | GCREFS zero-known-issues |
|---|---|---|
| Windows x86/x64/ARM64 | pre-existing (dotnet#130090) | pre-existing (dotnet#130090) |
| Linux ARM/ARM64 | pre-existing (dotnet#130090) | pre-existing (dotnet#130090) |
| Linux x64 | **new** | **new** |
| macOS x64 | supported by classifier; not in Helix matrix | same |
| RISC-V, LoongArch, WASM | skipped (still deferred) | skipped |
## Classifier design
New file: `Contracts/CallingConvention/SystemVStructClassifier.cs` (~430
LOC). Static helper class, called by
`CdacTypeHandle.GetSystemVAmd64PassStructInRegisterDescriptor`.
Structured so that:
1. The JitInterface descriptor type
`SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR` never leaks
into the cDAC abstractions layer. `SystemVAmd64PassingDescriptor.cs`
is file-linked only into the Contracts assembly, not into
Abstractions.
2. The classifier is NOT on `IRuntimeTypeSystem`. SysV struct
classification is a calling-convention concern, not a type-system
query. `CdacTypeHandle` calls the static classifier directly.
3. The only new API on the contract is a general-purpose flag
accessor `bool IsIntrinsicType(TypeHandle)`, matching the existing
`IsByRefLike` / `RequiresAlign8` / `ContainsGCPointers` pattern.
Algorithm mirrors `MethodTable::ClassifyEightBytes` in
`src/coreclr/vm/methodtable.cpp` with the same shape as the crossgen2
port in `SystemVStructClassificator.cs`:
- Self-gates on target OS+arch: returns false unless Unix x64
(`Unix or Apple`).
- Rejects value types > 16 bytes, intrinsic SIMD Vector wrappers
(`Vector64/128/256/512`, `System.Numerics.Vector<T>`), and
`Int128`/`UInt128` (all handled specially by the JIT).
- Recursive per-field walker with natural-alignment enforcement, union
merging via `ReClassifyField`, and nested value-type recursion via
the existing `GetFieldDescApproxTypeHandle`.
- Second pass (`AssignClassifiedEightByteTypes`) walks the struct
byte-by-byte from `0..StructSize` and assigns unique-offset fields
(plus their padding) to eightbytes with the SysV merge rules
(`Integer+IntegerReference -> IntegerReference`, `SSE+anything -> Integer`,
etc).
- Also folds in the previously-private intrinsic-Vector-name detector
into a reusable `TryGetIntrinsicVectorKind` helper shared between the
HFA/HVA path (in `RuntimeTypeSystem_1`) and the SysV classifier's
rejection filter.
## HFA recursion cap bump
`TryGetHFAElementSize` recursion cap goes from 16 to 128 as an aside.
16 was arbitrary and conservative; 128 is comfortable headroom while
still bounded well below any plausible nested-value-type depth (real
HFAs top out at 4 elements per level).
## Test coverage
`CallSignatures` debuggee gains a `SysVCategory` (~80 LOC) exercising:
- Single-eightbyte shapes: `IntPair` (Integer), `FloatPair` (SSE),
`IntFloat` (merged Integer), `SingleRef` (IntegerReference)
- Two-eightbyte shapes: `LongLong`, `LongDouble`, `DoubleDouble`, `RefInt`
- Nested value type
- ByRefLike (`Span<byte>`: IntegerByRef + Integer)
- Rejection cases: 24-byte 3-ref struct (>16 bytes), `Vector128<int>`
(intrinsic), `Int128Wrapper`, empty struct
- Sub-eightbyte trailer / single-field wrapper
Same debuggee runs on all platforms; only exercises the new classifier
on Unix x64 targets.
## Local validation
- `dotnet build src/native/managed/cdac/cdac.slnx -c Release`: clean.
- `dotnet test .../UnitTests/Microsoft.Diagnostics.DataContractReader.Tests.csproj`:
2630 pass / 0 fail / 16 skipped.
- Windows x64 local stress smoke (`RunStressTests.ps1`):
* GCREFS `BasicAlloc`: 5024 / 5024 pass, 0 known-issue.
* ARGITER `CallSignatures` (incl. new `SysVCategory`): 379 pass /
0 fail / 0 skip / 0 error.
* GCREFS `CallSignatures`: 6134 / 6134 pass, 0 known-issue.
- The SysV code self-gates on Windows; no regression on any
pre-existing platform. CI will validate `linux-x64` end-to-end via
the byte-for-byte ARGITER comparison against runtime
`ComputeCallRefMap`.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit to max-charlamb/runtime that referenced this pull request Jul 6, 2026
…zero-known-issues on Linux x64
Fills in the SystemV-AMD64 struct-in-register classification path in
the cDAC's ArgIterator port so `ICallingConvention.TryComputeArgGCRefMapBlob`
produces correct output on Unix x64. Lights up **Linux x64** for both
the ARGITER stress sub-check and the zero-known-issues GCREFS assertion.
The classifier itself is target-OS-and-arch aware (`Unix or Apple` +
`X64`), so macOS x64 works too if run locally, but we do not add
`osx_x64` to the Helix stress matrix in this PR -- macOS x64 Helix
machines are comparatively slow and the marginal signal (same SystemV
algorithm across Linux and macOS) doesn't justify the extra CI wall
time.
Follow-up to dotnet#130090. Advances the platform matrix tracked in dotnet#130008.
## Gates now flipped to an inverse list
All three coupled gates (`ArgIterStress_AllVerificationsPass` xunit
gate, `AssertAllPassed.requiresZeroKnownIssues`, and
`GcScanner.PromoteCallerStack.platformUnsupported`) now enumerate only
what is **not** yet supported by the cDAC ArgIterator port rather than
maintaining a growing allow-list. As of this PR the only remaining
unsupported architectures are:
- `RiscV64` (FP struct classifier not ported)
- `LoongArch64` (FP struct classifier not ported)
- `Wasm` (`GetFieldAlignment` not ported)
Everything else -- Windows/Linux/macOS on x86/x64/ARM/ARM64 -- runs the
full stress. (Windows ARM32 isn't a CoreCLR target and can't be run
even if it slipped through the gate.)
## What lights up in CI
| Platform | ARGITER | GCREFS zero-known-issues |
|---|---|---|
| Windows x86/x64/ARM64 | pre-existing (dotnet#130090) | pre-existing (dotnet#130090) |
| Linux ARM/ARM64 | pre-existing (dotnet#130090) | pre-existing (dotnet#130090) |
| Linux x64 | **new** | **new** |
| macOS x64 | supported by classifier; not in Helix matrix | same |
| RISC-V, LoongArch, WASM | skipped (still deferred) | skipped |
## Classifier design
New file: `Contracts/CallingConvention/SystemVStructClassifier.cs` (~430
LOC). Static helper class, called by
`CdacTypeHandle.GetSystemVAmd64PassStructInRegisterDescriptor`.
Structured so that:
1. The JitInterface descriptor type
`SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR` never leaks
into the cDAC abstractions layer. `SystemVAmd64PassingDescriptor.cs`
is file-linked only into the Contracts assembly, not into
Abstractions.
2. The classifier is NOT on `IRuntimeTypeSystem`. SysV struct
classification is a calling-convention concern, not a type-system
query. `CdacTypeHandle` calls the static classifier directly.
3. The only new API on the contract is a general-purpose flag
accessor `bool IsIntrinsicType(TypeHandle)`, matching the existing
`IsByRefLike` / `RequiresAlign8` / `ContainsGCPointers` pattern.
Algorithm mirrors `MethodTable::ClassifyEightBytes` in
`src/coreclr/vm/methodtable.cpp` with the same shape as the crossgen2
port in `SystemVStructClassificator.cs`:
- Self-gates on target OS+arch: returns false unless Unix x64
(`Unix or Apple`).
- Rejects value types > 16 bytes, intrinsic SIMD Vector wrappers
(`Vector64/128/256/512`, `System.Numerics.Vector<T>`), and
`Int128`/`UInt128` (all handled specially by the JIT).
- Recursive per-field walker with natural-alignment enforcement, union
merging via `ReClassifyField`, and nested value-type recursion via
the existing `GetFieldDescApproxTypeHandle`.
- Second pass (`AssignClassifiedEightByteTypes`) walks the struct
byte-by-byte from `0..StructSize` and assigns unique-offset fields
(plus their padding) to eightbytes with the SysV merge rules
(`Integer+IntegerReference -> IntegerReference`, `SSE+anything -> Integer`,
etc).
- Also folds in the previously-private intrinsic-Vector-name detector
into a reusable `TryGetIntrinsicVectorKind` helper shared between the
HFA/HVA path (in `RuntimeTypeSystem_1`) and the SysV classifier's
rejection filter.
## HFA recursion cap bump
`TryGetHFAElementSize` recursion cap goes from 16 to 128 as an aside.
16 was arbitrary and conservative; 128 is comfortable headroom while
still bounded well below any plausible nested-value-type depth (real
HFAs top out at 4 elements per level).
## Test coverage
`CallSignatures` debuggee gains a `SysVCategory` (~80 LOC) exercising:
- Single-eightbyte shapes: `IntPair` (Integer), `FloatPair` (SSE),
`IntFloat` (merged Integer), `SingleRef` (IntegerReference)
- Two-eightbyte shapes: `LongLong`, `LongDouble`, `DoubleDouble`, `RefInt`
- Nested value type
- ByRefLike (`Span<byte>`: IntegerByRef + Integer)
- Rejection cases: 24-byte 3-ref struct (>16 bytes), `Vector128<int>`
(intrinsic), `Int128Wrapper`, empty struct
- Sub-eightbyte trailer / single-field wrapper
Same debuggee runs on all platforms; only exercises the new classifier
on Unix x64 targets.
## Local validation
- `dotnet build src/native/managed/cdac/cdac.slnx -c Release`: clean.
- `dotnet test .../UnitTests/Microsoft.Diagnostics.DataContractReader.Tests.csproj`:
2630 pass / 0 fail / 16 skipped.
- Windows x64 local stress smoke (`RunStressTests.ps1`):
* GCREFS `BasicAlloc`: 5024 / 5024 pass, 0 known-issue.
* ARGITER `CallSignatures` (incl. new `SysVCategory`): 379 pass /
0 fail / 0 skip / 0 error.
* GCREFS `CallSignatures`: 6134 / 6134 pass, 0 known-issue.
- The SysV code self-gates on Windows; no regression on any
pre-existing platform. CI will validate `linux-x64` end-to-end via
the byte-for-byte ARGITER comparison against runtime
`ComputeCallRefMap`.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit to max-charlamb/runtime that referenced this pull request Jul 6, 2026
…zero-known-issues on Linux x64
Fills in the SystemV-AMD64 struct-in-register classification path in
the cDAC's ArgIterator port so `ICallingConvention.TryComputeArgGCRefMapBlob`
produces correct output on Unix x64. Lights up **Linux x64** for both
the ARGITER stress sub-check and the zero-known-issues GCREFS assertion.
The classifier itself is target-OS-and-arch aware (`Unix or Apple` +
`X64`), so macOS x64 works too if run locally, but we do not add
`osx_x64` to the Helix stress matrix in this PR -- macOS x64 Helix
machines are comparatively slow and the marginal signal (same SystemV
algorithm across Linux and macOS) doesn't justify the extra CI wall
time.
Follow-up to dotnet#130090. Advances the platform matrix tracked in dotnet#130008.
## Gates now flipped to an inverse list
All three coupled gates (`ArgIterStress_AllVerificationsPass` xunit
gate, `AssertAllPassed.requiresZeroKnownIssues`, and
`GcScanner.PromoteCallerStack.platformUnsupported`) now enumerate only
what is **not** yet supported by the cDAC ArgIterator port rather than
maintaining a growing allow-list. As of this PR the only remaining
unsupported architectures are:
- `RiscV64` (FP struct classifier not ported)
- `LoongArch64` (FP struct classifier not ported)
- `Wasm` (`GetFieldAlignment` not ported)
Everything else -- Windows/Linux/macOS on x86/x64/ARM/ARM64 -- runs the
full stress. (Windows ARM32 isn't a CoreCLR target and can't be run
even if it slipped through the gate.)
## What lights up in CI
| Platform | ARGITER | GCREFS zero-known-issues |
|---|---|---|
| Windows x86/x64/ARM64 | pre-existing (dotnet#130090) | pre-existing (dotnet#130090) |
| Linux ARM/ARM64 | pre-existing (dotnet#130090) | pre-existing (dotnet#130090) |
| Linux x64 | **new** | **new** |
| macOS x64 | supported by classifier; not in Helix matrix | same |
| RISC-V, LoongArch, WASM | skipped (still deferred) | skipped |
## Classifier design
New file: `Contracts/CallingConvention/SystemVStructClassifier.cs` (~430
LOC). Static helper class, called by
`CdacTypeHandle.GetSystemVAmd64PassStructInRegisterDescriptor`.
Structured so that:
1. The JitInterface descriptor type
`SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR` never leaks
into the cDAC abstractions layer. `SystemVAmd64PassingDescriptor.cs`
is file-linked only into the Contracts assembly, not into
Abstractions.
2. The classifier is NOT on `IRuntimeTypeSystem`. SysV struct
classification is a calling-convention concern, not a type-system
query. `CdacTypeHandle` calls the static classifier directly.
3. The only new API on the contract is a general-purpose flag
accessor `bool IsIntrinsicType(TypeHandle)`, matching the existing
`IsByRefLike` / `RequiresAlign8` / `ContainsGCPointers` pattern.
Algorithm mirrors `MethodTable::ClassifyEightBytes` in
`src/coreclr/vm/methodtable.cpp` with the same shape as the crossgen2
port in `SystemVStructClassificator.cs`:
- Self-gates on target OS+arch: returns false unless Unix x64
(`Unix or Apple`).
- Rejects value types > 16 bytes, intrinsic SIMD Vector wrappers
(`Vector64/128/256/512`, `System.Numerics.Vector<T>`), and
`Int128`/`UInt128` (all handled specially by the JIT).
- Recursive per-field walker with natural-alignment enforcement, union
merging via `ReClassifyField`, and nested value-type recursion via
the existing `GetFieldDescApproxTypeHandle`.
- Second pass (`AssignClassifiedEightByteTypes`) walks the struct
byte-by-byte from `0..StructSize` and assigns unique-offset fields
(plus their padding) to eightbytes with the SysV merge rules
(`Integer+IntegerReference -> IntegerReference`, `SSE+anything -> Integer`,
etc).
- Also folds in the previously-private intrinsic-Vector-name detector
into a reusable `TryGetIntrinsicVectorKind` helper shared between the
HFA/HVA path (in `RuntimeTypeSystem_1`) and the SysV classifier's
rejection filter.
## HFA recursion cap bump
`TryGetHFAElementSize` recursion cap goes from 16 to 128 as an aside.
16 was arbitrary and conservative; 128 is comfortable headroom while
still bounded well below any plausible nested-value-type depth (real
HFAs top out at 4 elements per level).
## Test coverage
`CallSignatures` debuggee gains a `SysVCategory` (~80 LOC) exercising:
- Single-eightbyte shapes: `IntPair` (Integer), `FloatPair` (SSE),
`IntFloat` (merged Integer), `SingleRef` (IntegerReference)
- Two-eightbyte shapes: `LongLong`, `LongDouble`, `DoubleDouble`, `RefInt`
- Nested value type
- ByRefLike (`Span<byte>`: IntegerByRef + Integer)
- Rejection cases: 24-byte 3-ref struct (>16 bytes), `Vector128<int>`
(intrinsic), `Int128Wrapper`, empty struct
- Sub-eightbyte trailer / single-field wrapper
Same debuggee runs on all platforms; only exercises the new classifier
on Unix x64 targets.
## Local validation
- `dotnet build src/native/managed/cdac/cdac.slnx -c Release`: clean.
- `dotnet test .../UnitTests/Microsoft.Diagnostics.DataContractReader.Tests.csproj`:
2630 pass / 0 fail / 16 skipped.
- Windows x64 local stress smoke (`RunStressTests.ps1`):
* GCREFS `BasicAlloc`: 5024 / 5024 pass, 0 known-issue.
* ARGITER `CallSignatures` (incl. new `SysVCategory`): 379 pass /
0 fail / 0 skip / 0 error.
* GCREFS `CallSignatures`: 6134 / 6134 pass, 0 known-issue.
- The SysV code self-gates on Windows; no regression on any
pre-existing platform. CI will validate `linux-x64` end-to-end via
the byte-for-byte ARGITER comparison against runtime
`ComputeCallRefMap`.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb pushed a commit to max-charlamb/runtime that referenced this pull request Jul 6, 2026
The referenced work (SystemV-AMD64 struct classifier for linux/macOS
x64, ARM64 struct-in-register classification, ARM32 ABI port) has
landed via dotnet#130090 and the SysV classifier in this PR. The WindowsOnly
skip stays -- it gates the PInvoke and VarArgs debuggees that only run
on Windows for unrelated reasons (Win32 API use / JIT __arglist gate).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
max-charlamb added a commit that referenced this pull request Jul 8, 2026
…zero-known-issues on Linux x64 (#130257)
Fills in the SystemV-AMD64 struct-in-register classification path in the
cDAC's ArgIterator port so
`ICallingConvention.TryComputeArgGCRefMapBlob` produces correct output
on Unix x64. Lights up **Linux x64** for both the ARGITER stress
sub-check and the zero-known-issues GCREFS assertion.
Follow-up to #130090. Advances the platform matrix tracked in #130008.
## Approach
The runtime already pre-computes the eightbyte classification for every
enregisterable managed value type at `MethodTable` construction time and
caches it in `EEClass::m_eightByteRegistersInfo` (inside
`EEClassOptionalFields`). Runtime callers -- `ArgIterator`,
`getSystemVAmd64PassStructInRegisterDescriptor` -- read that cached info
via `SystemVRegDescriptorFromSystemVEightByteRegistersInfo` rather than
re-running `ClassifyEightBytes`. The cDAC now does the same.
Non-enregisterable structs (too large, or classified as
`SSEUP`/`X87`/`Memory`) never reach `StoreEightByteClassification`, so
their descriptor reads back with `NumEightBytes == 0`. That's the
natural `passedInRegisters = false` signal the cDAC surfaces. Native
P/Invoke layouts are not cached here (the runtime classifies them on
demand) -- the cDAC never walks native transition frames for GC
scanning, so this is fine.
Because the stress harness compares against `ComputeCallRefMap` -- which
uses the runtime path -- reading the same cached info gives
byte-for-byte parity by construction. No merge-rule /
intrinsic-rejection / `Int128` divergence to reason about.
macOS x64 uses the same SystemV path and works the same way if run
locally, but we don't add `osx_x64` to the Helix stress matrix in this
PR.
## Descriptor plumbing
**C++:**
- `class.h`: `cdac_data<EEClass>::OptionalFields`;
`cdac_data<EEClassOptionalFields>` (UNIX_AMD64_ABI-guarded) exposing the
`EightByteRegistersInfo` offset. Friend declaration on
`EEClassOptionalFields`.
- `methodtable.h`: `cdac_data<SystemVEightByteRegistersInfo>` for
`NumEightBytes` plus the two `byte[2]` eightbyte tables. Friend struct.
- `datadescriptor.inc`: `EEClass.OptionalFields` field + new
`EEClassOptionalFields` and `SystemVEightByteRegistersInfo` types, both
under `#ifdef UNIX_AMD64_ABI`.
**Managed:**
- `DataType`: new `EEClassOptionalFields` and
`SystemVEightByteRegistersInfo` entries.
- `Data.EEClass`: adds `OptionalFields`.
- `Data.EEClassOptionalFields`: `[Field] SystemVEightByteRegistersInfo`
inline sub-IData.
- `Data.SystemVEightByteRegistersInfo`: `[Field] NumEightBytes`,
`OnInit`-populated arrays.
- `CdacTypeHandle.GetSystemVAmd64PassStructInRegisterDescriptor`: ~40
LOC inline. Reads `EEClass -> OptionalFields -> EightByteRegistersInfo`,
then projects into `SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR`
with `eightByteOffsets` synthesized as `i * 8` (matches
`SystemVRegDescriptorFromSystemVEightByteRegistersInfo`).
## Test coverage
`CallSignatures` debuggee gains a `SysVCategory` exercising:
- Single-eightbyte shapes: `IntPair` (Integer), `FloatPair` (SSE),
`IntFloat` (merged Integer), `SingleRef` (IntegerReference)
- Two-eightbyte shapes: `LongLong`, `LongDouble`, `DoubleDouble`,
`RefInt`
- Nested value type, ByRefLike (`Span<byte>`)
- Rejection / stack-passed: 24-byte 3-ref struct, `Vector128<int>`,
empty struct
- `Int128Wrapper` (2 Integer eightbytes -- register-passed; runtime does
NOT reject by name)
- Unions (`LayoutKind.Explicit`), sub-eightbyte trailer, single-field
wrapper
Same debuggee runs everywhere; only exercises the new descriptor read on
Unix x64 (Windows targets don't emit the descriptor entries, so the
classifier returns `passedInRegisters = false` immediately via a
`TryGetTypeInfo` probe).
`AssertAllPassed` now folds `KnownIssues` into the `Failed` check
unconditionally -- no more platform gate on the assertion side; any
surviving known-issue count is a regression.
## Local validation
- `dotnet build src/native/managed/cdac/cdac.slnx -c Release`: clean
- Unit tests: 2646 pass / 0 fail / 16 skipped
- **Windows x64 local stress smoke** (`RunStressTests.ps1`):
- GCREFS `CallSignatures`: 6150 / 6150 pass, 0 known-issue
- ARGITER `CallSignatures` (with the `SysVCategory`): 383 pass / 0 fail
/ 0 skip / 0 error
The descriptor entries are `UNIX_AMD64_ABI`-only, so the descriptor-read
path only activates on Linux/macOS x64 targets; no regression on any
pre-existing platform. CI validates `linux-x64` end-to-end via the
byte-for-byte ARGITER comparison against runtime `ComputeCallRefMap`.
> [!NOTE]
> This change was authored with assistance from GitHub Copilot.
---------
Co-authored-by: Max Charlamb <maxcharlamb@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
eiriktsarpalis pushed a commit that referenced this pull request Jul 15, 2026
…ws ARM64 + Linux ARM/ARM64 (#130090)
Fills in the HFA / HVA classification paths in the cDAC's shared
ArgIterator port so `ICallingConvention.TryComputeArgGCRefMapBlob`
produces correct output on FEATURE_HFA targets, then lights up **Windows
ARM64**, **Linux ARM**, and **Linux ARM64** for both the ARGITER stress
sub-check and the zero-known-issues GCREFS assertion.
Follow-up to #129769 and #129858. Advances the platform matrix tracked
in #130008.
## What lights up in CI
| Platform | ARGITER stress | GCREFS 0-known-issues |
|---|---|---|
| Windows x64 | ✅ pre-existing | ✅ pre-existing |
| Windows x86 | ✅ pre-existing | ✅ pre-existing |
| **Windows ARM64** | ✅ **new** | ✅ **new** |
| **Linux ARM** | ✅ **new** | ✅ **new** |
| **Linux ARM64** | ✅ **new** | ✅ **new** |
| Linux / macOS x64 | ⏭ still deferred (SystemV-AMD64 eightbyte
classifier) | ⏭ tolerated |
| RISC-V, LoongArch, WASM | ⏭ still deferred | ⏭ tolerated |
The Helix stress matrix in `runtime-diagnostics.yml` already covers all
six platforms; this PR is purely lifting the xunit-level and
contract-level gates.
## Contract addition
Single new API on `IRuntimeTypeSystem`:
```csharp
bool TryGetHFAElementSize(TypeHandle typeHandle, out int elementSize);
```
Returns `4` / `8` / `16` for HFA / HVA types, `false` otherwise.
Self-gates on target architecture (FEATURE_HFA = ARM/ARM64) so callers
don't need to. Mirrors `MethodTable::GetHFAType`
(src/coreclr/vm/class.cpp:1867):
- At each recursion level, first checks `GetVectorHFAElementSize` for an
intrinsic Vector shape (`Vector64<T>` / `Vector128<T>` /
`System.Numerics.Vector<T>`)
- Then walks the first non-static instance field. `R4` -> 4, `R8` -> 8,
`ValueType` -> recurse via the existing `GetFieldDescApproxTypeHandle`
- HVA detection uses `enum_flag_IsIntrinsicType`
(`WFLAGS2_ENUM.IsIntrinsicType = 0x0020`) plus TypeDef name+namespace
match, with T-must-be-numerical verification (`CorIsNumericalType`:
`I1..R8 || I || U`)
`MethodTable` flag `WFLAGS_LOW.IsHFA = 0x00000800` is added to
`MethodTableFlags_1.cs`. That bit is repurposed on `UNIX_AMD64_ABI` as
`enum_flag_IsRegStructPassed`, hence the arch self-gate baked into the
API.
## `CdacTypeHandle` (thin adapter)
```csharp
public bool IsHomogeneousAggregate()
=> !_typeHandle.IsNull && Rts.TryGetHFAElementSize(_typeHandle, out _);
public int GetHomogeneousAggregateElementSize()
{
if (Arch == RuntimeInfoArchitecture.Arm)
return RequiresAlign8() ? 8 : 4; // ARM has no HVA; shortcut avoids the field walk
return _typeHandle.IsNull ? 0
: (Rts.TryGetHFAElementSize(_typeHandle, out int size) ? size : 0);
}
```
The ARM shortcut avoids a field walk: on ARM there's no HVA, and
`RequiresAlign8` mirrors `CheckForHFA`'s element-type-driven alignment
choice.
## Test coverage additions
`CallSignatures` debuggee gains an `HfaCategory` (~100 LOC) exercising:
- **Scalar HFAs**: `Float2/3/4`, `Double2/3/4` (all legal HFA arities)
- **Nested HFAs**: `NestedFloat3`, `NestedDouble4` (first-field
recursion)
- **Mixed args**: `HfaThenRef` / `RefThenHfa` / `TwoFloatHfas` — FP-reg
+ int-reg interaction; validates GCRefMap tokens for trailing ref args
after FP-reg consumption
- **HFA returns**: `ReturnFloat3`, `ReturnDouble2` (FP-register return
path, no `HasRetBufArg`)
- **HVA args**: `Vector64<float>`, `Vector128<float>`,
`System.Numerics.Vector<float>` (ARM64-only classification; benign on
other targets)
- **Negative shapes**: `Float5` (>4 fields), `MixedR4R8` (mixed FP) —
runtime correctly declines to flag as HFA, cDAC matches
Same debuggee compiles/passes everywhere; only exercises the new HFA
walker on ARM/ARM64.
## Gate expansions
Three gates now all match: **Windows {x86, x64, ARM64} + Linux {ARM,
ARM64}**
1. **`CdacStressTests.ArgIterStress_AllVerificationsPass`**: admits the
new ARM/ARM64 platforms.
2. **`CdacStressTestBase.AssertAllPassed.requiresZeroKnownIssues`**:
extended from Windows x86/x64 to include the same set. Any deferred
Frame on these platforms is now a hard failure.
3. **`GcScanner.PromoteCallerStack.supportedByCallingConvention`**:
lifted to match, so on ARM/ARM64 caller-stack refs are enumerated via
`TryComputeArgGCRefMapBlob` instead of falling through to
`RecordDeferredFrame`.
## Local validation
- `dotnet build src/native/managed/cdac/cdac.slnx -c Release` -- clean
- `dotnet test
.../UnitTests/Microsoft.Diagnostics.DataContractReader.Tests.csproj` --
2611 pass / 0 fail / 16 skipped
- No local ARM hardware; the ARM/ARM64 paths are validated by CI. If any
HFA/HVA shape produces a divergence, the ARGITER assertion will surface
the exact `MethodDesc` + slot mismatch.
## Known gaps (follow-ups, tracked in #130008)
- Linux / macOS x64: SystemV-AMD64 eightbyte struct classifier not
ported (`CdacTypeHandle.GetSystemVAmd64PassStructInRegisterDescriptor`
throws)
- RISC-V64 / LoongArch64: FP struct classifier not ported
(`GetFpStructInRegistersInfo` throws)
- WASM32: `GetFieldAlignment` not ported
> [!NOTE]
> This change was authored with assistance from GitHub Copilot.
---------
Co-authored-by: Max Charlamb <maxcharlamb@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
eiriktsarpalis pushed a commit that referenced this pull request Jul 15, 2026
…zero-known-issues on Linux x64 (#130257)
Fills in the SystemV-AMD64 struct-in-register classification path in the
cDAC's ArgIterator port so
`ICallingConvention.TryComputeArgGCRefMapBlob` produces correct output
on Unix x64. Lights up **Linux x64** for both the ARGITER stress
sub-check and the zero-known-issues GCREFS assertion.
Follow-up to #130090. Advances the platform matrix tracked in #130008.
## Approach
The runtime already pre-computes the eightbyte classification for every
enregisterable managed value type at `MethodTable` construction time and
caches it in `EEClass::m_eightByteRegistersInfo` (inside
`EEClassOptionalFields`). Runtime callers -- `ArgIterator`,
`getSystemVAmd64PassStructInRegisterDescriptor` -- read that cached info
via `SystemVRegDescriptorFromSystemVEightByteRegistersInfo` rather than
re-running `ClassifyEightBytes`. The cDAC now does the same.
Non-enregisterable structs (too large, or classified as
`SSEUP`/`X87`/`Memory`) never reach `StoreEightByteClassification`, so
their descriptor reads back with `NumEightBytes == 0`. That's the
natural `passedInRegisters = false` signal the cDAC surfaces. Native
P/Invoke layouts are not cached here (the runtime classifies them on
demand) -- the cDAC never walks native transition frames for GC
scanning, so this is fine.
Because the stress harness compares against `ComputeCallRefMap` -- which
uses the runtime path -- reading the same cached info gives
byte-for-byte parity by construction. No merge-rule /
intrinsic-rejection / `Int128` divergence to reason about.
macOS x64 uses the same SystemV path and works the same way if run
locally, but we don't add `osx_x64` to the Helix stress matrix in this
PR.
## Descriptor plumbing
**C++:**
- `class.h`: `cdac_data<EEClass>::OptionalFields`;
`cdac_data<EEClassOptionalFields>` (UNIX_AMD64_ABI-guarded) exposing the
`EightByteRegistersInfo` offset. Friend declaration on
`EEClassOptionalFields`.
- `methodtable.h`: `cdac_data<SystemVEightByteRegistersInfo>` for
`NumEightBytes` plus the two `byte[2]` eightbyte tables. Friend struct.
- `datadescriptor.inc`: `EEClass.OptionalFields` field + new
`EEClassOptionalFields` and `SystemVEightByteRegistersInfo` types, both
under `#ifdef UNIX_AMD64_ABI`.
**Managed:**
- `DataType`: new `EEClassOptionalFields` and
`SystemVEightByteRegistersInfo` entries.
- `Data.EEClass`: adds `OptionalFields`.
- `Data.EEClassOptionalFields`: `[Field] SystemVEightByteRegistersInfo`
inline sub-IData.
- `Data.SystemVEightByteRegistersInfo`: `[Field] NumEightBytes`,
`OnInit`-populated arrays.
- `CdacTypeHandle.GetSystemVAmd64PassStructInRegisterDescriptor`: ~40
LOC inline. Reads `EEClass -> OptionalFields -> EightByteRegistersInfo`,
then projects into `SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR`
with `eightByteOffsets` synthesized as `i * 8` (matches
`SystemVRegDescriptorFromSystemVEightByteRegistersInfo`).
## Test coverage
`CallSignatures` debuggee gains a `SysVCategory` exercising:
- Single-eightbyte shapes: `IntPair` (Integer), `FloatPair` (SSE),
`IntFloat` (merged Integer), `SingleRef` (IntegerReference)
- Two-eightbyte shapes: `LongLong`, `LongDouble`, `DoubleDouble`,
`RefInt`
- Nested value type, ByRefLike (`Span<byte>`)
- Rejection / stack-passed: 24-byte 3-ref struct, `Vector128<int>`,
empty struct
- `Int128Wrapper` (2 Integer eightbytes -- register-passed; runtime does
NOT reject by name)
- Unions (`LayoutKind.Explicit`), sub-eightbyte trailer, single-field
wrapper
Same debuggee runs everywhere; only exercises the new descriptor read on
Unix x64 (Windows targets don't emit the descriptor entries, so the
classifier returns `passedInRegisters = false` immediately via a
`TryGetTypeInfo` probe).
`AssertAllPassed` now folds `KnownIssues` into the `Failed` check
unconditionally -- no more platform gate on the assertion side; any
surviving known-issue count is a regression.
## Local validation
- `dotnet build src/native/managed/cdac/cdac.slnx -c Release`: clean
- Unit tests: 2646 pass / 0 fail / 16 skipped
- **Windows x64 local stress smoke** (`RunStressTests.ps1`):
- GCREFS `CallSignatures`: 6150 / 6150 pass, 0 known-issue
- ARGITER `CallSignatures` (with the `SysVCategory`): 383 pass / 0 fail
/ 0 skip / 0 error
The descriptor entries are `UNIX_AMD64_ABI`-only, so the descriptor-read
path only activates on Linux/macOS x64 targets; no regression on any
pre-existing platform. CI validates `linux-x64` end-to-end via the
byte-for-byte ARGITER comparison against runtime `ComputeCallRefMap`.
> [!NOTE]
> This change was authored with assistance from GitHub Copilot.
---------
Co-authored-by: Max Charlamb <maxcharlamb@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Aug 3, 2026
Sign up for freeto 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.

4 participants

@max-charlamb@steveisok@davidwrighton