Skip to content

Conversation

@jkoritzinsky
Copy link
Member

@jkoritzinsky jkoritzinsky commented Jan 29, 2026

We will use this to represent accurate indirect loads/stores for structs with ExtendedLayoutKind.SwiftStruct and ExtendedLayoutKind.SwiftEnum. These types have no trailing padding themselves, but their stride includes trailing padding.

I believe this is the easiest way to represent this without breaking requiring special handling for System.Span and the like.

Adding this in a separate PR to be a zero-diff change.

After this PR, I'll put out a PR to update the VMs to correctly handle layout (including recursive layout) for types with Swift layout.

Contributes to #100896

Copilot AI review requested due to automatic review settings January 29, 2026 00:36
@github-actions github-actions bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Jan 29, 2026
@jkoritzinsky jkoritzinsky requested a review from a team January 29, 2026 00:37
@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @JulieLeeMSFT, @dotnet/jit-contrib
See info in area-owners.md if you want to be subscribed.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a new getClassIndirectSize API to the JIT-EE interface to support accurate indirect load/store operations for types with special layout characteristics, particularly Swift structs and enums. These types have different sizes when accessed indirectly (excluding trailing padding) versus their full stride (including padding).

Changes:

  • Adds getClassIndirectSize method to the JIT-EE interface (ICorStaticInfo)
  • Implements the new API in CoreCLR VM and NativeAOT (currently returning the same value as getClassSize for zero-diff behavior)
  • Updates JIT to use IndirectSize() instead of Size() for block operations (ldobj/stobj)
  • Adds full superpmi support for recording and replaying the new API call
  • Updates JITEEVERSIONGUID to reflect the interface change

Reviewed changes

Copilot reviewed 32 out of 32 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/coreclr/inc/corinfo.h Adds virtual method declaration for getClassIndirectSize
src/coreclr/inc/jiteeversionguid.h Updates GUID to reflect JIT-EE interface change
src/coreclr/inc/icorjitinfoimpl_generated.h Adds override declaration
src/coreclr/vm/jitinterface.cpp Implements getClassIndirectSize returning VMClsHnd.GetSize() (same as getClassSize); minor whitespace cleanup
src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp Adds wrapper implementation
src/coreclr/jit/ICorJitInfo_names_generated.h Adds API name for diagnostics
src/coreclr/jit/layout.h Adds m_indirectSize field and GetIndirectSize() accessor
src/coreclr/jit/layout.cpp Fetches and stores indirect size via new API call
src/coreclr/jit/gentree.h Adds IndirectSize() method to GenTreeBlk
src/coreclr/jit/gentree.cpp Updates GenTreeIndir::Size() to use IndirectSize() for block operations
src/coreclr/jit/valuenum.cpp Uses IndirectSize() for value numbering block loads
src/coreclr/jit/lsra*.cpp Updates BuildBlockStore to use IndirectSize() across all architectures
src/coreclr/jit/lower*.cpp Updates LowerBlockStore to use IndirectSize() across all architectures; updates helper call lowering
src/coreclr/jit/codegenlinear.cpp Uses IndirectSize() for setting block size in register
src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs Implements getClassIndirectSize returning GetElementSize() (same as getClassSize)
src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs Adds generated callback and delegate for new API
src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt Adds function signature for thunk generation
src/coreclr/tools/aot/jitinterface/jitinterface_generated.h Adds callback and wrapper implementation
src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp Adds replay support
src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp Adds passthrough interceptor
src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp Adds counting interceptor
src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp Adds recording interceptor
src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h Adds method declarations and packet enum
src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp Implements record/dump/replay methods
src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h Adds LightWeightMap entry for the new API

@jakobbotsch
Copy link
Member

jakobbotsch commented Jan 29, 2026

This concept feels unnatural on the JIT side. Almost everything deals with this size while almost nothing deals with stride as the concept (mostly element address calculations?). Is there a reason why we cannot introduce stride as the concept?

(Also, I expect we would not need a new JIT-EE API to compute the stride from the size and alignment.)

@jkoritzinsky
Copy link
Member Author

This concept feels unnatural on the JIT side. Almost everything deals with this size while almost nothing deals with stride as the concept (mostly element address calculations?). Is there a reason why we cannot introduce stride as the concept?

(Also, I expect we would not need a new JIT-EE API to compute the stride from the size and alignment.)

I was trying to do two things with this change:

  1. Avoid making sweeping changes across the VM.
  2. Avoid introducing a scenario where Unsafe.AddByteOffset(ref s, sizeof(s)) is either
    a) not equal to Unsafe.Add(ref s, 1) (would happen if sizeof represented size instead of stride)
    b) would point to an invalid GC heap address (would happen if sizeof represented stride instead of size and ref s didn't point into a collection).

Furthermore, as we already have to have handling for the Swift calling convention, we could account for the difference between stride and size in that lowering to avoid issues when passing by value.

The only case where we'd need to ensure that we don't accidentally read/write "too much" would be indirect loads/stores where the target could possibly be in memory allocated by Swift code. This felt like a targeted approach to handle that case specifically without causing changes to flow across the whole system.

I'll try doing an alternative PR where we split the concepts of stride and size in the VM and JIT (and keep sizeof referring to size as that feels correct/matches Swift behavior more closely).

@jkotas
Copy link
Member

jkotas commented Jan 29, 2026

The only case where we'd need to ensure that we don't accidentally read/write "too much" would be indirect loads/stores where the target could possibly be in memory allocated by Swift code.

Can this be a helper that copies the "right amount" from/to a local so that the JIT does not need to worry about it?

@jkoritzinsky
Copy link
Member Author

The only case where we'd need to ensure that we don't accidentally read/write "too much" would be indirect loads/stores where the target could possibly be in memory allocated by Swift code.

Can this be a helper that copies the "right amount" from/to a local so that the JIT does not need to worry about it?

That should work, yes. I'll give that a shot.

@jkoritzinsky
Copy link
Member Author

The more I look at this, the more I realize that this is just a specific case of the helper we already have for C++/CLI to support copy constructors, just that we want to fire it in different scenarios. I don't think there's a way to unify the two concepts (as C++/CLI already manually emits the copy constructors when needed, so we don't want to insert more than absolutely necessary for that case) but I don't like how closely they seem to be while not being the same.

… helper. This helper is inserted for ldobj/stobj instructions as well as writing to a caller-provided return buffer.
@jkoritzinsky jkoritzinsky changed the title Add a getIndirectSize API to the JIT-EE interface to represent the correct size for ldobj/stobj operations Add API to provide a JIT helper to the JIT-EE interface to use for implementing ldobj/stobj operations Jan 30, 2026
@jkoritzinsky jkoritzinsky requested a review from jkotas January 30, 2026 00:58
@jkotas
Copy link
Member

jkotas commented Jan 30, 2026

My thinking was this helper can be called by the Swift marshalling code so that the JIT does not need to worry about this at all. Is that not feasible?

@jkoritzinsky
Copy link
Member Author

That could handle the "return buffer" case (we could limit that path to Swift call conv UCO), but it wouldn't handle the case of actual pointers being dereferenced in user code (the ldobj/stobj scenario)

@jkotas
Copy link
Member

jkotas commented Jan 30, 2026

How does this work in C/C++ projections for Swift?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

3 participants