Skip to content

[cDAC] Expose EEClass layout alignment so the WASM ArgIterator can reconstruct value-type argument layout #131343

Description

@lewing

Summary

The cDAC's WASM ArgIterator cannot reconstruct the stack layout of value-type arguments, because CdacTypeHandle.GetFieldAlignment() is unimplemented:

public int GetFieldAlignment()
{
    throw new NotImplementedException("Field alignment is not yet implemented.");
}

The shared ArgIterator Wasm32 case computes align = Math.Clamp(_argTypeHandle.GetFieldAlignment(), 8, 16), so any managed stack walk over a wasm frame whose signature contains a value-type argument (Guid, DateTime, an ordinary struct, Int128, a SIMD vector, ...) throws instead of producing an argument layout.

This blocks cDAC-side argument enumeration / GC-ref reporting on wasm, which builds on the WASM stack walk added in #130988.

What the runtime does

The runtime's wasm ArgIterator reads the class's real alignment requirement (src/coreclr/vm/callingconvention.h):

align = std::clamp(CEEInfo::getClassAlignmentRequirementStatic(thValueType.GetMethodTable()),
                   INTERP_STACK_SLOT_SIZE, INTERP_STACK_ALIGNMENT);

which resolves to EEClassLayoutInfo::GetAlignmentRequirement() (src/coreclr/vm/class.h), returning m_ManagedLargestAlignmentRequirementOfAllMembers. The faithful cDAC implementation is to read that same value, with the same clamp.

Why "special-case the SIMD types" is not sufficient

An earlier attempt derived the alignment by matching type metadata (namespace/name) to identify 16-byte SIMD vectors. That approach cannot converge on the runtime's behavior, because the runtime's set of 16-byte-aligned wasm types is strictly broader than the SIMD vector types. Int128/UInt128 are the existing counterexample — src/coreclr/vm/methodtablebuilder.cpp does:

else if ((strcmp(name, g_Int128Name) == 0) || (strcmp(name, g_UInt128Name) == 0))
{
    ...
#elif defined(TARGET_WASM)
    pLayout->SetAlignmentRequirement(16); // sizeof(v128)

Any name-matching predicate that enumerates a hand-picked subset of the runtime's alignment policy will drift from it. Reading the computed alignment is the only approach that stays correct as that policy evolves, and it fixes every value type at once rather than one family.

Work required

  1. Runtime data descriptor: expose the layout-info alignment. Note that EEClassLayoutInfo m_LayoutInfo lives on LayoutEEClass : public EEClass (src/coreclr/vm/class.h), not on EEClass itself, so this needs a cdac_data<LayoutEEClass> specialization (in the style of the existing cdac_data<EEClassOptionalFields>) plus the EEClassLayoutInfo field offset, and matching datadescriptor.h entries.
  2. cDAC data type + contract: a Data type for the layout info and a contract surface to read the alignment requirement for a MethodTable/EEClass.
  3. CdacTypeHandle.GetFieldAlignment: return the alignment requirement, mirroring getClassAlignmentRequirementStatic.
  4. HasLayout gating: EEClass::GetLayoutInfo() asserts HasLayout(). The reader must check the corresponding flag before reading the layout info, or it will read unrelated memory for classes without layout. The behavior for value types without layout info needs to be established to match the runtime.
  5. Docs: update the affected contract doc(s).
  6. Tests: cDAC unit tests covering a non-SIMD struct, a 16-byte-aligned type (e.g. Int128), and the no-layout case.

Context

Note

This issue was authored with the assistance of GitHub Copilot.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions