Repository: dotnet/runtime
File: src/coreclr/vm/interpexec.cpp (C++)

Code before:
                            targetMethod = NonVirtualEntry2MethodDesc(actualTarget);
                        }

                        PTR_InterpByteCodeStart targetIp;
                        if ((targetMethod) && (targetIp = targetMethod->GetInterpreterCode()) != NULL)
                        {
                            pFrame->ip = ip;
                            InterpMethod* pTargetMethod = targetIp->Method;
                            if (isTailcall)
                            {
                                // Move args from callArgsOffset to start of stack frame.
                                _ASSERTE(pTargetMethod->CheckIntegrity());
                                // It is safe to use memcpy because the source and destination are both on the interp stack, not in the GC heap.
                                // We need to use the target method's argsSize, not our argsSize, because tail calls (unlike CEE_JMP) can have a
                                //  different signature from the caller.
                                memcpy(pFrame->pStack, LOCAL_VAR_ADDR(callArgsOffset + INTERP_STACK_SLOT_SIZE, int8_t), pTargetMethod->argsSize);
                                // Reuse current stack frame. We discard the call insn's returnOffset because it's not important and tail calls are
                                //  required to be followed by a ret, so we know nothing is going to read from stack[returnOffset] after the call.
                                pFrame->ReInit(pFrame->pParent, targetIp, pFrame->pRetVal, pFrame->pStack);
                            }
                            else
                            {
                                // Shift args down by one slot to remove the delegate obj pointer.
                                // We need to preserve alignment of arguments that require 16-byte alignment.
                                // The sizeOfArgsUpto16ByteAlignment is the size of all the target method args starting at the first argument upto the last
                                // argument that doesn't require 16-byte alignment.
                                if (sizeOfArgsUpto16ByteAlignment != 0)
                                {
                                    memmove(LOCAL_VAR_ADDR(callArgsOffset, int8_t), LOCAL_VAR_ADDR(callArgsOffset + INTERP_STACK_SLOT_SIZE, int8_t), sizeOfArgsUpto16ByteAlignment);
                                }

                                if (sizeOfArgsUpto16ByteAlignment != pTargetMethod->argsSize)
                                {
                                    // There are arguments that require 16-byte alignment
                                    size_t firstAlignedTargetArgDstOffset = ALIGN_UP(sizeOfArgsUpto16ByteAlignment, INTERP_STACK_ALIGNMENT);
                                    size_t firstAlignedTargetArgSrcOffset = ALIGN_UP(INTERP_STACK_SLOT_SIZE + sizeOfArgsUpto16ByteAlignment, INTERP_STACK_ALIGNMENT);
                                    memmove(LOCAL_VAR_ADDR(callArgsOffset + firstAlignedTargetArgDstOffset, int8_t), LOCAL_VAR_ADDR(callArgsOffset + firstAlignedTargetArgSrcOffset, int8_t), pTargetMethod->argsSize - sizeOfArgsUpto16ByteAlignment);
                                }

                                // Allocate child frame.
                                InterpMethodContextFrame *pChildFrame = pFrame->pNext;
                                if (!pChildFrame)
                                {
                                    pChildFrame = (InterpMethodContextFrame*)alloca(sizeof(InterpMethodContextFrame));
                                    pChildFrame->pNext = NULL;
                                    pFrame->pNext = pChildFrame;
                                    SAVE_THE_LOWEST_SP;
                                }
                                pChildFrame->ReInit(pFrame, targetIp, returnValueAddress, LOCAL_VAR_ADDR(callArgsOffset, int8_t));
                                pFrame = pChildFrame;
                            }

Review comment (suggestion):
The comment description is misleading. It says "upto the last argument that doesn't require 16-byte alignment" but the code actually stops at the first argument that requires 16-byte alignment. Consider rephrasing to: "The sizeOfArgsUpto16ByteAlignment is the size of all the target method args starting at the first argument up to (but not including) the first argument that requires 16-byte alignment."
```suggestion
                                // The sizeOfArgsUpto16ByteAlignment is the size of all the target method args starting at the first argument up to (but not including) the first argument that requires 16-byte alignment.
```

Code after:
                            targetMethod = NonVirtualEntry2MethodDesc(actualTarget);
                        }

                        PTR_InterpByteCodeStart targetIp;
                        if ((targetMethod) && (targetIp = targetMethod->GetInterpreterCode()) != NULL)
                        {
                            pFrame->ip = ip;
                            InterpMethod* pTargetMethod = targetIp->Method;
                            if (isTailcall)
                            {
                                // Move args from callArgsOffset to start of stack frame.
                                _ASSERTE(pTargetMethod->CheckIntegrity());
                                // It is safe to use memcpy because the source and destination are both on the interp stack, not in the GC heap.
                                // We need to use the target method's argsSize, not our argsSize, because tail calls (unlike CEE_JMP) can have a
                                //  different signature from the caller.
                                memcpy(pFrame->pStack, LOCAL_VAR_ADDR(callArgsOffset + INTERP_STACK_SLOT_SIZE, int8_t), pTargetMethod->argsSize);
                                // Reuse current stack frame. We discard the call insn's returnOffset because it's not important and tail calls are
                                //  required to be followed by a ret, so we know nothing is going to read from stack[returnOffset] after the call.
                                pFrame->ReInit(pFrame->pParent, targetIp, pFrame->pRetVal, pFrame->pStack);
                            }
                            else
                            {
                                // Shift args down by one slot to remove the delegate obj pointer.
                                // We need to preserve alignment of arguments that require 16-byte alignment.
                                // The sizeOfArgsUpto16ByteAlignment is the size of all the target method args starting at the first argument up to (but not including) the first argument that requires 16-byte alignment.
                                if (sizeOfArgsUpto16ByteAlignment != 0)
                                {
                                    memmove(LOCAL_VAR_ADDR(callArgsOffset, int8_t), LOCAL_VAR_ADDR(callArgsOffset + INTERP_STACK_SLOT_SIZE, int8_t), sizeOfArgsUpto16ByteAlignment);
                                }

                                if (sizeOfArgsUpto16ByteAlignment != pTargetMethod->argsSize)
                                {
                                    // There are arguments that require 16-byte alignment
                                    size_t firstAlignedTargetArgDstOffset = ALIGN_UP(sizeOfArgsUpto16ByteAlignment, INTERP_STACK_ALIGNMENT);
                                    size_t firstAlignedTargetArgSrcOffset = ALIGN_UP(INTERP_STACK_SLOT_SIZE + sizeOfArgsUpto16ByteAlignment, INTERP_STACK_ALIGNMENT);
                                    memmove(LOCAL_VAR_ADDR(callArgsOffset + firstAlignedTargetArgDstOffset, int8_t), LOCAL_VAR_ADDR(callArgsOffset + firstAlignedTargetArgSrcOffset, int8_t), pTargetMethod->argsSize - sizeOfArgsUpto16ByteAlignment);
                                }

                                // Allocate child frame.
                                InterpMethodContextFrame *pChildFrame = pFrame->pNext;
                                if (!pChildFrame)
                                {
                                    pChildFrame = (InterpMethodContextFrame*)alloca(sizeof(InterpMethodContextFrame));
                                    pChildFrame->pNext = NULL;
                                    pFrame->pNext = pChildFrame;
                                    SAVE_THE_LOWEST_SP;
                                }
                                pChildFrame->ReInit(pFrame, targetIp, returnValueAddress, LOCAL_VAR_ADDR(callArgsOffset, int8_t));
                                pFrame = pChildFrame;
                            }
                            // Set execution state for the new frame