-
-
Notifications
You must be signed in to change notification settings - Fork 187
Implement localloc and cpblk IL instructions #3242
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
base: develop
Are you sure you want to change the base?
Conversation
- Add handler for instruction. Add storage pointers to stack frame to allow dealocation on moethod return.
- Update mscorlib declaration.
WalkthroughAdds per-frame localloc tracking and implementations for CEE_LOCALLOC/CEE_CPBLK and RVA field loads; introduces DATATYPE_PTR and pointer-aware HeapBlock operations; adds native constructors for Span/ReadOnlySpan built from void*+length and updates native method tables and an mscorlib metadata token. Changes
Sequence Diagram(s)sequenceDiagram
participant IL as IL Interpreter
participant Frame as Stack Frame
participant Heap as Heap (Array)
participant GC as GC
IL->>Frame: CEE_LOCALLOC(size)
Frame->>Heap: allocate byte[] (size)
Heap-->>Frame: arrayRef
Frame->>GC: protect arrayRef (store in m_localAllocs)
Frame->>Frame: m_localAllocCount++
Frame-->>IL: push arrayRef
Note right of Frame: PushInline/RestoreFromInline copy/restore\nm_localAllocCount & m_localAllocs
Note right of Frame: On Pop: free/clear m_localAllocs and reset count
sequenceDiagram
participant Caller as Managed Call
participant SpanNative as Span native ctor
participant TypeSys as Type System / Metadata
participant Heap as Heap
Caller->>SpanNative: _ctor(void* ptr, int length)
SpanNative->>TypeSys: resolve and validate generic T (no refs)
TypeSys-->>SpanNative: resolved / error
SpanNative->>Heap: create T[] of length
Heap-->>SpanNative: arrayRef
SpanNative->>SpanNative: copy bytes from ptr -> arrayRef (element-size aware)
SpanNative-->>Caller: initialized Span instance (or throw)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (4)
src/CLR/CorLib/corlib_native_System_Span_1.cpp (1)
9-151: Span(void, int) ctor: logic is sound; consider minor robustness tweaks*Overall the implementation is careful: it reconstructs
Tfrom the caller’s generic context, rejects reference/contains‑reference element types, validateslength, enforces that the backing buffer isbyte[], computes the element size viac_CLR_RT_DataTypeLookup, validates capacity, reshapes the array metadata, and finally sets the Span length. This aligns with the ReadOnlySpan variant and the intendedlocalloc‑backed usage.A few points to double‑check:
sourceArray = stack.Arg1().DereferenceArray();assumes theVOID*argument is always a managed array reference. If any other caller ever passes a non‑array object (or null), this will AV; consider adding a null/array check and returningCLR_E_WRONG_TYPEorCLR_E_NULL_REFERENCEfor robustness.newNumElements = sourceArray->m_numOfElements / elementSizesilently truncates if the byte count is not an exact multiple ofelementSize. If the intent is to require exact divisibility (to avoid silently “losing” trailing bytes), you might want to checkm_numOfElements % elementSize == 0and treat mismatch asCLR_E_INVALID_PARAMETER.ClearElements(0, length)after reshaping is good for safety, but note that it zeroes the region even if the caller had already written into thelocallocbuffer; this is acceptable if the design is that the Span ctor owns initialization, but it’s worth confirming that no caller expects preserved contents.If all current call sites are only the new CoreLib APIs that hand in
localloc-allocatedbyte[], the current behavior is acceptable; the above mainly protects against future misuse.src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp (1)
9-151: ReadOnlySpan(void, int) mirrors Span correctly*The ReadOnlySpan pointer ctor is essentially identical to the Span version: it reconstructs
Tfrom the generic context, blocks reference/contains‑reference types, validateslength, enforcesbyte[]backing, reshapes the array metadata, and sets span length. That symmetry is good and keeps the two behaviors aligned.Same optional points as for Span:
- Consider a defensive null/array check on
sourceArray = stack.Arg1().DereferenceArray();to avoid hard faults on misuse.- Decide whether
m_numOfElements % elementSize != 0should be rejected explicitly rather than truncating via/.- Confirm that clearing elements via
ClearElements(0, length)matches expected semantics for any caller that might preinitialize the underlying buffer.Given the current intended use (backing from
localloc), the implementation is coherent; the suggestions are mainly to harden against future callers.src/CLR/Core/CLR_RT_StackFrame.cpp (1)
334-359: Inline frame save/restore of localloc state is consistentIn
PushInline, you:
- Save
m_localAllocCountand allm_localAllocs[]into the inline frame backup.- Then reset
m_localAllocCountand clearm_localAllocs[]for the inlined callee.This achieves the desired behavior: the caller’s locallocs are preserved and isolated from any additional locallocs in the inlined method. On
PopInline,RestoreFromInlineStackrestores the caller’sm_localAllocCountand array. This is logically sound and matches how other frame state is managed.src/CLR/Include/nanoCLR_Runtime.h (1)
2532-2534: Localloc tracking fields are consistent; just confirm init/GC integration and fix a small comment nitThe shared
MAX_LOCALALLOC_COUNTand mirroredm_localAllocCount/m_localAllocs[]in bothCLR_RT_InlineFrameandCLR_RT_StackFramelook coherent and match the intended per-frame localloc tracking.Two follow‑ups to double‑check in the implementation files:
- Ensure
m_localAllocCountis always reset andm_localAllocs[]are zeroed when stack frames are created or reused (including inline frames pulled fromCLR_RT_EventCache::m_inlineBufferStart), so no stale GC roots are left behind.- Ensure the GC mark path that walks stack/frame roots also walks each non‑null entry in
m_localAllocs[], otherwise these temporary arrays could be collected while still in use.Minor: the comment at Line 2570 has a typo (“max mumber” → “max number”).
Also applies to: 2545-2547, 2570-2572, 2674-2676
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
src/CLR/CorLib/corlib_native.cpp(5 hunks)src/CLR/CorLib/corlib_native.h(2 hunks)src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp(1 hunks)src/CLR/CorLib/corlib_native_System_Span_1.cpp(1 hunks)src/CLR/Core/CLR_RT_StackFrame.cpp(7 hunks)src/CLR/Core/Interpreter.cpp(1 hunks)src/CLR/Include/nanoCLR_Runtime.h(4 hunks)
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-01-09T13:32:43.711Z
Learnt from: josesimoes
Repo: nanoframework/nf-interpreter PR: 3062
File: src/System.Device.Spi/sys_dev_spi_native_System_Device_Spi_SpiDevice.cpp:106-188
Timestamp: 2025-01-09T13:32:43.711Z
Learning: In nanoFramework, CLR_RT_HeapBlock_Array::Pin() method returns void and cannot fail. It should be called without error handling.
Applied to files:
src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cppsrc/CLR/CorLib/corlib_native_System_Span_1.cpp
📚 Learning: 2024-10-12T19:00:39.000Z
Learnt from: josesimoes
Repo: nanoframework/nf-interpreter PR: 3023
File: targets/netcore/nanoFramework.nanoCLR/nanoCLR_native.cpp:191-225
Timestamp: 2024-10-12T19:00:39.000Z
Learning: When working with `nanoCLR_GetNativeAssemblyInformation`, fixed-size assembly names are required, so code that deals with variable-length names cannot be used.
Applied to files:
src/CLR/CorLib/corlib_native.hsrc/CLR/CorLib/corlib_native.cppsrc/CLR/Include/nanoCLR_Runtime.h
📚 Learning: 2025-06-26T09:16:55.184Z
Learnt from: josesimoes
Repo: nanoframework/nf-interpreter PR: 3190
File: src/CLR/Core/TypeSystem.cpp:0-0
Timestamp: 2025-06-26T09:16:55.184Z
Learning: In nanoFramework's CLR attribute parsing (src/CLR/Core/TypeSystem.cpp), the sentinel value 0xFFFF in string tokens represents a null string. When encountered, this should result in a true null reference (using SetObjectReference(nullptr)) rather than an empty string instance, and the boxing operation should be skipped via early return.
Applied to files:
src/CLR/CorLib/corlib_native.cpp
📚 Learning: 2024-09-25T11:28:38.536Z
Learnt from: josesimoes
Repo: nanoframework/nf-interpreter PR: 3023
File: targets/netcore/nanoFramework.nanoCLR/nanoCLR_native.cpp:191-225
Timestamp: 2024-09-25T11:28:38.536Z
Learning: In `nanoCLR_GetNativeAssemblyInformation`, there is no need to return the number of bytes written, as the memory buffer is zeroed, making the string buffer terminated.
Applied to files:
src/CLR/CorLib/corlib_native.cpp
📚 Learning: 2025-01-22T03:38:57.394Z
Learnt from: josesimoes
Repo: nanoframework/nf-interpreter PR: 3074
File: src/CLR/Core/GarbageCollector_Info.cpp:107-167
Timestamp: 2025-01-22T03:38:57.394Z
Learning: In nanoFramework's memory management code, DataSize() validation is comprehensively handled through CLR_RT_HeapCluster::ValidateBlock() and other caller code. Additional size checks in ValidateCluster() are redundant as the validation is already performed at multiple levels.
Applied to files:
src/CLR/Include/nanoCLR_Runtime.h
🧬 Code graph analysis (3)
src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp (1)
src/CLR/CorLib/corlib_native_System_Span_1.cpp (2)
_ctor___VOID__VOIDptr__I4(11-151)_ctor___VOID__VOIDptr__I4(11-11)
src/CLR/CorLib/corlib_native_System_Span_1.cpp (1)
src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp (2)
_ctor___VOID__VOIDptr__I4(11-151)_ctor___VOID__VOIDptr__I4(11-11)
src/CLR/CorLib/corlib_native.cpp (2)
src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp (2)
_ctor___VOID__VOIDptr__I4(11-151)_ctor___VOID__VOIDptr__I4(11-11)src/CLR/CorLib/corlib_native_System_Span_1.cpp (2)
_ctor___VOID__VOIDptr__I4(11-151)_ctor___VOID__VOIDptr__I4(11-11)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
- GitHub Check: nf-interpreter (Build_Azure_RTOS_targets SL_STK3701A)
- GitHub Check: nf-interpreter (Build_WIN32_nanoCLR)
- GitHub Check: nf-interpreter (Build_ESP32_targets ESP32_P4_UART)
- GitHub Check: nf-interpreter (Build_ESP32_targets ESP32_ETHERNET_KIT_1.2)
- GitHub Check: nf-interpreter (Build_ESP32_targets ESP32_S3_ALL)
- GitHub Check: nf-interpreter (Build_ESP32_targets ESP32_H2_THREAD)
- GitHub Check: nf-interpreter (Build_ESP32_targets ESP32_C6_THREAD)
- GitHub Check: nf-interpreter (Build_ESP32_targets ESP32_C3)
- GitHub Check: nf-interpreter (Build_ESP32_targets ESP32_BLE_REV0)
- GitHub Check: nf-interpreter (Build_STM32_targets ST_STM32F769I_DISCOVERY)
- GitHub Check: nf-interpreter (Build_STM32_targets ST_STM32F429I_DISCOVERY)
- GitHub Check: nf-interpreter (Build_ESP32_targets ESP32_PSRAM_REV0)
- GitHub Check: nf-interpreter (Build_NXP_targets NXP_MIMXRT1060_EVK)
- GitHub Check: nf-interpreter (Build_TI_SimpleLink_targets TI_CC1352R1_LAUNCHXL_915)
- GitHub Check: nf-interpreter (Nightly build) (Build_STM32_targets MXCHIP_AZ3166)
- GitHub Check: nf-interpreter (Nightly build) (Build_STM32_targets ORGPAL_PALX)
- GitHub Check: nf-interpreter (Nightly build) (Build_STM32_targets ORGPAL_PALTHREE)
- GitHub Check: nf-interpreter (Check_Code_Style)
- GitHub Check: nf-interpreter (Nightly build) (Check_Build_Options)
- GitHub Check: nf-interpreter (Check_Build_Options)
🔇 Additional comments (6)
src/CLR/CorLib/corlib_native.h (1)
781-790: Span/ReadOnlySpan pointer ctor declarations look consistentThe new native declarations for
_ctor___VOID__VOIDptr__I4on bothLibrary_corlib_native_System_ReadOnlySpan_1andLibrary_corlib_native_System_Span_1match the corresponding implementations and are correctly guarded by#if (NANOCLR_REFLECTION == TRUE). No further changes needed here.src/CLR/Core/Interpreter.cpp (1)
1005-1034: Generic .cctor reschedule helper is correct but edge cases should be confirmedThe new
HandleGenericCctorReschedulehelper cleanly centralizes the pattern of:
- Computing a hash for a closed generic type using
ComputeHashForClosedGenericType.- Treating
0xFFFFFFFFas an invalid hash and returningCLR_E_WRONG_TYPE.- Looking up or creating the
CLR_RT_GenericCctorExecutionRecord.- If a .cctor is scheduled but not executed, rewinding
ipby 3 bytes (opcode + compressed field token) and returningCLR_E_RESCHEDULE.Two points to verify:
- The “3 bytes” rewind (
*pIp -= 3) assumes this is only used for opcodes with exactly one‑byte opcode + two‑byte compressed token. You’re using it with field opcodes likeLDSFLD/LDSFLDA/STSFLDwhose encoding here indeed uses a 2‑byte compressed token; please confirm we don’t call this helper from any opcode with a different operand layout in future.FindOrCreateGenericCctorRecord(hash, nullptr)can return non‑null with flags that don’t havec_Scheduledset; you correctly treat that as “no reschedule” and returnS_OK, which is fine. Just be aware this will not attempt to schedule .cctors, only observe already‑scheduled ones.Conceptually this is a good extraction and keeps the LDSFLD/LDSFLDA/STSFLD paths simpler.
src/CLR/Core/CLR_RT_StackFrame.cpp (2)
124-130: Initialize localloc tracking in Push to a known stateZeroing
m_localAllocCountand clearingm_localAllocs[]on frame creation is correct and prevents stale pointers from previous uses of the same event‑heap block. No changes needed here.
425-473: RestoreStack/SaveStack correctly propagate localloc metadata across inline frames
SaveStackandRestoreStacknow copym_localAllocCountand eachm_localAllocs[i]to and from theCLR_RT_InlineFrame. This keeps the localloc tracking coherent across stack saves/restores (e.g., inlining transitions, exception unwinds). The loops mirror each other and usec_Max_Localloc_Count, so they’ll stay in sync with the header definition.src/CLR/CorLib/corlib_native.cpp (2)
699-702: ReadOnlySpan pointer ctor wiring looks correct; please confirm index alignmentThe new
Library_corlib_native_System_ReadOnlySpan_1::_ctor___VOID__VOIDptr__I4entry, with surroundingnullptrpadding and the existingNativeReadOnlySpanConstructorslot, matches the established layout pattern for generic natives in this table.Given how fragile this array/index mapping is, please confirm that:
- The managed
ReadOnlySpan<T>pointer ctor is marked as native and its method index matches this new slot in the reflection-enabled mscorlib.- Any other new
ReadOnlySpan<T>natives introduced in the managed assembly are either accounted for here or intentionally left as managed.Also applies to: 708-709, 718-725
813-825: Span pointer ctor and updated reflection token look consistent; verify against regenerated mscorlibThe added
Library_corlib_native_System_Span_1::_ctor___VOID__VOIDptr__I4entry, along with the nearbyCopyToandNativeSpanConstructorentries and the extranullptrpadding before the threading methods, follows the existing grouping and spacing conventions for Span-related natives.The updated native assembly token
0xCECAB752underNANOCLR_REFLECTION == TRUEis appropriate given the mscorlib metadata change; the non‑reflection token and table remain unchanged.Please verify that:
- The managed
Span<T>pointer ctor’s native index matches this new table position in the reflection-enabled mscorlib build.- The updated token matches the regenerated managed mscorlib (so loader and debugger see this as a single coherent version).
- For
NANOCLR_REFLECTION == FALSE, either the managed mscorlib did not change or its native table/token are regenerated in lockstep in the corresponding build configuration.Also applies to: 843-846, 1616-1616
- Update code accordingly.
- localloc now allocates memory from platform heap. - Stores it as unmaged pointer.
- Now gets pointer to metadata storage.
9c548a9 to
750b1ef
Compare
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
Automated fixes for code style.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/CLR/Core/CLR_RT_HeapBlock.cpp (1)
496-657: Fix DATATYPE_PTR handling inStoreToReference; pointer arithmetic looks OKThe new
DATATYPE_PTRsupport inNumericAdd/NumericSubis fine in spirit (treat pointers as byte-wise addresses and only allowI4offsets), but theStoreToReferencebranch forDATATYPE_PTRis unsafe and does not implement cpblk semantics correctly:
dtinStoreToReferenceisref.DataType(), so theDATATYPE_PTRcase runs when the reference is an unmanaged pointer.obj = ref.Dereference();returns the raw address stored in that pointer, which is an arbitrary unmanaged buffer, not aCLR_RT_HeapBlock*.&NumericByRef()is the address of this heap block’s internalNumericunion, not the memory pointed to by the source pointer.memcpy((void *)obj, (void *)&NumericByRef(), size);therefore copies bytes from the heapblock’s internal storage into the unmanaged buffer, not from the source buffer.- After that, the common
obj->Assign(*this);path treats the unmanaged buffer as aCLR_RT_HeapBlockand writes headers/fields into it. That is undefined behavior and will corrupt whatever memory the pointer happens to reference.For
cpblk-style scenarios (destptr,srcptr,size), you likely want:
- Both operands to be unmanaged pointers (e.g.,
src.StoreToReference(dest, size)), and- A raw block copy between the underlying addresses, without interpreting them as heap blocks or assigning heapblock headers.
A safer implementation for the
DATATYPE_PTRbranch would look like:- else if (dt == DATATYPE_PTR) - { - // unmanaged pointer, perform a direct memory copy - obj = ref.Dereference(); - memcpy((void *)obj, (void *)&NumericByRef(), size); - } + else if (dt == DATATYPE_PTR) + { + // Unmanaged pointer: copy raw bytes from this->Dereference() to ref.Dereference(). + void *dst = (void *)ref.Dereference(); + void *src = (void *)this->Dereference(); + + // cpblk allows overlapping regions; memmove is safer than memcpy here. + memmove(dst, src, size); + + // No heapblock assignment for raw pointer copies. + NANOCLR_SET_AND_LEAVE(S_OK); + } @@ - obj->Assign(*this); + obj->Assign(*this);(Where
this->Dereference()andref.Dereference()forDATATYPE_PTRare assumed to return the stored raw addresses.)This avoids treating arbitrary unmanaged buffers as
CLR_RT_HeapBlockinstances and alignsDATATYPE_PTRwith the intended “raw address” semantics.The
NumericAdd/NumericSubDATATYPE_PTRcases themselves look reasonable (byte-wise pointer arithmetic with anI4offset), so onceStoreToReferenceis fixed, the unmanaged pointer story should be consistent.Also applies to: 2005-2095, 2103-2199
♻️ Duplicate comments (1)
src/CLR/Core/Interpreter.cpp (1)
4224-4259: Validate negative sizes and address GC rooting concerns from previous review.This implementation has two issues:
New concern: Negative size check needed. Line 4226 reads the size as
u4(unsigned). If IL passes a negativeint32value, it will be reinterpreted as a large unsigned value, potentially causing a huge allocation attempt. Unlike the array creation path that usesCreateInstance(which checks for negative lengths),platform_mallochas no such guard.Previously flagged: GC rooting and OOM handling still missing. A past review comment (lines 4224-4293) identified that
m_localAllocspointers are not scanned or relocated by the GC, and that OOM handling should include compaction+retry. Those concerns remain unaddressed.Immediate fix for negative size:
OPDEF(CEE_LOCALLOC, "localloc", PopI, PushI, InlineNone, IPrimitive, 2, 0xFE, 0x0F, NEXT) { - CLR_UINT32 size = evalPos[0].NumericByRef().u4; + CLR_INT32 sizeRaw = evalPos[0].NumericByRef().s4; + + if (sizeRaw < 0) + { + NANOCLR_SET_AND_LEAVE(CLR_E_OUT_OF_RANGE); + } + + CLR_UINT32 size = (CLR_UINT32)sizeRaw; evalPos--; CHECKSTACK(stack, evalPos);For the GC rooting and OOM handling issues, please see the detailed previous review comment that recommended:
- Adding
CheckMultipleBlocks(stack->m_localAllocs, stack->m_localAllocCount)inThread_Mark(GC marking)- Calling
Heap_Relocateon each stored pointer in the relocation path- Adding compaction+retry on
CLR_E_OUT_OF_MEMORYsimilar to the array allocation flow (lines ~3410-3420)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp(1 hunks)src/CLR/CorLib/corlib_native_System_Span_1.cpp(1 hunks)src/CLR/Core/CLR_RT_HeapBlock.cpp(3 hunks)src/CLR/Core/CLR_RT_StackFrame.cpp(7 hunks)src/CLR/Core/Interpreter.cpp(3 hunks)src/CLR/Include/nanoCLR_Runtime.h(4 hunks)src/CLR/Include/nanoCLR_Runtime__HeapBlock.h(1 hunks)src/CLR/Include/nanoCLR_Types.h(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-01-22T03:38:57.394Z
Learnt from: josesimoes
Repo: nanoframework/nf-interpreter PR: 3074
File: src/CLR/Core/GarbageCollector_Info.cpp:107-167
Timestamp: 2025-01-22T03:38:57.394Z
Learning: In nanoFramework's memory management code, DataSize() validation is comprehensively handled through CLR_RT_HeapCluster::ValidateBlock() and other caller code. Additional size checks in ValidateCluster() are redundant as the validation is already performed at multiple levels.
Applied to files:
src/CLR/Core/CLR_RT_HeapBlock.cppsrc/CLR/Include/nanoCLR_Runtime.h
📚 Learning: 2025-01-09T13:32:43.711Z
Learnt from: josesimoes
Repo: nanoframework/nf-interpreter PR: 3062
File: src/System.Device.Spi/sys_dev_spi_native_System_Device_Spi_SpiDevice.cpp:106-188
Timestamp: 2025-01-09T13:32:43.711Z
Learning: In nanoFramework, CLR_RT_HeapBlock_Array::Pin() method returns void and cannot fail. It should be called without error handling.
Applied to files:
src/CLR/Include/nanoCLR_Runtime__HeapBlock.h
📚 Learning: 2024-10-12T19:00:39.000Z
Learnt from: josesimoes
Repo: nanoframework/nf-interpreter PR: 3023
File: targets/netcore/nanoFramework.nanoCLR/nanoCLR_native.cpp:191-225
Timestamp: 2024-10-12T19:00:39.000Z
Learning: When working with `nanoCLR_GetNativeAssemblyInformation`, fixed-size assembly names are required, so code that deals with variable-length names cannot be used.
Applied to files:
src/CLR/Include/nanoCLR_Runtime.h
📚 Learning: 2025-11-20T14:08:30.044Z
Learnt from: josesimoes
Repo: nanoframework/nf-interpreter PR: 3240
File: src/CLR/Core/TypeSystem.cpp:812-818
Timestamp: 2025-11-20T14:08:30.044Z
Learning: Repo: nanoframework/nf-interpreter
File: src/CLR/Core/TypeSystem.cpp
Context: Generics resolution in CLR_RT_TypeSpec_Instance::ResolveToken
Learning: When resolving a VAR (!T) TypeSpec using the caller’s closed generic, after switching to the caller context (Set(caller->genericType->Assembly(), closedTsRow) and assigning assembly accordingly), always fetch the TypeSpec with target = assembly->GetTypeSpec(closedTsRow) rather than assm->GetTypeSpec(closedTsRow) to avoid cross-assembly mismatches.
Applied to files:
src/CLR/Core/Interpreter.cpp
🧬 Code graph analysis (1)
src/CLR/CorLib/corlib_native_System_Span_1.cpp (1)
src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp (2)
_ctor___VOID__VOIDptr__I4(11-112)_ctor___VOID__VOIDptr__I4(11-11)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: nf-interpreter (Check_Code_Style)
- GitHub Check: nf-interpreter (Nightly build) (Build_STM32_targets MXCHIP_AZ3166)
- GitHub Check: nf-interpreter (Nightly build) (Build_STM32_targets ORGPAL_PALX)
- GitHub Check: nf-interpreter (Nightly build) (Build_STM32_targets ORGPAL_PALTHREE)
- GitHub Check: nf-interpreter (Nightly build) (Check_Build_Options)
- GitHub Check: nf-interpreter (Check_Build_Options)
🔇 Additional comments (5)
src/CLR/Include/nanoCLR_Runtime.h (4)
2570-2571: LGTM - Good practice using a class-scoped constant.The constant
c_Max_Localloc_Countcorrectly references the global macro and follows the existing naming convention for constants in this structure.
2545-2546: I'll verify the concerns raised in the original review comment about the new inline frame local allocation tracking fields. Let me start by examining the codebase to understand the implementation details.
<function_calls>
#!/bin/bashFind the full CLR_RT_InlineFrame structure context
rg -n "struct CLR_RT_InlineFrame|class CLR_RT_InlineFrame" -A 50 --type=cpp --type=h | head -100
</function_calls>Now let me search for the PopInline implementation and localloc cleanup logic:
<function_calls>
#!/bin/bashSearch for PopInline and inline frame cleanup
rg -n "PopInline\s*(" -B 3 -A 15 --type=cpp --type=h | head -150
</function_calls>Let me find the MAX_LOCALALLOC_COUNT definition and search for localloc-related cleanup:
<function_calls>
#!/bin/bashFind MAX_LOCALALLOC_COUNT and localloc handling
rg -n "MAX_LOCALALLOC_COUNT|m_localAlloc" --type=cpp --type=h | head -100
</function_calls>Let me also check the frame cleanup mechanism to understand how memory is deallocated:
<function_calls>
#!/bin/bashSearch for frame cleanup and platform_free calls related to allocations
rg -n "platform_free|CleanupAlllocations|m_localAlloc" -B 3 -A 3 --type=cpp | head -150
</function_calls>Let me also look for how regular frames handle localloc allocations to understand the pattern:
<function_calls>
#!/bin/bashCheck if there's a regular frame localloc tracking similar to what's being added for inline frames
rg -n "m_localAllocCount|m_localAllocs" --type=cpp --type=h -B 5 -A 5
</function_calls>
2674-2676: I'm unable to access the repository to verify the specific concerns raised in the review comment. However, based on the review comment itself, I can provide a rewritten version that clarifies the key verification points that need manual confirmation:
Verify cleanup responsibility, document fields, and confirm struct size constraints.
These fields mirror the inline frame additions and share the same requirements for proper maintenance:
Cleanup responsibility: Confirm that
CLR_RT_StackFrame::Pop()properly deallocates pointers tracked inm_localAllocsbefore frame destruction. If cleanup is missing, localloc'd memory could leak.Documentation: Add inline comments explaining that
m_localAllocCountandm_localAllocstrack unmanaged localloc pointers and clarify the cleanup lifecycle.Struct size impact: These additions increase the per-frame overhead by approximately 17–33 bytes (1 byte count + 4–8 bytes × 4 pointers, architecture-dependent). For deeply nested call stacks, verify this trade-off is acceptable and document it.
Struct layout validation: Confirm the placement before
m_extension[1]does not violate alignment expectations and that theCT_ASSERTstatements at lines 2876–2887 still validate field offsets correctly with these new members.
2532-2533: The review comment cannot be verified due to repository access limitations in the sandbox environment.The original comment raises valid concerns about the
MAX_LOCALALLOC_COUNThard limit and requests verification of:
- Overflow handling when the limit of 4 is exceeded
- Documentation explaining why 4 was chosen
- Bounds checking in the CEE_LOCALLOC implementation
However, these verifications require access to:
- The CEE_LOCALLOC instruction handler implementation
- Memory management and frame cleanup code
- Any validation logic related to
m_localAllocCountSince the header file alone cannot confirm whether proper overflow handling exists in the implementation, and sandbox repository access is unavailable, the original review comment's verification request remains unresolved but valid.
The comment should be addressed by the developer reviewing/implementing this change by confirming overflow behavior and adding documentation if not already present.
src/CLR/Include/nanoCLR_Types.h (1)
521-524: I'll help you verify and rewrite this review comment. Let me start by examining the enum structure and the specific concerns raised.
<function_calls>
First, let's read the header file to see the full enum context
cat -n src/CLR/Include/nanoCLR_Types.h | sed -n '500,540p'
</function_calls><function_calls>
Check the full DataType enum to understand the structure
rg -n "enum.*DataType|DATATYPE_" src/CLR/Include/nanoCLR_Types.h | head -50
</function_calls><function_calls>
Let's see more context around DATATYPE_FIRST_INVALID and DATATYPE_PTR
rg -n -B5 -A5 "DATATYPE_FIRST_INVALID|DATATYPE_PTR" src/CLR/Include/nanoCLR_Types.h
</function_calls>
| // check if field has RVA | ||
| CLR_RT_FieldDef_Instance inst; | ||
| if (inst.InitializeFromIndex(field) && (inst.target->flags & CLR_RECORD_FIELDDEF::FD_HasFieldRVA) && | ||
| inst.target->defaultValue != CLR_EmptyIndex) | ||
| { | ||
| CLR_PMETADATA ptrSrc; | ||
|
|
||
| // Get the data from the Signatures table (this contains the raw byte array) | ||
| ptrSrc = inst.assembly->GetSignature(inst.target->defaultValue); | ||
| CLR_UINT32 elementCount; | ||
| NANOCLR_READ_UNALIGNED_UINT16(elementCount, ptrSrc); | ||
|
|
||
| // ptrSrc is now pointing to the raw byte data | ||
| ptr = (CLR_RT_HeapBlock *)ptrSrc; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incorrect pointer cast and unclear RVA semantics.
Line 3082 casts the raw metadata pointer from GetSignature directly to CLR_RT_HeapBlock*. The Signatures table contains raw bytes, not CLR_RT_HeapBlock structures, so this cast is a type mismatch that could lead to incorrect pointer interpretation.
Additionally, the elementCount read at line 3079 is never used. More importantly, this code changes the semantics of ldsflda for RVA fields: instead of returning a reference to the static field storage (which AccessStaticField would provide), it now returns a pointer to raw metadata bytes. Managed code expecting a typed field reference will receive a raw byte pointer instead.
Clarify the intended behavior:
- If RVA fields should expose raw metadata bytes, document this deviation and ensure the cast is correct (likely
void*orCLR_UINT8*rather thanCLR_RT_HeapBlock*) - If RVA fields should behave like normal static fields, remove this special case and let
AccessStaticFieldhandle them - Consider whether
ldsfldaon an RVA field should even be allowed, as it exposes internal metadata layout
// Example fix if raw bytes are intended:
// check if field has RVA
CLR_RT_FieldDef_Instance inst;
if (inst.InitializeFromIndex(field) && (inst.target->flags & CLR_RECORD_FIELDDEF::FD_HasFieldRVA) &&
inst.target->defaultValue != CLR_EmptyIndex)
{
CLR_PMETADATA ptrSrc;
// Get the data from the Signatures table (this contains the raw byte array)
ptrSrc = inst.assembly->GetSignature(inst.target->defaultValue);
CLR_UINT32 elementCount;
NANOCLR_READ_UNALIGNED_UINT16(elementCount, ptrSrc);
// ptrSrc is now pointing to the raw byte data
// TODO: Verify this is the correct interpretation for RVA fields
ptr = (CLR_RT_HeapBlock *)(void *)ptrSrc;
}…d0f50-b140-488f-b95f-db856b2f79b6 Code style fixes for nanoframework/nf-interpreter PR#3242
Automated fixes for code style.
…c26bb-406f-4265-bae1-c4f38e4cd866 Code style fixes for nanoframework/nf-interpreter PR#3242
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
src/CLR/Core/CLR_RT_HeapBlock.cpp (1)
644-654: Critical: Missing early return afterDATATYPE_PTRcase causes undefined behavior.After the
memcpyon line 647, execution falls through to line 654 (obj->Assign(*this)), butobjwas never initialized in theDATATYPE_PTRbranch. This will dereference an uninitialized pointer, causing a crash or memory corruption.Apply this diff to fix the control flow:
else if (dt == DATATYPE_PTR) { // unmanaged pointer, perform a direct memory copy memcpy((void *)ref.UnmanagedPointer(), (void *)&NumericByRef(), size); + + NANOCLR_SET_AND_LEAVE(S_OK); } else { NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE); } - - obj->Assign(*this);Alternatively, move the
obj->Assign(*this)inside the previous else-if branches that initializeobj.src/CLR/CorLib/corlib_native_System_Span_1.cpp (1)
163-205: ExistingNativeSpanConstructormissing error check onCreateInstance.Line 189 calls
CLR_RT_HeapBlock_Array::CreateInstancewithoutNANOCLR_CHECK_HRESULT, unlike the new constructor at line 99. If allocation fails, subsequent code will operate on invalid data.Apply this diff for consistency:
CLR_RT_HeapBlock &refArray = thisSpan[FIELD___array]; - CLR_RT_HeapBlock_Array::CreateInstance(refArray, length, sourceType); + NANOCLR_CHECK_HRESULT(CLR_RT_HeapBlock_Array::CreateInstance(refArray, length, sourceType));src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp (1)
117-159: ExistingNativeReadOnlySpanConstructormissing error check onCreateInstance.Line 143 calls
CLR_RT_HeapBlock_Array::CreateInstancewithoutNANOCLR_CHECK_HRESULT, same issue as in the Span implementation. This should be fixed for consistency with the new constructors.Apply this diff:
CLR_RT_HeapBlock &refArray = thisSpan[FIELD___array]; - CLR_RT_HeapBlock_Array::CreateInstance(refArray, length, sourceType); + NANOCLR_CHECK_HRESULT(CLR_RT_HeapBlock_Array::CreateInstance(refArray, length, sourceType));
🧹 Nitpick comments (1)
src/CLR/Core/Interpreter.cpp (1)
3051-3089: RVA handling inldsfldalooks coherent; only a tiny readability nitThe new RVA path cleanly:
- Re-resolves the field,
- Reads and discards the length prefix from the signature blob to advance
ptrSrc,- Treats the remaining bytes as the backing storage whose address is then exposed via
SetReference.This matches the intent of exposing a stable address for RVA-backed data and is self‑contained. If you want, you could make
dummyVaraCLR_UINT16to better document the blob header width, but that’s purely cosmetic.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp(1 hunks)src/CLR/CorLib/corlib_native_System_Span_1.cpp(1 hunks)src/CLR/Core/CLR_RT_HeapBlock.cpp(3 hunks)src/CLR/Core/Interpreter.cpp(3 hunks)src/CLR/Include/nanoCLR_Runtime__HeapBlock.h(2 hunks)
🧰 Additional context used
🧠 Learnings (9)
📓 Common learnings
Learnt from: josesimoes
Repo: nanoframework/nf-interpreter PR: 3242
File: src/CLR/Core/Interpreter.cpp:4263-4293
Timestamp: 2025-11-27T02:57:09.957Z
Learning: In nanoFramework's nf-interpreter, the cpblk IL instruction implementation in src/CLR/Core/Interpreter.cpp does not require runtime validation of pointer addresses, size, or bounds. Per ECMA-335 specification, cpblk is an unverifiable instruction where the CIL compiler ensures correctness, not the runtime.
📚 Learning: 2025-01-22T03:38:57.394Z
Learnt from: josesimoes
Repo: nanoframework/nf-interpreter PR: 3074
File: src/CLR/Core/GarbageCollector_Info.cpp:107-167
Timestamp: 2025-01-22T03:38:57.394Z
Learning: In nanoFramework's memory management code, DataSize() validation is comprehensively handled through CLR_RT_HeapCluster::ValidateBlock() and other caller code. Additional size checks in ValidateCluster() are redundant as the validation is already performed at multiple levels.
Applied to files:
src/CLR/CorLib/corlib_native_System_Span_1.cppsrc/CLR/Core/CLR_RT_HeapBlock.cppsrc/CLR/Core/Interpreter.cppsrc/CLR/Include/nanoCLR_Runtime__HeapBlock.h
📚 Learning: 2025-01-09T13:32:43.711Z
Learnt from: josesimoes
Repo: nanoframework/nf-interpreter PR: 3062
File: src/System.Device.Spi/sys_dev_spi_native_System_Device_Spi_SpiDevice.cpp:106-188
Timestamp: 2025-01-09T13:32:43.711Z
Learning: In nanoFramework, CLR_RT_HeapBlock_Array::Pin() method returns void and cannot fail. It should be called without error handling.
Applied to files:
src/CLR/CorLib/corlib_native_System_Span_1.cppsrc/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cppsrc/CLR/Core/Interpreter.cppsrc/CLR/Include/nanoCLR_Runtime__HeapBlock.h
📚 Learning: 2024-10-08T15:52:09.445Z
Learnt from: josesimoes
Repo: nanoframework/nf-interpreter PR: 3023
File: targets/netcore/nanoFramework.nanoCLR/nanoCLR_native.cpp:191-225
Timestamp: 2024-10-08T15:52:09.445Z
Learning: In `nanoCLR_GetNativeAssemblyInformation`, there is no need to return the number of bytes written, as the memory buffer is zeroed, making the string buffer terminated.
Applied to files:
src/CLR/CorLib/corlib_native_System_Span_1.cppsrc/CLR/Include/nanoCLR_Runtime__HeapBlock.h
📚 Learning: 2025-11-27T02:57:09.957Z
Learnt from: josesimoes
Repo: nanoframework/nf-interpreter PR: 3242
File: src/CLR/Core/Interpreter.cpp:4263-4293
Timestamp: 2025-11-27T02:57:09.957Z
Learning: In nanoFramework's nf-interpreter, the cpblk IL instruction implementation in src/CLR/Core/Interpreter.cpp does not require runtime validation of pointer addresses, size, or bounds. Per ECMA-335 specification, cpblk is an unverifiable instruction where the CIL compiler ensures correctness, not the runtime.
Applied to files:
src/CLR/Core/Interpreter.cppsrc/CLR/Include/nanoCLR_Runtime__HeapBlock.h
📚 Learning: 2024-10-12T19:00:39.000Z
Learnt from: josesimoes
Repo: nanoframework/nf-interpreter PR: 3023
File: targets/netcore/nanoFramework.nanoCLR/nanoCLR_native.cpp:191-225
Timestamp: 2024-10-12T19:00:39.000Z
Learning: When working with `nanoCLR_GetNativeAssemblyInformation`, fixed-size assembly names are required, so code that deals with variable-length names cannot be used.
Applied to files:
src/CLR/Core/Interpreter.cpp
📚 Learning: 2025-11-20T14:08:30.044Z
Learnt from: josesimoes
Repo: nanoframework/nf-interpreter PR: 3240
File: src/CLR/Core/TypeSystem.cpp:812-818
Timestamp: 2025-11-20T14:08:30.044Z
Learning: Repo: nanoframework/nf-interpreter
File: src/CLR/Core/TypeSystem.cpp
Context: Generics resolution in CLR_RT_TypeSpec_Instance::ResolveToken
Learning: When resolving a VAR (!T) TypeSpec using the caller’s closed generic, after switching to the caller context (Set(caller->genericType->Assembly(), closedTsRow) and assigning assembly accordingly), always fetch the TypeSpec with target = assembly->GetTypeSpec(closedTsRow) rather than assm->GetTypeSpec(closedTsRow) to avoid cross-assembly mismatches.
Applied to files:
src/CLR/Core/Interpreter.cppsrc/CLR/Include/nanoCLR_Runtime__HeapBlock.h
📚 Learning: 2025-01-22T03:38:57.394Z
Learnt from: josesimoes
Repo: nanoframework/nf-interpreter PR: 3074
File: src/CLR/Core/GarbageCollector_Info.cpp:107-167
Timestamp: 2025-01-22T03:38:57.394Z
Learning: In CLR_RT_GarbageCollector::ValidateCluster, DataSize() validation is already handled by ValidateBlock() and other caller code, making additional size checks redundant.
Applied to files:
src/CLR/Core/Interpreter.cppsrc/CLR/Include/nanoCLR_Runtime__HeapBlock.h
📚 Learning: 2025-06-26T09:16:55.184Z
Learnt from: josesimoes
Repo: nanoframework/nf-interpreter PR: 3190
File: src/CLR/Core/TypeSystem.cpp:0-0
Timestamp: 2025-06-26T09:16:55.184Z
Learning: In nanoFramework's CLR attribute parsing (src/CLR/Core/TypeSystem.cpp), the sentinel value 0xFFFF in string tokens represents a null string. When encountered, this should result in a true null reference (using SetObjectReference(nullptr)) rather than an empty string instance, and the boxing operation should be skipped via early return.
Applied to files:
src/CLR/Include/nanoCLR_Runtime__HeapBlock.h
🧬 Code graph analysis (3)
src/CLR/CorLib/corlib_native_System_Span_1.cpp (1)
src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp (2)
_ctor___VOID__VOIDptr__I4(11-115)_ctor___VOID__VOIDptr__I4(11-11)
src/CLR/Core/CLR_RT_HeapBlock.cpp (1)
src/CLR/Include/nanoCLR_Runtime__HeapBlock.h (1)
SetUnmanagedPointer(1106-1110)
src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp (1)
src/CLR/CorLib/corlib_native_System_Span_1.cpp (2)
_ctor___VOID__VOIDptr__I4(11-115)_ctor___VOID__VOIDptr__I4(11-11)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
- GitHub Check: nf-interpreter (Build_Azure_RTOS_targets SL_STK3701A)
- GitHub Check: nf-interpreter (Build_WIN32_nanoCLR)
- GitHub Check: nf-interpreter (Build_ESP32_targets ESP32_P4_UART)
- GitHub Check: nf-interpreter (Build_ESP32_targets ESP32_ETHERNET_KIT_1.2)
- GitHub Check: nf-interpreter (Build_ESP32_targets ESP32_S3_ALL)
- GitHub Check: nf-interpreter (Build_ESP32_targets ESP32_H2_THREAD)
- GitHub Check: nf-interpreter (Build_ESP32_targets ESP32_C6_THREAD)
- GitHub Check: nf-interpreter (Build_ESP32_targets ESP32_C3)
- GitHub Check: nf-interpreter (Build_STM32_targets ST_STM32F769I_DISCOVERY)
- GitHub Check: nf-interpreter (Build_ESP32_targets ESP32_BLE_REV0)
- GitHub Check: nf-interpreter (Build_TI_SimpleLink_targets TI_CC1352R1_LAUNCHXL_915)
- GitHub Check: nf-interpreter (Build_ESP32_targets ESP32_PSRAM_REV0)
- GitHub Check: nf-interpreter (Build_NXP_targets NXP_MIMXRT1060_EVK)
- GitHub Check: nf-interpreter (Build_STM32_targets ST_STM32F429I_DISCOVERY)
- GitHub Check: nf-interpreter (Check_Code_Style)
- GitHub Check: nf-interpreter (Nightly build) (Build_STM32_targets MXCHIP_AZ3166)
- GitHub Check: nf-interpreter (Nightly build) (Build_STM32_targets ORGPAL_PALX)
- GitHub Check: nf-interpreter (Nightly build) (Build_STM32_targets ORGPAL_PALTHREE)
- GitHub Check: nf-interpreter (Nightly build) (Check_Build_Options)
- GitHub Check: nf-interpreter (Check_Build_Options)
🔇 Additional comments (11)
src/CLR/Include/nanoCLR_Runtime__HeapBlock.h (2)
763-768: NewUnmanagedPointerstruct correctly defined for pointer storage.The struct layout with a single
uintptr_tmember is appropriate for storing raw unmanaged pointers, and integrates well with the existing union structure.
1106-1115: Accessor methods for unmanaged pointer look correct.
SetUnmanagedPointerproperly setsDATATYPE_PTRwith size 1 and stores the pointerUnmanagedPointer()safely returns 0 for non-PTR types, preventing misuseThis is a clean API for the new pointer data type.
src/CLR/Core/CLR_RT_HeapBlock.cpp (2)
2081-2094: Pointer addition implementation looks correct.The byte-wise pointer arithmetic follows ECMA-335 Table III.2 semantics. The
[[fallthrough]]attribute properly documents the intentional fall-through to the error case when the RHS type is notI4.
2181-2194: Pointer subtraction implementation is consistent with addition.Mirrors the addition logic correctly for subtraction, maintaining consistency in pointer arithmetic handling.
src/CLR/CorLib/corlib_native_System_Span_1.cpp (2)
9-9: Type alias improves readability.The
typedefalias forRuntimeHelpersis consistent with the parallel change incorlib_native_System_ReadOnlySpan_1.cpp.
11-115: Span pointer constructor implementation looks correct.The implementation properly:
- Validates the generic type argument and ensures T doesn't contain references
- Validates length is non-negative and input is
DATATYPE_PTR- Uses
NANOCLR_CHECK_HRESULTforCreateInstance(addressing previous review)- Protects array from GC before dereferencing (addressing previous review)
- Correctly uses
UnmanagedPointer()to retrieve the raw pointerThe logic is consistent with the parallel
ReadOnlySpanimplementation.src/CLR/CorLib/corlib_native_System_ReadOnlySpan_1.cpp (2)
9-9: Type alias consistent with Span implementation.
11-115: ReadOnlySpan pointer constructor implementation is correct and consistent.The implementation mirrors the
Span<T>version exactly, which is appropriate since both types need identical construction semantics from unmanaged pointers. All validations, error handling, and GC protection are properly in place.src/CLR/Core/Interpreter.cpp (3)
1777-1818: STIND handler: behavior unchanged; whitespace-only deltaThis region’s logic (size selection +
Promote()+StoreToReference) remains consistent with existing semantics; the only observable change is a blank line, so there’s no behavioral impact to review here.
4263-4293:CEE_CPBLK: directmemmoveis appropriate for an unverifiable instructionThe cpblk handler correctly:
- Pops
size,sourceAddress, anddestinationAddress(usinguintptr_tand handling 32/64‑bit via_WIN64),- Uses
memmoveto allow overlapping regions, and- Cleans up the eval stack consistently with the existing opcode patterns.
Per the ECMA-335 semantics and your documented design for this runtime, cpblk is unverifiable and the CIL producer is responsible for pointer validity, so the lack of extra runtime checks here is intentional and acceptable. Based on learnings, no additional validation is required.
4224-4259: I'm unable to complete the verification due to repository access limitations. The repository clone failed, and there are no public references to CEE_LOCALLOC or m_localAllocs in available documentation.Per the instructions, absence of evidence is not evidence of absence—the code patterns may exist in the implementation but not be publicly documented. Without direct access to verify the frame teardown cleanup flow, I cannot confirm or contradict the review's assumption that "corresponding stack-frame teardown frees entries in m_localAllocs."
CEE_LOCALLOC: unmanaged allocation flow is straightforward and bounded by per-frame countThis implementation:
- Pops the requested size, enforces the per-frame
m_localAllocCount < c_Max_Localloc_Countguard,- Allocates via
platform_malloc, mapsnullptrtoCLR_E_OUT_OF_MEMORY,- Zeroes the buffer per ECMA-335, tracks the pointer in
m_localAllocs, and- Exposes it as an unmanaged pointer via
SetUnmanagedPointer.Assuming the corresponding stack-frame teardown frees entries in
m_localAllocs, this is a clear and reasonable design for stackalloc-like behavior on your platforms.
Description
Span<T>andReadOnlySpan<T>.Motivation and Context
stackallocinstruction.Span<T>andReadOnlySpan<T>CoreLibrary#260.How Has This Been Tested?
Span<T>andReadOnlySpan<T>CoreLibrary#260].Screenshots
Types of changes
Checklist
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.