diff --git a/src/coreclr/interpreter/compiler.cpp b/src/coreclr/interpreter/compiler.cpp index 262cb8673422e8..c5ddc3c9942602 100644 --- a/src/coreclr/interpreter/compiler.cpp +++ b/src/coreclr/interpreter/compiler.cpp @@ -4528,15 +4528,20 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re } else if (opcode == INTOP_CALLDELEGATE) { - int32_t firstTargetArgOffset = INTERP_STACK_SLOT_SIZE; - if (numArgs > 1) + int32_t sizeOfArgsUpto16ByteAlignment = 0; + for (int argIndex = 1; argIndex < numArgs; argIndex++) { - // The first argument is the delegate obj, the second is the first target arg - // The offset of the first target arg relative to the start of delegate call args is equal to the alignment of the - // first target arg. - GetInterpTypeStackSize(m_pVars[callArgs[1]].clsHnd, m_pVars[callArgs[1]].interpType, &firstTargetArgOffset); + int32_t argAlignment = INTERP_STACK_SLOT_SIZE; + int32_t size = GetInterpTypeStackSize(m_pVars[callArgs[argIndex]].clsHnd, m_pVars[callArgs[argIndex]].interpType, &argAlignment); + size = ALIGN_UP_TO(size, INTERP_STACK_SLOT_SIZE); + if (argAlignment == INTERP_STACK_ALIGNMENT) + { + break; + } + sizeOfArgsUpto16ByteAlignment += size; } - m_pLastNewIns->data[1] = firstTargetArgOffset; + + m_pLastNewIns->data[1] = sizeOfArgsUpto16ByteAlignment; } } break; diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index ed673cf571728f..db0994390e29f8 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -2564,10 +2564,10 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr int8_t* returnValueAddress = LOCAL_VAR_ADDR(returnOffset, int8_t); // Used only for INTOP_CALLDELEGATE to allow removal of the delegate object from the argument list - size_t firstTargetArgOffset = 0; + int32_t sizeOfArgsUpto16ByteAlignment = 0; if (*ip == INTOP_CALLDELEGATE) { - firstTargetArgOffset = (size_t)ip[4]; + sizeOfArgsUpto16ByteAlignment = ip[4]; ip += 5; } else @@ -2633,8 +2633,22 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr } else { - // Shift args down by one slot to remove the delegate obj pointer - memmove(LOCAL_VAR_ADDR(callArgsOffset, int8_t), LOCAL_VAR_ADDR(callArgsOffset + firstTargetArgOffset, int8_t), pTargetMethod->argsSize); + // 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)