Skip to content

[cDAC] GcScanner stack-ref scanning gaps vs native DAC #127765

@max-charlamb

Description

@max-charlamb

Gaps vs native DAC

1. Embedded GC refs in large value types passed by value

  • Native: MetaSig::GcScanRoots ELEMENT_TYPE_VALUETYPE case
    (src/coreclr/vm/siginfo.cpp:5437-5457) -> ReportPointersFromValueTypeArg -> walks the
    type's CGCDesc series and reports each embedded GC ref.
  • cDAC today: GcTypeKind.Other parameters are silently skipped
    (src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/GC/GcScanner.cs).
  • Where to implement: new helper in GcScanner.cs (or sibling ValueTypeGcWalker.cs)
    consuming a new IRuntimeTypeSystem.GetGCDescSeries(typeHandle) accessor. Requires
    GcSignatureTypeProvider / decoder consumers to surface the TypeHandle alongside
    GcTypeKind.Other so the walker has the type to enumerate. The ArgIterator port already
    provides IsArgPassedByRef, which is the first branch native takes here.

2. String..ctor this-slot handling

  • Native: src/coreclr/vm/frames.cpp:1523-1525:
    if (msig.HasThis() && pFunction->GetMethodTable() == g_pStringClass && pFunction->IsCtor())
        msig.ClearHasThis();
    The caller does not pass this for String..ctor; the runtime allocates and overwrites it.
  • cDAC today: no special case; the slot is treated as a tracked ref.
  • Where to implement: GcScanner.PromoteCallerStack (around the existing hasThis
    capture). Needs IRuntimeTypeSystem accessors for the well-known String MethodTable
    and an IsCtor predicate on MethodDesc.

3. SuppressParamTypeArg

  • Native: src/coreclr/vm/frames.cpp:1527-1528:
    if (pFunction->RequiresInstArg() && !SuppressParamTypeArg())
        msig.SetHasParamTypeArg();
    Per-Frame-subclass virtual; some frame kinds suppress the inst-arg slot.
  • cDAC today: RequiresInstArg is honored unconditionally in GcScanner.PromoteCallerStack.
  • Where to implement: extend the frame-kind table in the StackWalk contract with a
    SuppressParamTypeArg bool per FrameKind, then in GcScanner.PromoteCallerStack:
    requiresInstArg = rts.RequiresInstArg(mdh) && !frameSuppressesParamTypeArg;.

4. SysV AMD64 struct classification

  • Native: SystemVStructClassifier::Classify splits structs <= 16 bytes into two 8-byte "eightbytes", classifies each as SSE/INTEGER/MEMORY per the SysV ABI, and places them in mixed (rdi, xmm0)-style register combos. ArgIterator::GetNextOffset on UNIX_AMD64_ABI uses this classification.
  • cDAC today: ArgIterator.GetNextOffsetX64 has a placeholder that routes all SysV-x64 structs through GP regs / stack. Results in wrong offsets for every struct arg on Linux/macOS x64 -> missed or misreported refs in struct { object, object }, struct { float, float }, mixed classes, etc.
  • Where to implement: port SystemVStructClassifier into the cDAC CallingConvention/ namespace and wire it into GetNextOffsetX64 when IsX64UnixABI is set. Needs to walk MT fields recursively to classify each eightbyte.

5. GCRefMap parser for ExternalMethodFrame / StubDispatchFrame

  • Native: TransitionFrame::PromoteCallerStackUsingGCRefMap (src/coreclr/vm/frames.cpp:1622) walks a precomputed bitmap emitted by crossgen at fixup call sites. Used unconditionally by ExternalMethodFrame::GcScanRoots_Impl (frames.cpp:1018) and as the fast path by StubDispatchFrame::GcScanRoots_Impl (frames.cpp:939-947) when available.
  • cDAC today: no GCRefMap parser. ExternalMethodFrame stack args are not scanned at all; StubDispatchFrame falls through to signature-walk (which is wrong for stub-dispatch slots where the MethodDesc may not describe the real callee).
  • Where to implement: new GcRefMapDecoder next to GcScanner; needs data-descriptor exposure for ExternalMethodFrame.m_pGCRefMap and the R2R FindGCRefMap lookup path for indirection cells.

6. TypeSpec / generic value-type signature resolution

  • Native: MetaSig::GetByValType resolves TypeSpec blobs through the loader (LoadTypeHandleFromSignature) so HFA classification works for ValueTuple<float,float,float,float>, Nullable<long>, etc.
  • cDAC today: GcSignatureTypeProvider.GetTypeFromSpecification and GetGenericInstantiation return GcArgInfo.OtherUnresolved -> falls back to pointer-sized placeholder. Generic HFA structs are misclassified on ARM64 (routed through int regs instead of float regs), causing all subsequent arg offsets to be wrong.
  • Where to implement: add signature-to-TypeHandle resolution in the Loader contract (mirror of LoadTypeHandleFromSignature) and wire it into GcSignatureTypeProvider's TypeSpec/instantiation paths.

7. TypedReference TypeHandle lookup

  • Native: MetaSig::GetByValType for ELEMENT_TYPE_TYPEDBYREF returns the well-known g_TypedReferenceMT TypeHandle.
  • cDAC today: GcSignatureTypeProvider.GetPrimitiveType for PrimitiveTypeCode.TypedReference returns GcArgInfo.OtherUnresolved with no TypeHandle. ArgIterator gets pointer-sized placeholder; the embedded ref inside TypedReference is not reported.
  • Where to implement: add a GetWellKnownTypeHandle(WellKnownType) accessor to IRuntimeTypeSystem (or piggy-back on GetCoreLibFieldDescAndDef's lookup pattern) and wire it into the TypedReference branch of GcSignatureTypeProvider.

8. ARM / ARM64 dump test coverage

  • Native: ARM32 + ARM64 ABIs (Win-ARM64 + Linux-ARM64 + Apple-ARM64) fully exercised through DAC.
  • cDAC today: PR3 (Fix ThinSList size and unify EMPTY_BASES definitions #127911) ports ArgIterator code paths for ARM/ARM64 but there are no DumpTests that capture dumps on those platforms. The HFA classification path (FeatureHFA datadescriptor + IsHFA + HFA element sizing) is unverified against real dumps.
  • Where to implement: extend the cDAC DumpTests Helix legs to run on linux-arm64 / windows-arm64, and add HFA-focused debuggees (struct { double, double, double }, Vector128<float> args, etc.) under tests/DumpTests/Debuggees/.

Note

Sections 4-8 added by an AI/Copilot-generated edit.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions