Skip to content

[clr-interp] Implement CEE_LOCALLOC and frame data allocator for dynamic stack allocations #114860

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

kotlarmilos
Copy link
Member

Description

This PR adds support for the CEE_LOCALLOC and frame data allocator to the interpreter for handling dynamic stack allocations.

Changes

  • Implemented CEE_LOCALLOC with a frame data allocator for dynamic stack memory
  • Implemented overflow-check multiplication operations used in the interpreter to evaluate stackalloc arguments
  • Implemented FrameDataAllocator based on the existing allocator
    typedef struct {
    FrameDataFragment *first, *current;
    FrameDataInfo *infos;
    int infos_len, infos_capacity;
    /* For GC sync */
    int inited;
    } FrameDataAllocator;

Validation

Tests in Interpreter.cs covering frame data allocator scenarios.

@Copilot Copilot AI review requested due to automatic review settings April 21, 2025 11:26
@ghost ghost added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Apr 21, 2025
@kotlarmilos kotlarmilos self-assigned this Apr 21, 2025
@kotlarmilos kotlarmilos added area-CodeGen-Interpreter-coreclr and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Apr 21, 2025
@kotlarmilos kotlarmilos added this to the 10.0.0 milestone Apr 21, 2025
Copy link
Contributor

@Copilot 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 implements dynamic stack allocation capabilities in the interpreter by introducing a frame data allocator and adding support for the CEE_LOCALLOC opcode.

  • Added comprehensive tests for localloc operations in Interpreter.cs
  • Implemented frame data allocator infrastructure and dynamic memory allocation functions in interpexec.{h,cpp}
  • Updated the interpreter compiler to generate code for CEE_LOCALLOC and overflow-check multiplication opcodes

Reviewed Changes

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

File Description
src/tests/JIT/interpreter/Interpreter.cs Added tests for dynamic stack allocation via TestLocalloc()
src/coreclr/vm/interpexec.h Added definitions for FrameDataFragment, FrameDataInfo, and FrameDataAllocator structures
src/coreclr/vm/interpexec.cpp Implemented functions for managing dynamic stack memory and added INTOP_LOCALLOC opcode handling
src/coreclr/interpreter/compiler.cpp Updated code generation to support CEE_LOCALLOC and checked overflow multiplication opcodes
Files not reviewed (1)
  • src/coreclr/interpreter/intops.def: Language not supported
Comments suppressed due to low confidence (2)

src/coreclr/vm/interpexec.cpp:1266

  • Ensure that the ALIGN_UP macro and related allocation logic correctly enforce the required alignment for dynamic stack allocations to avoid subtle memory issues.
mem = frame_data_alloc(&pThreadContext->frameDataAllocator, (InterpreterFrame*)pFrame, ALIGN_UP(len, INTERP_STACK_ALIGNMENT));

src/coreclr/interpreter/compiler.cpp:3145

  • The handling of the CEE_LOCALLOC opcode involves pointer arithmetic and stack pointer adjustments; please verify that these operations maintain the stack invariants under all conditions to prevent runtime errors.
case CEE_LOCALLOC:

Copy link
Contributor

Tagging subscribers to this area: @BrzVlad, @janvorli, @kg
See info in area-owners.md if you want to be subscribed.

@@ -1129,6 +1204,8 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr
goto MAIN_LOOP;
}

// Interpreter-TODO: FrameDataAllocator is owned by the current thread, free it when the thread exits instead of here
delete pThreadContext->pFrameDataAllocator;
Copy link
Member

Choose a reason for hiding this comment

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

I am not sure how can we delete the allocator here. If I understand it correctly, we allocate it in the InterpGetThreadContext, but only if the t_pThreadContext was not set yet. And we delete it here when we exit from a block of interpreted frames that can happen many times. E.g. with the EH, a catch funclet exits here and then we resume in the parent function after the catch. That means that after resuming from catch, the pFrameDataAllocator would be invalid.

Copy link
Member Author

@kotlarmilos kotlarmilos Apr 25, 2025

Choose a reason for hiding this comment

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

That workaround was only temporary, but you are right. I’ve now implemented a destructor for InterpThreadContext. Please let me know if there is a deterministic place to destroy the thread context.

If this approach is reasonable, I would move the initialization into the constructor instead.

Copy link
Member

Choose a reason for hiding this comment

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

I would prefer moving the InterpThreadContext instance to the coreclr Thread class instead and performing the cleanup in the Thread::~Thread or Thread::OnThreadTerminate. Having a thread local object with a destructor is a recipe for possible problems due to the fact that the order of their destruction is arbitrary.

Copy link
Member Author

Choose a reason for hiding this comment

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

Implemented the InterpThreadContext constructor and destructor, and moved its instance into the CoreCLR Thread class. It is now created lazily on first pThread->GetInterpThreadContext() in prestub.cpp. This ensures that all allocated memory is properly released when the Thread destructor runs.

… destructor. Inline FrameDataAllocator into InterpThreadContext and ensure allocated memory is released

if (len > 0)
{
pMemory = pThreadContext->frameDataAllocator.Alloc(pFrame, &len);
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
pMemory = pThreadContext->frameDataAllocator.Alloc(pFrame, &len);
pMemory = pThreadContext->frameDataAllocator.Alloc(pFrame, len);

It is not necessary to use the aligned length for memset. We just need to clear the memory that the user code asked for. We do not need to clear the extra padding.

@kotlarmilos kotlarmilos mentioned this pull request Apr 28, 2025
58 tasks
Copy link
Member

@janvorli janvorli left a comment

Choose a reason for hiding this comment

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

LGTM, thank you!

@janvorli janvorli merged commit f2f058e into dotnet:main Apr 28, 2025
99 of 101 checks passed
@github-actions github-actions bot locked and limited conversation to collaborators May 29, 2025
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.

6 participants