diff --git a/src/coreclr/src/jit/codegenarmarch.cpp b/src/coreclr/src/jit/codegenarmarch.cpp index ef884c5e930f4..6b7ca7955b2aa 100644 --- a/src/coreclr/src/jit/codegenarmarch.cpp +++ b/src/coreclr/src/jit/codegenarmarch.cpp @@ -3281,7 +3281,7 @@ void CodeGen::genCreateAndStoreGCInfo(unsigned codeSize, unsigned reversePInvokeFrameVarNumber = compiler->lvaReversePInvokeFrameVar; assert(reversePInvokeFrameVarNumber != BAD_VAR_NUM && reversePInvokeFrameVarNumber < compiler->lvaRefCount); LclVarDsc& reversePInvokeFrameVar = compiler->lvaTable[reversePInvokeFrameVarNumber]; - gcInfoEncoder->SetReversePInvokeFrameSlot(reversePInvokeFrameVar.lvStkOffs); + gcInfoEncoder->SetReversePInvokeFrameSlot(reversePInvokeFrameVar.GetStackOffset()); } gcInfoEncoder->Build(); diff --git a/src/coreclr/src/jit/codegencommon.cpp b/src/coreclr/src/jit/codegencommon.cpp index ec8a0abee07fd..cadfd3e276e16 100644 --- a/src/coreclr/src/jit/codegencommon.cpp +++ b/src/coreclr/src/jit/codegencommon.cpp @@ -2355,25 +2355,25 @@ void CodeGen::genEmitMachineCode() } #endif -#if EMIT_TRACK_STACK_DEPTH +#if EMIT_TRACK_STACK_DEPTH && defined(DEBUG) && !defined(OSX_ARM64_ABI) // Check our max stack level. Needed for fgAddCodeRef(). // We need to relax the assert as our estimation won't include code-gen // stack changes (which we know don't affect fgAddCodeRef()). // NOTE: after emitEndCodeGen (including here), emitMaxStackDepth is a // count of DWORD-sized arguments, NOT argument size in bytes. { - unsigned maxAllowedStackDepth = compiler->fgPtrArgCntMax + // Max number of pointer-sized stack arguments. - compiler->compHndBBtabCount + // Return address for locally-called finallys - genTypeStSz(TYP_LONG) + // longs/doubles may be transferred via stack, etc + unsigned maxAllowedStackDepth = compiler->fgGetPtrArgCntMax() + // Max number of pointer-sized stack arguments. + compiler->compHndBBtabCount + // Return address for locally-called finallys + genTypeStSz(TYP_LONG) + // longs/doubles may be transferred via stack, etc (compiler->compTailCallUsed ? 4 : 0); // CORINFO_HELP_TAILCALL args #if defined(UNIX_X86_ABI) // Convert maxNestedAlignment to DWORD count before adding to maxAllowedStackDepth. assert(maxNestedAlignment % sizeof(int) == 0); maxAllowedStackDepth += maxNestedAlignment / sizeof(int); #endif - noway_assert(GetEmitter()->emitMaxStackDepth <= maxAllowedStackDepth); + assert(GetEmitter()->emitMaxStackDepth <= maxAllowedStackDepth); } -#endif // EMIT_TRACK_STACK_DEPTH +#endif // EMIT_TRACK_STACK_DEPTH && DEBUG *nativeSizeOfCode = codeSize; compiler->info.compNativeCodeSize = (UNATIVE_OFFSET)codeSize; @@ -6763,15 +6763,15 @@ void CodeGen::genReportGenericContextArg(regNumber initReg, bool* pInitRegZeroed if (isFramePointerUsed()) { #if defined(TARGET_ARM) - // lvStkOffs is always valid for incoming stack-arguments, even if the argument + // GetStackOffset() is always valid for incoming stack-arguments, even if the argument // will become enregistered. // On Arm compiler->compArgSize doesn't include r11 and lr sizes and hence we need to add 2*REGSIZE_BYTES - noway_assert((2 * REGSIZE_BYTES <= varDsc->lvStkOffs) && - (size_t(varDsc->lvStkOffs) < compiler->compArgSize + 2 * REGSIZE_BYTES)); + noway_assert((2 * REGSIZE_BYTES <= varDsc->GetStackOffset()) && + (size_t(varDsc->GetStackOffset()) < compiler->compArgSize + 2 * REGSIZE_BYTES)); #else - // lvStkOffs is always valid for incoming stack-arguments, even if the argument + // GetStackOffset() is always valid for incoming stack-arguments, even if the argument // will become enregistered. - noway_assert((0 < varDsc->lvStkOffs) && (size_t(varDsc->lvStkOffs) < compiler->compArgSize)); + noway_assert((0 < varDsc->GetStackOffset()) && (size_t(varDsc->GetStackOffset()) < compiler->compArgSize)); #endif } @@ -6781,7 +6781,8 @@ void CodeGen::genReportGenericContextArg(regNumber initReg, bool* pInitRegZeroed *pInitRegZeroed = false; // mov reg, [compiler->info.compTypeCtxtArg] - GetEmitter()->emitIns_R_AR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, reg, genFramePointerReg(), varDsc->lvStkOffs); + GetEmitter()->emitIns_R_AR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, reg, genFramePointerReg(), + varDsc->GetStackOffset()); regSet.verifyRegUsed(reg); } @@ -7459,8 +7460,8 @@ void CodeGen::genFnProlog() continue; } - signed int loOffs = varDsc->lvStkOffs; - signed int hiOffs = varDsc->lvStkOffs + compiler->lvaLclSize(varNum); + signed int loOffs = varDsc->GetStackOffset(); + signed int hiOffs = varDsc->GetStackOffset() + compiler->lvaLclSize(varNum); /* We need to know the offset range of tracked stack GC refs */ /* We assume that the GC reference can be anywhere in the TYP_STRUCT */ @@ -7860,7 +7861,7 @@ void CodeGen::genFnProlog() #else // mov [lvaStubArgumentVar], EAX GetEmitter()->emitIns_AR_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, REG_SECRET_STUB_PARAM, genFramePointerReg(), - compiler->lvaTable[compiler->lvaStubArgumentVar].lvStkOffs); + compiler->lvaTable[compiler->lvaStubArgumentVar].GetStackOffset()); #endif assert(intRegState.rsCalleeRegArgMaskLiveIn & RBM_SECRET_STUB_PARAM); @@ -8109,7 +8110,7 @@ void CodeGen::genFnProlog() LclVarDsc* lastArg = &compiler->lvaTable[compiler->info.compArgsCount - 1]; noway_assert(!lastArg->lvRegister); - signed offset = lastArg->lvStkOffs; + signed offset = lastArg->GetStackOffset(); assert(offset != BAD_STK_OFFS); noway_assert(lastArg->lvFramePointerBased); @@ -10617,8 +10618,8 @@ void CodeGen::genSetScopeInfo(unsigned which, // Can't check compiler->lvaTable[varNum].lvOnFrame as we don't set it for // arguments of vararg functions to avoid reporting them to GC. noway_assert(!compiler->lvaTable[varNum].lvRegister); - unsigned cookieOffset = compiler->lvaTable[compiler->lvaVarargsHandleArg].lvStkOffs; - unsigned varOffset = compiler->lvaTable[varNum].lvStkOffs; + unsigned cookieOffset = compiler->lvaTable[compiler->lvaVarargsHandleArg].GetStackOffset(); + unsigned varOffset = compiler->lvaTable[varNum].GetStackOffset(); noway_assert(cookieOffset < varOffset); unsigned offset = varOffset - cookieOffset; diff --git a/src/coreclr/src/jit/codegenlinear.cpp b/src/coreclr/src/jit/codegenlinear.cpp index cf0d1cd149a00..557e290504cab 100644 --- a/src/coreclr/src/jit/codegenlinear.cpp +++ b/src/coreclr/src/jit/codegenlinear.cpp @@ -1822,7 +1822,7 @@ void CodeGen::genPutArgStkFieldList(GenTreePutArgStk* putArgStk, unsigned outArg // We can't write beyond the arg area unless this is a tail call, in which case we use // the first stack arg as the base of the incoming arg area. #ifdef DEBUG - size_t areaSize = compiler->lvaLclSize(outArgVarNum); + unsigned areaSize = compiler->lvaLclSize(outArgVarNum); #if FEATURE_FASTTAILCALL if (putArgStk->gtCall->IsFastTailCall()) { diff --git a/src/coreclr/src/jit/codegenxarch.cpp b/src/coreclr/src/jit/codegenxarch.cpp index 6bca839d10514..1199ef6afb42d 100644 --- a/src/coreclr/src/jit/codegenxarch.cpp +++ b/src/coreclr/src/jit/codegenxarch.cpp @@ -8271,7 +8271,7 @@ void CodeGen::genCreateAndStoreGCInfoX64(unsigned codeSize, unsigned prologSize unsigned reversePInvokeFrameVarNumber = compiler->lvaReversePInvokeFrameVar; assert(reversePInvokeFrameVarNumber != BAD_VAR_NUM && reversePInvokeFrameVarNumber < compiler->lvaRefCount); LclVarDsc& reversePInvokeFrameVar = compiler->lvaTable[reversePInvokeFrameVarNumber]; - gcInfoEncoder->SetReversePInvokeFrameSlot(reversePInvokeFrameVar.lvStkOffs); + gcInfoEncoder->SetReversePInvokeFrameSlot(reversePInvokeFrameVar.GetStackOffset()); } gcInfoEncoder->Build(); @@ -8547,7 +8547,7 @@ void CodeGen::genProfilingEnterCallback(regNumber initReg, bool* pInitRegZeroed) EA_UNKNOWN); // retSize // Check that we have place for the push. - assert(compiler->fgPtrArgCntMax >= 1); + assert(compiler->fgGetPtrArgCntMax() >= 1); #if defined(UNIX_X86_ABI) // Restoring alignment manually. This is similar to CodeGen::genRemoveAlignmentAfterCall @@ -8628,7 +8628,7 @@ void CodeGen::genProfilingLeaveCallback(unsigned helper) genEmitHelperCall(helper, argSize, EA_UNKNOWN /* retSize */); // Check that we have place for the push. - assert(compiler->fgPtrArgCntMax >= 1); + assert(compiler->fgGetPtrArgCntMax() >= 1); #if defined(UNIX_X86_ABI) // Restoring alignment manually. This is similar to CodeGen::genRemoveAlignmentAfterCall diff --git a/src/coreclr/src/jit/compiler.cpp b/src/coreclr/src/jit/compiler.cpp index 8b0e049c38d76..610b5224b1196 100644 --- a/src/coreclr/src/jit/compiler.cpp +++ b/src/coreclr/src/jit/compiler.cpp @@ -4921,10 +4921,13 @@ void Compiler::compCompile(void** methodCodePtr, ULONG* methodCodeSize, JitFlags m_pLowering = new (this, CMK_LSRA) Lowering(this, m_pLinearScan); // PHASE_LOWERING m_pLowering->Run(); - // Set stack levels - // +#if !defined(OSX_ARM64_ABI) + // Set stack levels; this information is necessary for x86 + // but on other platforms it is used only in asserts. + // TODO: do not run it in release on other platforms, see https://github.com/dotnet/runtime/issues/42673. StackLevelSetter stackLevelSetter(this); stackLevelSetter.Run(); +#endif // !OSX_ARM64_ABI // We can not add any new tracked variables after this point. lvaTrackedFixed = true; @@ -5028,7 +5031,7 @@ void Compiler::generatePatchpointInfo() assert(varDsc->lvFramePointerBased); // Record FramePtr relative offset (no localloc yet) - patchpointInfo->SetOffset(lclNum, varDsc->lvStkOffs); + patchpointInfo->SetOffset(lclNum, varDsc->GetStackOffset()); // Note if IL stream contained an address-of that potentially leads to exposure. // This bit of IL may be skipped by OSR partial importation. @@ -5061,7 +5064,7 @@ void Compiler::generatePatchpointInfo() { assert(lvaGSSecurityCookie != BAD_VAR_NUM); LclVarDsc* const varDsc = lvaGetDesc(lvaGSSecurityCookie); - patchpointInfo->SetSecurityCookieOffset(varDsc->lvStkOffs); + patchpointInfo->SetSecurityCookieOffset(varDsc->GetStackOffset()); JITDUMP("--OSR-- security cookie V%02u offset is FP %d\n", lvaGSSecurityCookie, patchpointInfo->SecurityCookieOffset()); } diff --git a/src/coreclr/src/jit/compiler.h b/src/coreclr/src/jit/compiler.h index 0f1f09dc71b07..371782228685d 100644 --- a/src/coreclr/src/jit/compiler.h +++ b/src/coreclr/src/jit/compiler.h @@ -805,7 +805,20 @@ class LclVarDsc void incLvRefCntWtd(BasicBlock::weight_t delta, RefCountState state = RCS_NORMAL); void setLvRefCntWtd(BasicBlock::weight_t newValue, RefCountState state = RCS_NORMAL); - int lvStkOffs; // stack offset of home +private: + int lvStkOffs; // stack offset of home in bytes. + +public: + int GetStackOffset() const + { + return lvStkOffs; + } + + void SetStackOffset(int offset) + { + lvStkOffs = offset; + } + unsigned lvExactSize; // (exact) size of the type in bytes // Is this a promoted struct? @@ -1446,11 +1459,28 @@ struct fgArgTabEntry unsigned structFloatRegs; #endif // UNIX_AMD64_ABI +#if defined(DEBUG_ARG_SLOTS) + // These fields were used to calculate stack size in stack slots for arguments + // but now they are replaced by precise `m_byteOffset/m_byteSize` because of + // arm64 apple abi requirements. + // A slot is a pointer sized region in the OutArg area. unsigned slotNum; // When an argument is passed in the OutArg area this is the slot number in the OutArg area unsigned numSlots; // Count of number of slots that this argument uses +#endif // DEBUG_ARG_SLOTS + + // Return number of stack slots that this argument is taking. + // TODO-Cleanup: this function does not align with arm64 apple model, + // delete it. In most cases we just want to know if we it is using stack or not + // but in some cases we are checking if it is a multireg arg, like: + // `numRegs + GetStackSlotsNumber() > 1` that is harder to replace. + // + unsigned GetStackSlotsNumber() const + { + return roundUp(GetStackByteSize(), TARGET_POINTER_SIZE) / TARGET_POINTER_SIZE; + } - unsigned alignment; // 1 or 2 (slots/registers) + unsigned byteAlignment; // usually 8 or 16 bytes (slots/registers). private: unsigned _lateArgInx; // index into gtCallLateArgs list; UINT_MAX if this is not a late arg. public: @@ -1600,9 +1630,19 @@ struct fgArgTabEntry return 0; } - unsigned stackSize() const + // Get the number of bytes that this argument is occupying on the stack. + unsigned GetStackByteSize() const { - return (TARGET_POINTER_SIZE * this->numSlots); + if (!IsSplit() && numRegs > 0) + { + return 0; + } + + assert(!IsHfaArg() || !IsSplit()); + + assert(GetByteSize() > TARGET_POINTER_SIZE * numRegs); + unsigned stackByteSize = GetByteSize() - TARGET_POINTER_SIZE * numRegs; + return GetByteSize() - TARGET_POINTER_SIZE * numRegs; } var_types GetHfaType() const @@ -1620,7 +1660,7 @@ struct fgArgTabEntry if (type != TYP_UNDEF) { // We must already have set the passing mode. - assert(numRegs != 0 || numSlots != 0); + assert(numRegs != 0 || GetStackByteSize() != 0); // We originally set numRegs according to the size of the struct, but if the size of the // hfaType is not the same as the pointer size, we need to correct it. // Note that hfaSlots is the number of registers we will use. For ARM, that is twice @@ -1695,11 +1735,13 @@ struct fgArgTabEntry #endif } - bool isSingleRegOrSlot() const + // Can we replace the struct type of this node with a primitive type for argument passing? + bool TryPassAsPrimitive() const { - return !IsSplit() && ((numRegs == 1) || (numSlots == 1)); + return !IsSplit() && ((numRegs == 1) || (m_byteSize <= TARGET_POINTER_SIZE)); } +#if defined(DEBUG_ARG_SLOTS) // Returns the number of "slots" used, where for this purpose a // register counts as a slot. unsigned getSlotCount() const @@ -1720,7 +1762,9 @@ struct fgArgTabEntry } return numSlots + numRegs; } +#endif +#if defined(DEBUG_ARG_SLOTS) // Returns the size as a multiple of pointer-size. // For targets without HFAs, this is the same as getSlotCount(). unsigned getSize() const @@ -1756,6 +1800,43 @@ struct fgArgTabEntry return size; } +#endif // DEBUG && !OSX_ARM64_ABI + +private: + unsigned m_byteOffset; + unsigned m_byteSize; + +public: + void SetByteOffset(unsigned byteOffset) + { + DEBUG_ARG_SLOTS_ASSERT(byteOffset / TARGET_POINTER_SIZE == slotNum); + m_byteOffset = byteOffset; + } + + unsigned GetByteOffset() const + { + DEBUG_ARG_SLOTS_ASSERT(m_byteOffset / TARGET_POINTER_SIZE == slotNum); + return m_byteOffset; + } + + void SetByteSize(unsigned byteSize) + { +#if defined(DEBUG_ARG_SLOTS) + assert(byteAlignment != 0); + if (!isStruct) + { + const unsigned alignedByteSize = roundUp(byteSize, byteAlignment); + assert(alignedByteSize == getSlotCount() * TARGET_POINTER_SIZE); + } +#endif + m_byteSize = byteSize; + } + + unsigned GetByteSize() const + { + return m_byteSize; + } + // Set the register numbers for a multireg argument. // There's nothing to do on x64/Ux because the structDesc has already been used to set the // register numbers. @@ -1785,6 +1866,7 @@ struct fgArgTabEntry #endif // FEATURE_MULTIREG_ARGS && !defined(UNIX_AMD64_ABI) } +#ifdef DEBUG // Check that the value of 'isStruct' is consistent. // A struct arg must be one of the following: // - A node of struct type, @@ -1802,7 +1884,8 @@ struct fgArgTabEntry // This is the case where we are passing a struct as a primitive type. // On most targets, this is always a single register or slot. // However, on ARM this could be two slots if it is TYP_DOUBLE. - bool isPassedAsPrimitiveType = ((numRegs == 1) || ((numRegs == 0) && (numSlots == 1))); + bool isPassedAsPrimitiveType = + ((numRegs == 1) || ((numRegs == 0) && (GetByteSize() <= TARGET_POINTER_SIZE))); #ifdef TARGET_ARM if (!isPassedAsPrimitiveType) { @@ -1821,7 +1904,6 @@ struct fgArgTabEntry } } -#ifdef DEBUG void Dump() const; #endif }; @@ -1834,11 +1916,14 @@ struct fgArgTabEntry class fgArgInfo { - Compiler* compiler; // Back pointer to the compiler instance so that we can allocate memory - GenTreeCall* callTree; // Back pointer to the GT_CALL node for this fgArgInfo - unsigned argCount; // Updatable arg count value - unsigned nextSlotNum; // Updatable slot count value - unsigned stkLevel; // Stack depth when we make this call (for x86) + Compiler* compiler; // Back pointer to the compiler instance so that we can allocate memory + GenTreeCall* callTree; // Back pointer to the GT_CALL node for this fgArgInfo + unsigned argCount; // Updatable arg count value +#if defined(DEBUG_ARG_SLOTS) + unsigned nextSlotNum; // Updatable slot count value +#endif + unsigned nextStackByteOffset; + unsigned stkLevel; // Stack depth when we make this call (for x86) #if defined(UNIX_X86_ABI) bool alignmentDone; // Updateable flag, set to 'true' after we've done any required alignment. @@ -1873,7 +1958,8 @@ class fgArgInfo GenTreeCall::Use* use, regNumber regNum, unsigned numRegs, - unsigned alignment, + unsigned byteSize, + unsigned byteAlignment, bool isStruct, bool isVararg = false); @@ -1883,7 +1969,8 @@ class fgArgInfo GenTreeCall::Use* use, regNumber regNum, unsigned numRegs, - unsigned alignment, + unsigned byteSize, + unsigned byteAlignment, const bool isStruct, const bool isVararg, const regNumber otherRegNum, @@ -1896,7 +1983,8 @@ class fgArgInfo GenTree* node, GenTreeCall::Use* use, unsigned numSlots, - unsigned alignment, + unsigned byteSize, + unsigned byteAlignment, bool isStruct, bool isVararg = false); @@ -1922,10 +2010,19 @@ class fgArgInfo { return argTable; } + +#if defined(DEBUG_ARG_SLOTS) unsigned GetNextSlotNum() const { return nextSlotNum; } +#endif + + unsigned GetNextSlotByteOffset() const + { + return nextStackByteOffset; + } + bool HasRegArgs() const { return hasRegArgs; @@ -3198,6 +3295,12 @@ class Compiler unsigned lvaOutgoingArgSpaceVar; // dummy TYP_LCLBLK var for fixed outgoing argument space PhasedVar lvaOutgoingArgSpaceSize; // size of fixed outgoing argument space #endif // FEATURE_FIXED_OUT_ARGS + + static unsigned GetOutgoingArgByteSize(unsigned sizeWithoutPadding) + { + return roundUp(sizeWithoutPadding, TARGET_POINTER_SIZE); + } + // Variable representing the return address. The helper-based tailcall // mechanism passes the address of the return address to a runtime helper // where it is used to detect tail-call chains. @@ -5713,8 +5816,8 @@ class Compiler SpecialCodeKind acdKind; // what kind of a special block is this? #if !FEATURE_FIXED_OUT_ARGS bool acdStkLvlInit; // has acdStkLvl value been already set? - unsigned acdStkLvl; -#endif // !FEATURE_FIXED_OUT_ARGS + unsigned acdStkLvl; // stack level in stack slots. +#endif // !FEATURE_FIXED_OUT_ARGS }; private: @@ -9127,8 +9230,8 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX unsigned compArgsCount; // Number of arguments (incl. implicit and hidden) #if FEATURE_FASTTAILCALL - size_t compArgStackSize; // Incoming argument stack size in bytes -#endif // FEATURE_FASTTAILCALL + unsigned compArgStackSize; // Incoming argument stack size in bytes +#endif // FEATURE_FASTTAILCALL unsigned compRetBuffArg; // position of hidden return param var (0, 1) (BAD_VAR_NUM means not present); int compTypeCtxtArg; // position of hidden param for type context for generic code (CORINFO_CALLCONV_PARAMTYPE) diff --git a/src/coreclr/src/jit/compiler.hpp b/src/coreclr/src/jit/compiler.hpp index 3fb8413b820b0..8b80eb04b24b8 100644 --- a/src/coreclr/src/jit/compiler.hpp +++ b/src/coreclr/src/jit/compiler.hpp @@ -2117,7 +2117,7 @@ inline } #endif // DEBUG - varOffset = varDsc->lvStkOffs; + varOffset = varDsc->GetStackOffset(); } else // Its a spill-temp { diff --git a/src/coreclr/src/jit/emit.cpp b/src/coreclr/src/jit/emit.cpp index 30f2c640f36c7..b4b9d159055e5 100644 --- a/src/coreclr/src/jit/emit.cpp +++ b/src/coreclr/src/jit/emit.cpp @@ -4802,7 +4802,7 @@ unsigned emitter::emitEndCodeGen(Compiler* comp, } #endif // FEATURE_FIXED_OUT_ARGS - int offs = dsc->lvStkOffs; + int offs = dsc->GetStackOffset(); /* Is it within the interesting range of offsets */ diff --git a/src/coreclr/src/jit/gcencode.cpp b/src/coreclr/src/jit/gcencode.cpp index 6cbe702524282..d315ef9a430db 100644 --- a/src/coreclr/src/jit/gcencode.cpp +++ b/src/coreclr/src/jit/gcencode.cpp @@ -1597,7 +1597,7 @@ size_t GCInfo::gcInfoBlockHdrSave( if (compiler->getNeedsGSSecurityCookie()) { assert(compiler->lvaGSSecurityCookie != BAD_VAR_NUM); - int stkOffs = compiler->lvaTable[compiler->lvaGSSecurityCookie].lvStkOffs; + int stkOffs = compiler->lvaTable[compiler->lvaGSSecurityCookie].GetStackOffset(); header->gsCookieOffset = compiler->isFramePointerUsed() ? -stkOffs : stkOffs; assert(header->gsCookieOffset != INVALID_GS_COOKIE_OFFSET); } @@ -2198,7 +2198,7 @@ size_t GCInfo::gcMakeRegPtrTable(BYTE* dest, int mask, const InfoHdr& header, un continue; } - int offset = varDsc->lvStkOffs; + int offset = varDsc->GetStackOffset(); #if DOUBLE_ALIGN // For genDoubleAlign(), locals are addressed relative to ESP and // arguments are addressed relative to EBP. @@ -2247,7 +2247,7 @@ size_t GCInfo::gcMakeRegPtrTable(BYTE* dest, int mask, const InfoHdr& header, un continue; } - unsigned offset = varDsc->lvStkOffs + i * TARGET_POINTER_SIZE; + unsigned offset = varDsc->GetStackOffset() + i * TARGET_POINTER_SIZE; #if DOUBLE_ALIGN // For genDoubleAlign(), locals are addressed relative to ESP and // arguments are addressed relative to EBP. @@ -2352,7 +2352,7 @@ size_t GCInfo::gcMakeRegPtrTable(BYTE* dest, int mask, const InfoHdr& header, un assert(compiler->lvaTable[compiler->info.compThisArg].TypeGet() == TYP_REF); - unsigned varOffs = compiler->lvaTable[compiler->info.compThisArg].lvStkOffs; + unsigned varOffs = compiler->lvaTable[compiler->info.compThisArg].GetStackOffset(); /* For negative stack offsets we must reset the low bits, * take abs and then set them back */ @@ -4169,18 +4169,19 @@ void GCInfo::gcMakeRegPtrTable( // No need to hash/lookup untracked GC refs; just grab a new Slot Id. if (mode == MAKE_REG_PTR_MODE_ASSIGN_SLOTS) { - gcInfoEncoderWithLog->GetStackSlotId(varDsc->lvStkOffs, flags, stackSlotBase); + gcInfoEncoderWithLog->GetStackSlotId(varDsc->GetStackOffset(), flags, stackSlotBase); } } else { - StackSlotIdKey sskey(varDsc->lvStkOffs, (stackSlotBase == GC_FRAMEREG_REL), flags); + StackSlotIdKey sskey(varDsc->GetStackOffset(), (stackSlotBase == GC_FRAMEREG_REL), flags); GcSlotId varSlotId; if (mode == MAKE_REG_PTR_MODE_ASSIGN_SLOTS) { if (!m_stackSlotMap->Lookup(sskey, &varSlotId)) { - varSlotId = gcInfoEncoderWithLog->GetStackSlotId(varDsc->lvStkOffs, flags, stackSlotBase); + varSlotId = + gcInfoEncoderWithLog->GetStackSlotId(varDsc->GetStackOffset(), flags, stackSlotBase); m_stackSlotMap->Set(sskey, varSlotId); } } @@ -4201,7 +4202,7 @@ void GCInfo::gcMakeRegPtrTable( continue; } - int offset = varDsc->lvStkOffs + i * TARGET_POINTER_SIZE; + int offset = varDsc->GetStackOffset() + i * TARGET_POINTER_SIZE; #if DOUBLE_ALIGN // For genDoubleAlign(), locals are addressed relative to ESP and // arguments are addressed relative to EBP. diff --git a/src/coreclr/src/jit/gcinfo.cpp b/src/coreclr/src/jit/gcinfo.cpp index a27c41c50e634..fb253360e13fb 100644 --- a/src/coreclr/src/jit/gcinfo.cpp +++ b/src/coreclr/src/jit/gcinfo.cpp @@ -394,7 +394,7 @@ void GCInfo::gcCountForHeader(UNALIGNED unsigned int* pUntrackedCount, UNALIGNED #ifdef DEBUG if (compiler->verbose) { - int offs = varDsc->lvStkOffs; + int offs = varDsc->GetStackOffset(); printf("GCINFO: untrckd %s lcl at [%s", varTypeGCstring(varDsc->TypeGet()), compiler->GetEmitter()->emitGetFrameReg()); diff --git a/src/coreclr/src/jit/gentree.cpp b/src/coreclr/src/jit/gentree.cpp index 12e61e8fc18eb..5da7ce2a73e7a 100644 --- a/src/coreclr/src/jit/gentree.cpp +++ b/src/coreclr/src/jit/gentree.cpp @@ -11114,8 +11114,6 @@ void Compiler::gtDispLeaf(GenTree* tree, IndentStack* indentStack) switch (tree->gtOper) { - unsigned varNum; - LclVarDsc* varDsc; case GT_LCL_FLD: case GT_LCL_FLD_ADDR: @@ -11127,9 +11125,10 @@ void Compiler::gtDispLeaf(GenTree* tree, IndentStack* indentStack) case GT_LCL_VAR: case GT_LCL_VAR_ADDR: case GT_STORE_LCL_VAR: + { printf(" "); - varNum = tree->AsLclVarCommon()->GetLclNum(); - varDsc = &lvaTable[varNum]; + const unsigned varNum = tree->AsLclVarCommon()->GetLclNum(); + const LclVarDsc* varDsc = lvaGetDesc(varNum); gtDispLclVar(varNum); if (tree->AsLclVarCommon()->HasSsaName()) { @@ -11170,8 +11169,6 @@ void Compiler::gtDispLeaf(GenTree* tree, IndentStack* indentStack) } else { - CORINFO_CLASS_HANDLE typeHnd = varDsc->GetStructHnd(); - CORINFO_FIELD_HANDLE fldHnd; for (unsigned i = varDsc->lvFieldLclStart; i < varDsc->lvFieldLclStart + varDsc->lvFieldCnt; ++i) { @@ -11185,7 +11182,9 @@ void Compiler::gtDispLeaf(GenTree* tree, IndentStack* indentStack) else #endif // !defined(TARGET_64BIT) { - fldHnd = info.compCompHnd->getFieldInClass(typeHnd, fieldVarDsc->lvFldOrdinal); + CORINFO_CLASS_HANDLE typeHnd = varDsc->GetStructHnd(); + CORINFO_FIELD_HANDLE fldHnd = + info.compCompHnd->getFieldInClass(typeHnd, fieldVarDsc->lvFldOrdinal); fieldName = eeGetFieldName(fldHnd); } @@ -11217,7 +11216,8 @@ void Compiler::gtDispLeaf(GenTree* tree, IndentStack* indentStack) printf(" (last use)"); } } - break; + } + break; case GT_JMP: { diff --git a/src/coreclr/src/jit/jit.h b/src/coreclr/src/jit/jit.h index 70296152db539..1c8a9e70d996b 100644 --- a/src/coreclr/src/jit/jit.h +++ b/src/coreclr/src/jit/jit.h @@ -245,6 +245,23 @@ #define UNIX_AMD64_ABI_ONLY(x) #endif // defined(UNIX_AMD64_ABI) +#if defined(DEBUG) && !defined(OSX_ARM64_ABI) +// On all platforms except Arm64 OSX arguments on the stack are taking +// register size slots. On these platforms we could check that stack slots count +// matchs out new byte size calculations. +#define DEBUG_ARG_SLOTS +#endif + +#if defined(DEBUG_ARG_SLOTS) +#define DEBUG_ARG_SLOTS_ARG(x) , x +#define DEBUG_ARG_SLOTS_ONLY(x) x +#define DEBUG_ARG_SLOTS_ASSERT(x) assert(x) +#else +#define DEBUG_ARG_SLOTS_ARG(x) +#define DEBUG_ARG_SLOTS_ONLY(x) +#define DEBUG_ARG_SLOTS_ASSERT(x) +#endif + #if defined(UNIX_AMD64_ABI) || !defined(TARGET_64BIT) || defined(TARGET_ARM64) #define FEATURE_PUT_STRUCT_ARG_STK 1 #define PUT_STRUCT_ARG_STK_ONLY_ARG(x) , x diff --git a/src/coreclr/src/jit/lclvars.cpp b/src/coreclr/src/jit/lclvars.cpp index 4b9aa6398e406..975a359466ce0 100644 --- a/src/coreclr/src/jit/lclvars.cpp +++ b/src/coreclr/src/jit/lclvars.cpp @@ -1014,7 +1014,7 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo) #endif // TARGET_XXX #if FEATURE_FASTTAILCALL - varDsc->lvStkOffs = varDscInfo->stackArgSize; + varDsc->SetStackOffset(varDscInfo->stackArgSize); varDscInfo->stackArgSize += roundUp(argSize, TARGET_POINTER_SIZE); #endif // FEATURE_FASTTAILCALL } @@ -1030,7 +1030,7 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo) if (info.compIsVarArgs || (isHfaArg && varDsc->lvHfaSlots() != 1) || isSoftFPPreSpill) { #if defined(TARGET_X86) - varDsc->lvStkOffs = compArgSize; + varDsc->SetStackOffset(compArgSize); #else // !TARGET_X86 // TODO-CQ: We shouldn't have to go as far as to declare these // address-exposed -- DoNotEnregister should suffice. @@ -1047,6 +1047,7 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo) } } // for each user arg + compArgSize = roundUp(compArgSize, TARGET_POINTER_SIZE); #ifdef TARGET_ARM if (doubleAlignMask != RBM_NONE) @@ -1117,7 +1118,7 @@ void Compiler::lvaInitGenericsCtxt(InitVarDscInfo* varDscInfo) // returns false. varDsc->lvOnFrame = true; #if FEATURE_FASTTAILCALL - varDsc->lvStkOffs = varDscInfo->stackArgSize; + varDsc->SetStackOffset(varDscInfo->stackArgSize); varDscInfo->stackArgSize += TARGET_POINTER_SIZE; #endif // FEATURE_FASTTAILCALL } @@ -1126,7 +1127,7 @@ void Compiler::lvaInitGenericsCtxt(InitVarDscInfo* varDscInfo) #if defined(TARGET_X86) if (info.compIsVarArgs) - varDsc->lvStkOffs = compArgSize; + varDsc->SetStackOffset(compArgSize); #endif // TARGET_X86 varDscInfo->varNum++; @@ -1187,7 +1188,7 @@ void Compiler::lvaInitVarArgsHandle(InitVarDscInfo* varDscInfo) // returns false. varDsc->lvOnFrame = true; #if FEATURE_FASTTAILCALL - varDsc->lvStkOffs = varDscInfo->stackArgSize; + varDsc->SetStackOffset(varDscInfo->stackArgSize); varDscInfo->stackArgSize += TARGET_POINTER_SIZE; #endif // FEATURE_FASTTAILCALL } @@ -1200,7 +1201,7 @@ void Compiler::lvaInitVarArgsHandle(InitVarDscInfo* varDscInfo) varDscInfo->varDsc++; #if defined(TARGET_X86) - varDsc->lvStkOffs = compArgSize; + varDsc->SetStackOffset(compArgSize); // Allocate a temp to point at the beginning of the args @@ -1339,7 +1340,7 @@ void Compiler::lvaInitVarDsc(LclVarDsc* varDsc, #endif #ifdef DEBUG - varDsc->lvStkOffs = BAD_STK_OFFS; + varDsc->SetStackOffset(BAD_STK_OFFS); #endif #if FEATURE_MULTIREG_ARGS @@ -4972,13 +4973,13 @@ void Compiler::lvaFixVirtualFrameOffsets() varDsc = &lvaTable[lvaPSPSym]; assert(varDsc->lvFramePointerBased); // We always access it RBP-relative. assert(!varDsc->lvMustInit); // It is never "must init". - varDsc->lvStkOffs = codeGen->genCallerSPtoInitialSPdelta() + lvaLclSize(lvaOutgoingArgSpaceVar); + varDsc->SetStackOffset(codeGen->genCallerSPtoInitialSPdelta() + lvaLclSize(lvaOutgoingArgSpaceVar)); // With OSR the new frame RBP points at the base of the new frame, but the virtual offsets // are from the base of the old frame. Adjust. if (opts.IsOSR()) { - varDsc->lvStkOffs -= info.compPatchpointInfo->FpToSpDelta(); + varDsc->SetStackOffset(varDsc->GetStackOffset() - info.compPatchpointInfo->FpToSpDelta()); } } #endif @@ -5070,29 +5071,30 @@ void Compiler::lvaFixVirtualFrameOffsets() if (doAssignStkOffs) { - JITDUMP("-- V%02u was %d, now %d\n", lclNum, varDsc->lvStkOffs, varDsc->lvStkOffs + delta); - varDsc->lvStkOffs += delta; + JITDUMP("-- V%02u was %d, now %d\n", lclNum, varDsc->GetStackOffset(), varDsc->GetStackOffset() + delta); + varDsc->SetStackOffset(varDsc->GetStackOffset() + delta); #if DOUBLE_ALIGN if (genDoubleAlign() && !codeGen->isFramePointerUsed()) { if (varDsc->lvFramePointerBased) { - varDsc->lvStkOffs -= delta; + varDsc->SetStackOffset(varDsc->GetStackOffset() - delta); // We need to re-adjust the offsets of the parameters so they are EBP // relative rather than stack/frame pointer relative - varDsc->lvStkOffs += (2 * TARGET_POINTER_SIZE); // return address and pushed EBP + varDsc->SetStackOffset(varDsc->GetStackOffset() + + (2 * TARGET_POINTER_SIZE)); // return address and pushed EBP - noway_assert(varDsc->lvStkOffs >= FIRST_ARG_STACK_OFFS); + noway_assert(varDsc->GetStackOffset() >= FIRST_ARG_STACK_OFFS); } } #endif // On System V environments the stkOffs could be 0 for params passed in registers. // // For normal methods only EBP relative references can have negative offsets. - assert(codeGen->isFramePointerUsed() || varDsc->lvStkOffs >= 0); + assert(codeGen->isFramePointerUsed() || varDsc->GetStackOffset() >= 0); } } @@ -5108,8 +5110,8 @@ void Compiler::lvaFixVirtualFrameOffsets() if (lvaOutgoingArgSpaceVar != BAD_VAR_NUM) { - varDsc = &lvaTable[lvaOutgoingArgSpaceVar]; - varDsc->lvStkOffs = 0; + varDsc = &lvaTable[lvaOutgoingArgSpaceVar]; + varDsc->SetStackOffset(0); varDsc->lvFramePointerBased = false; varDsc->lvMustInit = false; } @@ -5126,7 +5128,7 @@ void Compiler::lvaFixVirtualFrameOffsets() assert(codeGen->isFramePointerUsed()); if (lvaRetAddrVar != BAD_VAR_NUM) { - lvaTable[lvaRetAddrVar].lvStkOffs = REGSIZE_BYTES; + lvaTable[lvaRetAddrVar].SetStackOffset(REGSIZE_BYTES); } #endif } @@ -5414,11 +5416,11 @@ int Compiler::lvaAssignVirtualFrameOffsetToArg(unsigned lclNum, if (varDsc->lvOnFrame) { // The offset for args needs to be set only for the stack homed arguments for System V. - varDsc->lvStkOffs = argOffs; + varDsc->SetStackOffset(argOffs); } else { - varDsc->lvStkOffs = 0; + varDsc->SetStackOffset(0); } } else @@ -5435,7 +5437,7 @@ int Compiler::lvaAssignVirtualFrameOffsetToArg(unsigned lclNum, // method, which fixes the offsets, based on frame pointer existence, existence of alloca instructions, ret // address pushed, ets. - varDsc->lvStkOffs = *callerArgOffset; + varDsc->SetStackOffset(*callerArgOffset); // Structs passed on stack could be of size less than TARGET_POINTER_SIZE. // Make sure they get at least TARGET_POINTER_SIZE on the stack - this is required for alignment. if (argSize > TARGET_POINTER_SIZE) @@ -5454,11 +5456,11 @@ int Compiler::lvaAssignVirtualFrameOffsetToArg(unsigned lclNum, if (varDsc->lvPromotedStruct()) { unsigned firstFieldNum = varDsc->lvFieldLclStart; - int offset = varDsc->lvStkOffs; + int offset = varDsc->GetStackOffset(); for (unsigned i = 0; i < varDsc->lvFieldCnt; i++) { LclVarDsc* fieldVarDsc = lvaGetDesc(firstFieldNum + i); - fieldVarDsc->lvStkOffs = offset; + fieldVarDsc->SetStackOffset(offset); offset += fieldVarDsc->lvFldOffset; } } @@ -5466,7 +5468,7 @@ int Compiler::lvaAssignVirtualFrameOffsetToArg(unsigned lclNum, else if (varDsc->lvIsStructField) { noway_assert(varDsc->lvParentLcl < lvaCount); - lvaTable[varDsc->lvParentLcl].lvStkOffs = varDsc->lvStkOffs; + lvaTable[varDsc->lvParentLcl].SetStackOffset(varDsc->GetStackOffset()); } if (Target::g_tgtArgOrder == Target::ARG_ORDER_R2L && !varDsc->lvIsRegArg) @@ -5525,7 +5527,7 @@ int Compiler::lvaAssignVirtualFrameOffsetToArg(unsigned lclNum, argOffs += TARGET_POINTER_SIZE; #elif defined(TARGET_AMD64) // Register arguments on AMD64 also takes stack space. (in the backing store) - varDsc->lvStkOffs = argOffs; + varDsc->SetStackOffset(argOffs); argOffs += TARGET_POINTER_SIZE; #elif defined(TARGET_ARM64) // Register arguments on ARM64 only take stack space when they have a frame home. @@ -5538,7 +5540,7 @@ int Compiler::lvaAssignVirtualFrameOffsetToArg(unsigned lclNum, { // This is a split struct. It will account for an extra (8 bytes) // of alignment. - varDsc->lvStkOffs += TARGET_POINTER_SIZE; + varDsc->SetStackOffset(varDsc->GetStackOffset() + TARGET_POINTER_SIZE); argOffs += TARGET_POINTER_SIZE; } } @@ -5546,7 +5548,7 @@ int Compiler::lvaAssignVirtualFrameOffsetToArg(unsigned lclNum, #elif defined(TARGET_ARM) // On ARM we spill the registers in codeGen->regSet.rsMaskPreSpillRegArg - // in the prolog, so we have to fill in lvStkOffs here + // in the prolog, so we have to do SetStackOffset() here // regMaskTP regMask = genRegMask(varDsc->GetArgReg()); if (codeGen->regSet.rsMaskPreSpillRegArg & regMask) @@ -5619,7 +5621,7 @@ int Compiler::lvaAssignVirtualFrameOffsetToArg(unsigned lclNum, // No alignment of argOffs required break; } - varDsc->lvStkOffs = argOffs; + varDsc->SetStackOffset(argOffs); argOffs += argSize; } #else // TARGET* @@ -5738,7 +5740,7 @@ int Compiler::lvaAssignVirtualFrameOffsetToArg(unsigned lclNum, } #endif // TARGET_ARM - varDsc->lvStkOffs = argOffs; + varDsc->SetStackOffset(argOffs); } // For struct promoted parameters we need to set the offsets for both LclVars. @@ -5750,9 +5752,9 @@ int Compiler::lvaAssignVirtualFrameOffsetToArg(unsigned lclNum, if ((varDsc->TypeGet() == TYP_LONG) && varDsc->lvPromoted) { noway_assert(varDsc->lvFieldCnt == 2); - fieldVarNum = varDsc->lvFieldLclStart; - lvaTable[fieldVarNum].lvStkOffs = varDsc->lvStkOffs; - lvaTable[fieldVarNum + 1].lvStkOffs = varDsc->lvStkOffs + genTypeSize(TYP_INT); + fieldVarNum = varDsc->lvFieldLclStart; + lvaTable[fieldVarNum].SetStackOffset(varDsc->GetStackOffset()); + lvaTable[fieldVarNum + 1].SetStackOffset(varDsc->GetStackOffset() + genTypeSize(TYP_INT)); } else #endif // !defined(TARGET_64BIT) @@ -5762,7 +5764,7 @@ int Compiler::lvaAssignVirtualFrameOffsetToArg(unsigned lclNum, for (unsigned i = 0; i < varDsc->lvFieldCnt; i++) { LclVarDsc* fieldVarDsc = lvaGetDesc(firstFieldNum + i); - fieldVarDsc->lvStkOffs = varDsc->lvStkOffs + fieldVarDsc->lvFldOffset; + fieldVarDsc->SetStackOffset(varDsc->GetStackOffset() + fieldVarDsc->lvFldOffset); } } @@ -5820,7 +5822,7 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() stkOffs -= TARGET_POINTER_SIZE; // return address; if (lvaRetAddrVar != BAD_VAR_NUM) { - lvaTable[lvaRetAddrVar].lvStkOffs = stkOffs; + lvaTable[lvaRetAddrVar].SetStackOffset(stkOffs); } // If we are an OSR method, we "inherit" the frame of the original method, @@ -5888,7 +5890,7 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() // top. if (lvaRetAddrVar != BAD_VAR_NUM) { - lvaTable[lvaRetAddrVar].lvStkOffs = stkOffs - REGSIZE_BYTES; + lvaTable[lvaRetAddrVar].SetStackOffset(stkOffs - REGSIZE_BYTES); } #endif @@ -6199,7 +6201,7 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() JITDUMP("---OSR--- V%02u (on old frame) old rbp offset %d old frame offset %d new virt offset %d\n", lclNum, originalOffset, originalFrameStkOffs, offset); - lvaTable[lclNum].lvStkOffs = offset; + lvaTable[lclNum].SetStackOffset(offset); continue; } @@ -6235,7 +6237,7 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() "virt offset %d\n", lclNum, originalOffset, originalFrameStkOffs, offset); - lvaTable[lclNum].lvStkOffs = offset; + lvaTable[lclNum].SetStackOffset(offset); } continue; @@ -6254,7 +6256,7 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() #endif // JIT32_GCENCODER lclNum == lvaRetAddrVar) { - assert(varDsc->lvStkOffs != BAD_STK_OFFS); + assert(varDsc->GetStackOffset() != BAD_STK_OFFS); continue; } @@ -6284,7 +6286,7 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() #if defined(TARGET_AMD64) && !defined(UNIX_AMD64_ABI) // On Windows AMD64 we can use the caller-reserved stack area that is already setup - assert(varDsc->lvStkOffs != BAD_STK_OFFS); + assert(varDsc->GetStackOffset() != BAD_STK_OFFS); continue; #else // !TARGET_AMD64 @@ -6301,8 +6303,8 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() if (info.compIsVarArgs && varDsc->GetArgReg() != theFixedRetBuffArgNum()) { // Stack offset to varargs (parameters) should point to home area which will be preallocated. - varDsc->lvStkOffs = - -initialStkOffs + genMapIntRegNumToRegArgNum(varDsc->GetArgReg()) * REGSIZE_BYTES; + const unsigned regArgNum = genMapIntRegNumToRegArgNum(varDsc->GetArgReg()); + varDsc->SetStackOffset(-initialStkOffs + regArgNum * REGSIZE_BYTES); continue; } @@ -6314,7 +6316,7 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() // if ((codeGen->regSet.rsMaskPreSpillRegs(false) & genRegMask(varDsc->GetArgReg())) != 0) { - assert(varDsc->lvStkOffs != BAD_STK_OFFS); + assert(varDsc->GetStackOffset() != BAD_STK_OFFS); continue; } #endif @@ -6409,8 +6411,8 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() { noway_assert(varDsc->lvFieldCnt == 1); // We only handle one field here - unsigned fieldVarNum = varDsc->lvFieldLclStart; - lvaTable[fieldVarNum].lvStkOffs = varDsc->lvStkOffs; + unsigned fieldVarNum = varDsc->lvFieldLclStart; + lvaTable[fieldVarNum].SetStackOffset(varDsc->GetStackOffset()); } #endif // TARGET_ARM64 #ifdef TARGET_ARM @@ -6421,9 +6423,9 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() { assert(varTypeIsLong(varDsc) && (varDsc->lvFieldCnt == 2)); - unsigned fieldVarNum = varDsc->lvFieldLclStart; - lvaTable[fieldVarNum].lvStkOffs = varDsc->lvStkOffs; - lvaTable[fieldVarNum + 1].lvStkOffs = varDsc->lvStkOffs + 4; + unsigned fieldVarNum = varDsc->lvFieldLclStart; + lvaTable[fieldVarNum].SetStackOffset(varDsc->GetStackOffset()); + lvaTable[fieldVarNum + 1].SetStackOffset(varDsc->GetStackOffset() + 4); } #endif // TARGET_ARM } @@ -6651,7 +6653,7 @@ int Compiler::lvaAllocLocalAndSetVirtualOffset(unsigned lclNum, unsigned size, i lvaIncrementFrameSize(size); stkOffs -= size; - lvaTable[lclNum].lvStkOffs = stkOffs; + lvaTable[lclNum].SetStackOffset(stkOffs); #ifdef DEBUG if (verbose) @@ -6866,7 +6868,7 @@ void Compiler::lvaAssignFrameOffsetsToPromotedStructs() noway_assert(varDsc->lvOnFrame); if (parentvarDsc->lvOnFrame) { - varDsc->lvStkOffs = parentvarDsc->lvStkOffs + varDsc->lvFldOffset; + varDsc->SetStackOffset(parentvarDsc->GetStackOffset() + varDsc->lvFldOffset); } else { @@ -7455,11 +7457,11 @@ int Compiler::lvaGetSPRelativeOffset(unsigned varNum) { // The stack offset is relative to the frame pointer, so convert it to be // relative to the stack pointer (which makes no sense for localloc functions). - spRelativeOffset = varDsc->lvStkOffs + codeGen->genSPtoFPdelta(); + spRelativeOffset = varDsc->GetStackOffset() + codeGen->genSPtoFPdelta(); } else { - spRelativeOffset = varDsc->lvStkOffs; + spRelativeOffset = varDsc->GetStackOffset(); } assert(spRelativeOffset >= 0); @@ -7479,7 +7481,7 @@ int Compiler::lvaGetCallerSPRelativeOffset(unsigned varNum) LclVarDsc* varDsc = lvaTable + varNum; assert(varDsc->lvOnFrame); - return lvaToCallerSPRelativeOffset(varDsc->lvStkOffs, varDsc->lvFramePointerBased); + return lvaToCallerSPRelativeOffset(varDsc->GetStackOffset(), varDsc->lvFramePointerBased); } int Compiler::lvaToCallerSPRelativeOffset(int offset, bool isFpBased) const @@ -7526,7 +7528,7 @@ int Compiler::lvaGetInitialSPRelativeOffset(unsigned varNum) LclVarDsc* varDsc = lvaTable + varNum; assert(varDsc->lvOnFrame); - return lvaToInitialSPRelativeOffset(varDsc->lvStkOffs, varDsc->lvFramePointerBased); + return lvaToInitialSPRelativeOffset(varDsc->GetStackOffset(), varDsc->lvFramePointerBased); } // Given a local variable offset, and whether that offset is frame-pointer based, return its offset from Initial-SP. diff --git a/src/coreclr/src/jit/lower.cpp b/src/coreclr/src/jit/lower.cpp index 2cd902219bac8..9a5f843a038fd 100644 --- a/src/coreclr/src/jit/lower.cpp +++ b/src/coreclr/src/jit/lower.cpp @@ -1041,8 +1041,15 @@ GenTree* Lowering::NewPutArg(GenTreeCall* call, GenTree* arg, fgArgTabEntry* inf #endif // TARGET_ARM } + unsigned slotNumber = info->GetByteOffset() / TARGET_POINTER_SIZE; +#if defined(FEATURE_PUT_STRUCT_ARG_STK) + unsigned numberOfStackSlots = info->GetStackSlotsNumber(); + DEBUG_ARG_SLOTS_ASSERT(numberOfStackSlots == info->numSlots); +#endif + DEBUG_ARG_SLOTS_ASSERT(slotNumber == info->slotNum); + putArg = new (comp, GT_PUTARG_SPLIT) - GenTreePutArgSplit(arg, info->slotNum PUT_STRUCT_ARG_STK_ONLY_ARG(info->numSlots), info->numRegs, + GenTreePutArgSplit(arg, slotNumber PUT_STRUCT_ARG_STK_ONLY_ARG(numberOfStackSlots), info->numRegs, call->IsFastTailCall(), call); // If struct argument is morphed to GT_FIELD_LIST node(s), @@ -1126,12 +1133,16 @@ GenTree* Lowering::NewPutArg(GenTreeCall* call, GenTree* arg, fgArgTabEntry* inf // Mark this one as tail call arg if it is a fast tail call. // This provides the info to put this argument in in-coming arg area slot // instead of in out-going arg area slot. + CLANG_FORMAT_COMMENT_ANCHOR; +#ifdef DEBUG // Make sure state is correct. The PUTARG_STK has TYP_VOID, as it doesn't produce // a result. So the type of its operand must be the correct type to push on the stack. // For a FIELD_LIST, this will be the type of the field (not the type of the arg), // but otherwise it is generally the type of the operand. info->checkIsStruct(); +#endif + if ((arg->OperGet() != GT_FIELD_LIST)) { #if defined(FEATURE_SIMD) && defined(FEATURE_PUT_STRUCT_ARG_STK) @@ -1145,10 +1156,16 @@ GenTree* Lowering::NewPutArg(GenTreeCall* call, GenTree* arg, fgArgTabEntry* inf assert(genActualType(arg->TypeGet()) == type); } } + unsigned slotNumber = info->GetByteOffset() / TARGET_POINTER_SIZE; +#if defined(FEATURE_PUT_STRUCT_ARG_STK) + unsigned numberOfStackSlots = info->GetStackSlotsNumber(); + DEBUG_ARG_SLOTS_ASSERT(numberOfStackSlots == info->numSlots); +#endif + DEBUG_ARG_SLOTS_ASSERT(slotNumber == info->slotNum); putArg = new (comp, GT_PUTARG_STK) GenTreePutArgStk(GT_PUTARG_STK, TYP_VOID, arg, - info->slotNum PUT_STRUCT_ARG_STK_ONLY_ARG(info->numSlots), + slotNumber PUT_STRUCT_ARG_STK_ONLY_ARG(numberOfStackSlots), call->IsFastTailCall(), call); #ifdef FEATURE_PUT_STRUCT_ARG_STK @@ -1895,19 +1912,19 @@ void Lowering::LowerFastTailCall(GenTreeCall* call) unsigned int argStart = callerArgLclNum * TARGET_POINTER_SIZE; unsigned int argEnd = argStart + static_cast(callerArgDsc->lvArgStackSize()); #else - assert(callerArgDsc->lvStkOffs != BAD_STK_OFFS); + assert(callerArgDsc->GetStackOffset() != BAD_STK_OFFS); if (baseOff == -1) { - baseOff = callerArgDsc->lvStkOffs; + baseOff = callerArgDsc->GetStackOffset(); } // On all ABIs where we fast tail call the stack args should come in order. - assert(baseOff <= callerArgDsc->lvStkOffs); + assert(baseOff <= callerArgDsc->GetStackOffset()); // Compute offset of this stack argument relative to the first stack arg. // This will be its offset into the incoming arg space area. - unsigned int argStart = static_cast(callerArgDsc->lvStkOffs - baseOff); + unsigned int argStart = static_cast(callerArgDsc->GetStackOffset() - baseOff); unsigned int argEnd = argStart + comp->lvaLclSize(callerArgLclNum); #endif @@ -2115,7 +2132,10 @@ GenTree* Lowering::LowerTailCallViaJitHelper(GenTreeCall* call, GenTree* callTar // We need to figure out the size of the outgoing stack arguments, not including the special args. // The number of 4-byte words is passed to the helper for the incoming and outgoing argument sizes. // This number is exactly the next slot number in the call's argument info struct. - unsigned nNewStkArgsWords = call->fgArgInfo->GetNextSlotNum(); + unsigned nNewStkArgsBytes = call->fgArgInfo->GetNextSlotByteOffset(); + const int wordSize = 4; + unsigned nNewStkArgsWords = nNewStkArgsBytes / wordSize; + DEBUG_ARG_SLOTS_ASSERT(call->fgArgInfo->GetNextSlotNum() == nNewStkArgsWords); assert(nNewStkArgsWords >= 4); // There must be at least the four special stack args. nNewStkArgsWords -= 4; @@ -4167,7 +4187,7 @@ void Lowering::InsertPInvokeCallProlog(GenTreeCall* call) // On x86 targets, PInvoke calls need the size of the stack args in InlinedCallFrame.m_Datum. // This is because the callee pops stack arguments, and we need to keep track of this during stack // walking - const unsigned numStkArgBytes = call->fgArgInfo->GetNextSlotNum() * TARGET_POINTER_SIZE; + const unsigned numStkArgBytes = call->fgArgInfo->GetNextSlotByteOffset(); GenTree* stackBytes = comp->gtNewIconNode(numStkArgBytes, TYP_INT); GenTreeCall::Use* args = comp->gtNewCallArgs(frameAddr, stackBytes); #else @@ -4201,9 +4221,9 @@ void Lowering::InsertPInvokeCallProlog(GenTreeCall* call) { #if !defined(TARGET_64BIT) // On 32-bit targets, indirect calls need the size of the stack args in InlinedCallFrame.m_Datum. - const unsigned numStkArgBytes = call->fgArgInfo->GetNextSlotNum() * TARGET_POINTER_SIZE; + const unsigned stackByteOffset = call->fgArgInfo->GetNextSlotByteOffset(); - src = comp->gtNewIconNode(numStkArgBytes, TYP_INT); + src = comp->gtNewIconNode(stackByteOffset, TYP_INT); #else // On 64-bit targets, indirect calls may need the stub parameter value in InlinedCallFrame.m_Datum. // If the stub parameter value is not needed, m_Datum will be initialized by the VM. diff --git a/src/coreclr/src/jit/morph.cpp b/src/coreclr/src/jit/morph.cpp index 6b75dadc3b620..54af6b01c35c6 100644 --- a/src/coreclr/src/jit/morph.cpp +++ b/src/coreclr/src/jit/morph.cpp @@ -77,8 +77,10 @@ GenTree* Compiler::fgMorphIntoHelperCall(GenTree* tree, int helper, GenTreeCall: #if DEBUG // Helper calls are never candidates. - call->gtInlineObservation = InlineObservation::CALLSITE_IS_CALL_TO_HELPER; + + call->callSig = nullptr; + #endif // DEBUG #ifdef FEATURE_READYTORUN_COMPILER @@ -781,11 +783,16 @@ void fgArgTabEntry::Dump() const printf(" %s", getRegName(regNums[i])); } } - if (numSlots > 0) + if (GetStackByteSize() > 0) { - printf(", numSlots=%u, slotNum=%u", numSlots, slotNum); +#if defined(DEBUG_ARG_SLOTS) + printf(", numSlots=%u, slotNum=%u, byteSize=%u, byteOffset=%u", numSlots, slotNum, m_byteSize, m_byteOffset); +#else + printf(", byteSize=%u, byteOffset=%u", m_byteSize, m_byteOffset); + +#endif } - printf(", align=%u", alignment); + printf(", byteAlignment=%u", byteAlignment); if (isLateArg()) { printf(", lateArgInx=%u", GetLateArgInx()); @@ -832,11 +839,12 @@ void fgArgTabEntry::Dump() const fgArgInfo::fgArgInfo(Compiler* comp, GenTreeCall* call, unsigned numArgs) { - compiler = comp; - callTree = call; - argCount = 0; // filled in arg count, starts at zero - nextSlotNum = INIT_ARG_STACK_SLOT; - stkLevel = 0; + compiler = comp; + callTree = call; + argCount = 0; // filled in arg count, starts at zero + DEBUG_ARG_SLOTS_ONLY(nextSlotNum = INIT_ARG_STACK_SLOT;) + nextStackByteOffset = INIT_ARG_STACK_SLOT * TARGET_POINTER_SIZE; + stkLevel = 0; #if defined(UNIX_X86_ABI) alignmentDone = false; stkSizeBytes = 0; @@ -879,11 +887,12 @@ fgArgInfo::fgArgInfo(GenTreeCall* newCall, GenTreeCall* oldCall) { fgArgInfo* oldArgInfo = oldCall->AsCall()->fgArgInfo; - compiler = oldArgInfo->compiler; - callTree = newCall; - argCount = 0; // filled in arg count, starts at zero - nextSlotNum = INIT_ARG_STACK_SLOT; - stkLevel = oldArgInfo->stkLevel; + compiler = oldArgInfo->compiler; + callTree = newCall; + argCount = 0; // filled in arg count, starts at zero + DEBUG_ARG_SLOTS_ONLY(nextSlotNum = INIT_ARG_STACK_SLOT;) + nextStackByteOffset = INIT_ARG_STACK_SLOT * TARGET_POINTER_SIZE; + stkLevel = oldArgInfo->stkLevel; #if defined(UNIX_X86_ABI) alignmentDone = oldArgInfo->alignmentDone; stkSizeBytes = oldArgInfo->stkSizeBytes; @@ -957,8 +966,10 @@ fgArgInfo::fgArgInfo(GenTreeCall* newCall, GenTreeCall* oldCall) } } - argCount = oldArgInfo->argCount; - nextSlotNum = oldArgInfo->nextSlotNum; + argCount = oldArgInfo->argCount; + DEBUG_ARG_SLOTS_ONLY(nextSlotNum = oldArgInfo->nextSlotNum;) + nextStackByteOffset = oldArgInfo->nextStackByteOffset; + hasRegArgs = oldArgInfo->hasRegArgs; hasStackArgs = oldArgInfo->hasStackArgs; argsComplete = true; @@ -977,7 +988,8 @@ fgArgTabEntry* fgArgInfo::AddRegArg(unsigned argNum, GenTreeCall::Use* use, regNumber regNum, unsigned numRegs, - unsigned alignment, + unsigned byteSize, + unsigned byteAlignment, bool isStruct, bool isVararg /*=false*/) { @@ -989,14 +1001,18 @@ fgArgTabEntry* fgArgInfo::AddRegArg(unsigned argNum, // may actually be less. curArgTabEntry->setRegNum(0, regNum); - curArgTabEntry->argNum = argNum; - curArgTabEntry->argType = node->TypeGet(); - curArgTabEntry->use = use; - curArgTabEntry->lateUse = nullptr; - curArgTabEntry->slotNum = 0; - curArgTabEntry->numRegs = numRegs; - curArgTabEntry->numSlots = 0; - curArgTabEntry->alignment = alignment; + curArgTabEntry->argNum = argNum; + curArgTabEntry->argType = node->TypeGet(); + curArgTabEntry->use = use; + curArgTabEntry->lateUse = nullptr; + curArgTabEntry->numRegs = numRegs; + +#if defined(DEBUG_ARG_SLOTS) + curArgTabEntry->slotNum = 0; + curArgTabEntry->numSlots = 0; +#endif + + curArgTabEntry->byteAlignment = byteAlignment; curArgTabEntry->SetLateArgInx(UINT_MAX); curArgTabEntry->tmpNum = BAD_VAR_NUM; curArgTabEntry->SetSplit(false); @@ -1011,6 +1027,8 @@ fgArgTabEntry* fgArgInfo::AddRegArg(unsigned argNum, curArgTabEntry->isNonStandard = false; curArgTabEntry->isStruct = isStruct; curArgTabEntry->SetIsVararg(isVararg); + curArgTabEntry->SetByteSize(byteSize); + curArgTabEntry->SetByteOffset(0); hasRegArgs = true; AddArg(curArgTabEntry); @@ -1023,7 +1041,8 @@ fgArgTabEntry* fgArgInfo::AddRegArg(unsigned GenTreeCall::Use* use, regNumber regNum, unsigned numRegs, - unsigned alignment, + unsigned byteSize, + unsigned byteAlignment, const bool isStruct, const bool isVararg, const regNumber otherRegNum, @@ -1031,14 +1050,15 @@ fgArgTabEntry* fgArgInfo::AddRegArg(unsigned const unsigned structFloatRegs, const SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* const structDescPtr) { - fgArgTabEntry* curArgTabEntry = AddRegArg(argNum, node, use, regNum, numRegs, alignment, isStruct, isVararg); + fgArgTabEntry* curArgTabEntry = + AddRegArg(argNum, node, use, regNum, numRegs, byteSize, byteAlignment, isStruct, isVararg); assert(curArgTabEntry != nullptr); curArgTabEntry->isStruct = isStruct; // is this a struct arg curArgTabEntry->structIntRegs = structIntRegs; curArgTabEntry->structFloatRegs = structFloatRegs; - curArgTabEntry->checkIsStruct(); + INDEBUG(curArgTabEntry->checkIsStruct();) assert(numRegs <= 2); if (numRegs == 2) { @@ -1058,27 +1078,36 @@ fgArgTabEntry* fgArgInfo::AddStkArg(unsigned argNum, GenTree* node, GenTreeCall::Use* use, unsigned numSlots, - unsigned alignment, + unsigned byteSize, + unsigned byteAlignment, bool isStruct, bool isVararg /*=false*/) { fgArgTabEntry* curArgTabEntry = new (compiler, CMK_fgArgInfo) fgArgTabEntry; - nextSlotNum = roundUp(nextSlotNum, alignment); +#if defined(DEBUG_ARG_SLOTS) + nextSlotNum = roundUp(nextSlotNum, byteAlignment / TARGET_POINTER_SIZE); +#endif + + nextStackByteOffset = roundUp(nextStackByteOffset, byteAlignment); + DEBUG_ARG_SLOTS_ASSERT(nextStackByteOffset / TARGET_POINTER_SIZE == nextSlotNum); curArgTabEntry->setRegNum(0, REG_STK); curArgTabEntry->argNum = argNum; curArgTabEntry->argType = node->TypeGet(); curArgTabEntry->use = use; curArgTabEntry->lateUse = nullptr; - curArgTabEntry->slotNum = nextSlotNum; +#if defined(DEBUG_ARG_SLOTS) + curArgTabEntry->numSlots = numSlots; + curArgTabEntry->slotNum = nextSlotNum; +#endif + curArgTabEntry->numRegs = 0; #if defined(UNIX_AMD64_ABI) curArgTabEntry->structIntRegs = 0; curArgTabEntry->structFloatRegs = 0; #endif // defined(UNIX_AMD64_ABI) - curArgTabEntry->numSlots = numSlots; - curArgTabEntry->alignment = alignment; + curArgTabEntry->byteAlignment = byteAlignment; curArgTabEntry->SetLateArgInx(UINT_MAX); curArgTabEntry->tmpNum = BAD_VAR_NUM; curArgTabEntry->SetSplit(false); @@ -1094,16 +1123,20 @@ fgArgTabEntry* fgArgInfo::AddStkArg(unsigned argNum, curArgTabEntry->isStruct = isStruct; curArgTabEntry->SetIsVararg(isVararg); + curArgTabEntry->SetByteSize(byteSize); + curArgTabEntry->SetByteOffset(nextStackByteOffset); + hasStackArgs = true; AddArg(curArgTabEntry); - - nextSlotNum += numSlots; + DEBUG_ARG_SLOTS_ONLY(nextSlotNum += numSlots;) + nextStackByteOffset += byteSize; return curArgTabEntry; } void fgArgInfo::RemorphReset() { - nextSlotNum = INIT_ARG_STACK_SLOT; + DEBUG_ARG_SLOTS_ONLY(nextSlotNum = INIT_ARG_STACK_SLOT;) + nextStackByteOffset = INIT_ARG_STACK_SLOT * TARGET_POINTER_SIZE; } //------------------------------------------------------------------------ @@ -1149,10 +1182,22 @@ void fgArgInfo::UpdateStkArg(fgArgTabEntry* curArgTabEntry, GenTree* node, bool noway_assert(curArgTabEntry->use != callTree->gtCallThisArg); assert((curArgTabEntry->GetRegNum() == REG_STK) || curArgTabEntry->IsSplit()); assert(curArgTabEntry->use->GetNode() == node); - nextSlotNum = (unsigned)roundUp(nextSlotNum, curArgTabEntry->alignment); +#if defined(DEBUG_ARG_SLOTS) + nextSlotNum = roundUp(nextSlotNum, curArgTabEntry->byteAlignment / TARGET_POINTER_SIZE); assert(curArgTabEntry->slotNum == nextSlotNum); - nextSlotNum += curArgTabEntry->numSlots; +#endif + nextStackByteOffset = roundUp(nextStackByteOffset, curArgTabEntry->byteAlignment); + assert(curArgTabEntry->GetByteOffset() == nextStackByteOffset); + + if (!curArgTabEntry->IsSplit()) + { + nextStackByteOffset += curArgTabEntry->GetByteSize(); + } + else + { + nextStackByteOffset += curArgTabEntry->GetStackByteSize(); + } } void fgArgInfo::SplitArg(unsigned argNum, unsigned numRegs, unsigned numSlots) @@ -1175,17 +1220,20 @@ void fgArgInfo::SplitArg(unsigned argNum, unsigned numRegs, unsigned numSlots) { assert(curArgTabEntry->IsSplit() == true); assert(curArgTabEntry->numRegs == numRegs); - assert(curArgTabEntry->numSlots == numSlots); + DEBUG_ARG_SLOTS_ONLY(assert(curArgTabEntry->numSlots == numSlots);) assert(hasStackArgs == true); } else { curArgTabEntry->SetSplit(true); - curArgTabEntry->numRegs = numRegs; - curArgTabEntry->numSlots = numSlots; - hasStackArgs = true; - } - nextSlotNum += numSlots; + curArgTabEntry->numRegs = numRegs; + DEBUG_ARG_SLOTS_ONLY(curArgTabEntry->numSlots = numSlots;) + curArgTabEntry->SetByteOffset(0); + hasStackArgs = true; + } + DEBUG_ARG_SLOTS_ONLY(nextSlotNum += numSlots;) + // TODO-Cleanup: structs are aligned to 8 bytes on arm64 apple, so it would work, but pass the precise size. + nextStackByteOffset += numSlots * TARGET_POINTER_SIZE; } //------------------------------------------------------------------------ @@ -1378,7 +1426,8 @@ void fgArgInfo::ArgsComplete() // CLANG_FORMAT_COMMENT_ANCHOR; #ifdef TARGET_ARM - bool isMultiRegArg = (curArgTabEntry->numRegs > 0) && (curArgTabEntry->numRegs + curArgTabEntry->numSlots > 1); + bool isMultiRegArg = + (curArgTabEntry->numRegs > 0) && (curArgTabEntry->numRegs + curArgTabEntry->GetStackSlotsNumber() > 1); #else bool isMultiRegArg = (curArgTabEntry->numRegs > 1); #endif @@ -1901,7 +1950,7 @@ GenTree* Compiler::fgMakeTmpArgNode(fgArgTabEntry* curArgTabEntry) // Otherwise, it will return TYP_UNKNOWN and we will pass it as a struct type. bool passedAsPrimitive = false; - if (curArgTabEntry->isSingleRegOrSlot()) + if (curArgTabEntry->TryPassAsPrimitive()) { CORINFO_CLASS_HANDLE clsHnd = varDsc->GetStructHnd(); var_types structBaseType = @@ -2384,7 +2433,7 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) unsigned argIndex = 0; unsigned intArgRegNum = 0; unsigned fltArgRegNum = 0; - unsigned argSlots = 0; + DEBUG_ARG_SLOTS_ONLY(unsigned argSlots = 0;) bool callHasRetBuffArg = call->HasRetBufArg(); bool callIsVararg = call->IsVarargs(); @@ -2677,7 +2726,7 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) *insertionPoint = gtNewCallArgs(arg); #else // !defined(TARGET_X86) // All other architectures pass the cookie in a register. - call->gtCallArgs = gtPrependNewCallArg(arg, call->gtCallArgs); + call->gtCallArgs = gtPrependNewCallArg(arg, call->gtCallArgs); #endif // defined(TARGET_X86) nonStandardArgs.Add(arg, REG_PINVOKE_COOKIE_PARAM); @@ -2736,10 +2785,16 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) assert(call->gtCallType == CT_USER_FUNC || call->gtCallType == CT_INDIRECT); assert(varTypeIsGC(argx) || (argx->gtType == TYP_I_IMPL)); + const regNumber regNum = genMapIntRegArgNumToRegNum(intArgRegNum); + const unsigned numRegs = 1; + const unsigned byteSize = TARGET_POINTER_SIZE; + const unsigned byteAlignment = TARGET_POINTER_SIZE; + const bool isStruct = false; + // This is a register argument - put it in the table. - call->fgArgInfo->AddRegArg(argIndex, argx, call->gtCallThisArg, genMapIntRegArgNumToRegNum(intArgRegNum), 1, 1, - false, callIsVararg UNIX_AMD64_ABI_ONLY_ARG(REG_STK) UNIX_AMD64_ABI_ONLY_ARG(0) - UNIX_AMD64_ABI_ONLY_ARG(0) UNIX_AMD64_ABI_ONLY_ARG(nullptr)); + call->fgArgInfo->AddRegArg(argIndex, argx, call->gtCallThisArg, regNum, numRegs, byteSize, byteAlignment, + isStruct, callIsVararg UNIX_AMD64_ABI_ONLY_ARG(REG_STK) UNIX_AMD64_ABI_ONLY_ARG(0) + UNIX_AMD64_ABI_ONLY_ARG(0) UNIX_AMD64_ABI_ONLY_ARG(nullptr)); intArgRegNum++; #ifdef WINDOWS_AMD64_ABI @@ -2748,7 +2803,7 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) fltArgRegNum++; #endif // WINDOWS_AMD64_ABI argIndex++; - argSlots++; + DEBUG_ARG_SLOTS_ONLY(argSlots++;) } #ifdef TARGET_X86 @@ -2856,9 +2911,14 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) var_types hfaType = TYP_UNDEF; unsigned hfaSlots = 0; - bool passUsingFloatRegs; - unsigned argAlign = 1; + bool passUsingFloatRegs; +#if !defined(OSX_ARM64_ABI) + unsigned argAlignBytes = TARGET_POINTER_SIZE; +#else + unsigned argAlignBytes = TARGET_POINTER_SIZE; // TODO-OSX-ARM64: change it after other changes are merged. +#endif unsigned size = 0; + unsigned byteSize = 0; CORINFO_CLASS_HANDLE copyBlkClass = nullptr; bool isRegArg = false; bool isNonStandard = false; @@ -2892,12 +2952,11 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) bool passUsingIntRegs = passUsingFloatRegs ? false : (intArgRegNum < MAX_REG_ARG); // We don't use the "size" return value from InferOpSizeAlign(). - codeGen->InferOpSizeAlign(argx, &argAlign); + codeGen->InferOpSizeAlign(argx, &argAlignBytes); - argAlign = roundUp(argAlign, TARGET_POINTER_SIZE); - argAlign /= TARGET_POINTER_SIZE; + argAlignBytes = roundUp(argAlignBytes, TARGET_POINTER_SIZE); - if (argAlign == 2) + if (argAlignBytes == 2 * TARGET_POINTER_SIZE) { if (passUsingFloatRegs) { @@ -2916,10 +2975,12 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) } } +#if defined(DEBUG) if (argSlots % 2 == 1) { argSlots++; } +#endif } #elif defined(TARGET_ARM64) @@ -2988,15 +3049,22 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) #ifdef UNIX_AMD64_ABI if (!isStructArg) { - size = 1; // On AMD64, all primitives fit in a single (64-bit) 'slot' + size = 1; // On AMD64, all primitives fit in a single (64-bit) 'slot' + byteSize = genTypeSize(argx); } else { - size = (unsigned)(roundUp(structSize, TARGET_POINTER_SIZE)) / TARGET_POINTER_SIZE; + size = (unsigned)(roundUp(structSize, TARGET_POINTER_SIZE)) / TARGET_POINTER_SIZE; + byteSize = structSize; eeGetSystemVAmd64PassStructInRegisterDescriptor(objClass, &structDesc); } -#else // !UNIX_AMD64_ABI +#else // !UNIX_AMD64_ABI size = 1; // On AMD64 Windows, all args fit in a single (64-bit) 'slot' + if (!isStructArg) + { + byteSize = genTypeSize(argx); + } + #endif // UNIX_AMD64_ABI #elif defined(TARGET_ARM64) if (isStructArg) @@ -3005,7 +3073,9 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) { // HFA structs are passed by value in multiple registers. // The "size" in registers may differ the size in pointer-sized units. - size = GetHfaCount(argx); + CORINFO_CLASS_HANDLE structHnd = gtGetStructHandle(argx); + size = GetHfaCount(structHnd); + byteSize = info.compCompHnd->getClassSize(structHnd); } else { @@ -3014,8 +3084,8 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) // if sufficient registers are available. // Structs that are larger than 2 pointers (except for HFAs) are passed by // reference (to a copy) - size = (unsigned)(roundUp(structSize, TARGET_POINTER_SIZE)) / TARGET_POINTER_SIZE; - + size = (unsigned)(roundUp(structSize, TARGET_POINTER_SIZE)) / TARGET_POINTER_SIZE; + byteSize = structSize; if (size > 2) { size = 1; @@ -3026,52 +3096,48 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) } else { - size = 1; // Otherwise, all primitive types fit in a single (64-bit) 'slot' + size = 1; // Otherwise, all primitive types fit in a single (64-bit) 'slot' + byteSize = genTypeSize(argx); } #elif defined(TARGET_ARM) || defined(TARGET_X86) if (isStructArg) { - size = (unsigned)(roundUp(structSize, TARGET_POINTER_SIZE)) / TARGET_POINTER_SIZE; + size = (unsigned)(roundUp(structSize, TARGET_POINTER_SIZE)) / TARGET_POINTER_SIZE; + byteSize = structSize; } else { // The typical case. // Long/double type argument(s) will be modified as needed in Lowering. - size = genTypeStSz(argx->gtType); + size = genTypeStSz(argx->gtType); + byteSize = genTypeSize(argx); } #else #error Unsupported or unset target architecture #endif // TARGET_XXX + if (isStructArg) { // We have an argument with a struct type, but it may be be a child of a GT_COMMA GenTree* argObj = argx->gtEffectiveVal(true /*commaOnly*/); assert(argx == args->GetNode()); - - unsigned originalSize = structSize; - originalSize = (originalSize == 0 ? TARGET_POINTER_SIZE : originalSize); - unsigned roundupSize = (unsigned)roundUp(originalSize, TARGET_POINTER_SIZE); - - structSize = originalSize; + assert(structSize != 0); structPassingKind howToPassStruct; - - structBaseType = getArgTypeForStruct(objClass, &howToPassStruct, callIsVararg, originalSize); - - bool passedInRegisters = false; - passStructByRef = (howToPassStruct == SPK_ByReference); + structBaseType = getArgTypeForStruct(objClass, &howToPassStruct, callIsVararg, structSize); + passStructByRef = (howToPassStruct == SPK_ByReference); + if (howToPassStruct == SPK_ByReference) + { + byteSize = TARGET_POINTER_SIZE; + } + else + { + byteSize = structSize; + } if (howToPassStruct == SPK_PrimitiveType) { -// For ARM64 or AMD64/UX we can pass non-power-of-2 structs in a register. -// For ARM or AMD64/Windows only power-of-2 structs are passed in registers. -#if !defined(TARGET_ARM64) && !defined(UNIX_AMD64_ABI) - if (!isPow2(originalSize)) -#endif // !TARGET_ARM64 && !UNIX_AMD64_ABI - { - passedInRegisters = true; - } #ifdef TARGET_ARM // TODO-CQ: getArgTypeForStruct should *not* return TYP_DOUBLE for a double struct, // or for a struct of two floats. This causes the struct to be address-taken. @@ -3093,6 +3159,7 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) // The 'size' value has now must have been set. (the original value of zero is an invalid value) assert(size != 0); + assert(byteSize != 0); // // Figure out if the argument will be passed in a register. @@ -3335,11 +3402,11 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) #endif // This is a register argument - put it in the table - newArgEntry = call->fgArgInfo->AddRegArg(argIndex, argx, args, nextRegNum, size, argAlign, isStructArg, - callIsVararg UNIX_AMD64_ABI_ONLY_ARG(nextOtherRegNum) - UNIX_AMD64_ABI_ONLY_ARG(structIntRegs) - UNIX_AMD64_ABI_ONLY_ARG(structFloatRegs) - UNIX_AMD64_ABI_ONLY_ARG(&structDesc)); + newArgEntry = call->fgArgInfo->AddRegArg(argIndex, argx, args, nextRegNum, size, byteSize, argAlignBytes, + isStructArg, callIsVararg UNIX_AMD64_ABI_ONLY_ARG(nextOtherRegNum) + UNIX_AMD64_ABI_ONLY_ARG(structIntRegs) + UNIX_AMD64_ABI_ONLY_ARG(structFloatRegs) + UNIX_AMD64_ABI_ONLY_ARG(&structDesc)); newArgEntry->SetIsBackFilled(isBackFilled); newArgEntry->isNonStandard = isNonStandard; @@ -3400,7 +3467,8 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) else // We have an argument that is not passed in a register { // This is a stack argument - put it in the table - newArgEntry = call->fgArgInfo->AddStkArg(argIndex, argx, args, size, argAlign, isStructArg, callIsVararg); + newArgEntry = call->fgArgInfo->AddStkArg(argIndex, argx, args, size, byteSize, argAlignBytes, isStructArg, + callIsVararg); #ifdef UNIX_AMD64_ABI // TODO-Amd64-Unix-CQ: This is temporary (see also in fgMorphArgs). if (structDesc.passedInRegisters) @@ -3429,7 +3497,7 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) newArgEntry->argType = argx->TypeGet(); } - argSlots += size; + DEBUG_ARG_SLOTS_ONLY(argSlots += size;) } // end foreach argument loop #ifdef DEBUG @@ -3491,7 +3559,8 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) unsigned flagsSummary = 0; unsigned argIndex = 0; - unsigned argSlots = 0; + + DEBUG_ARG_SLOTS_ONLY(unsigned argSlots = 0;) bool reMorphing = call->AreArgsComplete(); @@ -3530,7 +3599,7 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) flagsSummary |= argx->gtFlags; assert(argIndex == 0); argIndex++; - argSlots++; + DEBUG_ARG_SLOTS_ONLY(argSlots++;) } // Note that this name is a bit of a misnomer - it indicates that there are struct args @@ -3547,17 +3616,18 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) *parentArgx = argx; assert(argx == args->GetNode()); - unsigned argAlign = argEntry->alignment; - unsigned size = argEntry->getSize(); + DEBUG_ARG_SLOTS_ONLY(unsigned size = argEntry->getSize();) CORINFO_CLASS_HANDLE copyBlkClass = NO_CLASS_HANDLE; - if (argAlign == 2) +#if defined(DEBUG_ARG_SLOTS) + if (argEntry->byteAlignment == 2 * TARGET_POINTER_SIZE) { if (argSlots % 2 == 1) { argSlots++; } } +#endif // DEBUG if (argEntry->isNonStandard) { // We need to update the node field for this nonStandard arg here @@ -3566,9 +3636,8 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) flagsSummary |= argx->gtFlags; continue; } - - assert(size != 0); - argSlots += argEntry->getSlotCount(); + DEBUG_ARG_SLOTS_ASSERT(size != 0); + DEBUG_ARG_SLOTS_ONLY(argSlots += argEntry->getSlotCount();) if (argx->IsLocalAddrExpr() != nullptr) { @@ -3619,7 +3688,7 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) // First, handle the case where the argument is passed by reference. if (argEntry->passedByRef) { - assert(size == 1); + DEBUG_ARG_SLOTS_ASSERT(size == 1); copyBlkClass = objClass; #ifdef UNIX_AMD64_ABI assert(!"Structs are not passed by reference on x64/ux"); @@ -3631,7 +3700,7 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) #ifndef TARGET_X86 // Check to see if we can transform this into load of a primitive type. // 'size' must be the number of pointer sized items - assert(size == roundupSize / TARGET_POINTER_SIZE); + DEBUG_ARG_SLOTS_ASSERT(size == roundupSize / TARGET_POINTER_SIZE); structSize = originalSize; unsigned passingSize = originalSize; @@ -3728,12 +3797,11 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) // or a local. // Change our argument, as needed, into a value of the appropriate type. CLANG_FORMAT_COMMENT_ANCHOR; - #ifdef TARGET_ARM - assert((size == 1) || ((structBaseType == TYP_DOUBLE) && (size == 2))); + DEBUG_ARG_SLOTS_ASSERT((size == 1) || ((structBaseType == TYP_DOUBLE) && (size == 2))); #else - assert((size == 1) || - (varTypeIsSIMD(structBaseType) && size == (genTypeSize(structBaseType) / REGSIZE_BYTES))); + DEBUG_ARG_SLOTS_ASSERT((size == 1) || (varTypeIsSIMD(structBaseType) && + size == (genTypeSize(structBaseType) / REGSIZE_BYTES))); #endif assert((structBaseType != TYP_STRUCT) && (genTypeSize(structBaseType) >= originalSize)); @@ -3827,9 +3895,8 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) // We still have a struct unless we converted the GT_OBJ into a GT_IND above... if (isHfaArg && passUsingFloatRegs) { - size = argEntry->numRegs; } - else if (structBaseType == TYP_STRUCT) + else if ((!isHfaArg || !passUsingFloatRegs) && (structBaseType == TYP_STRUCT)) { // If the valuetype size is not a multiple of TARGET_POINTER_SIZE, // we must copyblk to a temp before doing the obj to avoid @@ -3849,8 +3916,6 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) copyBlkClass = NO_CLASS_HANDLE; } } - - size = roundupSize / TARGET_POINTER_SIZE; // Normalize size to number of pointer sized items } #endif // !UNIX_AMD64_ABI @@ -3916,7 +3981,8 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) #if FEATURE_MULTIREG_ARGS if (isStructArg) { - if (((argEntry->numRegs + argEntry->numSlots) > 1) || (isHfaArg && argx->TypeGet() == TYP_STRUCT)) + if (((argEntry->numRegs + argEntry->GetStackSlotsNumber()) > 1) || + (isHfaArg && argx->TypeGet() == TYP_STRUCT)) { hasMultiregStructArgs = true; } @@ -3930,8 +3996,8 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) else { // We must have exactly one register or slot. - assert(((argEntry->numRegs == 1) && (argEntry->numSlots == 0)) || - ((argEntry->numRegs == 0) && (argEntry->numSlots == 1))); + assert(((argEntry->numRegs == 1) && (argEntry->GetStackSlotsNumber() == 0)) || + ((argEntry->numRegs == 0) && (argEntry->GetStackSlotsNumber() == 1))); } #endif @@ -4001,21 +4067,33 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) // outgoing arg size. if (!call->IsFastTailCall()) { - unsigned preallocatedArgCount = call->fgArgInfo->GetNextSlotNum(); #if defined(UNIX_AMD64_ABI) // This is currently required for the UNIX ABI to work correctly. opts.compNeedToAlignFrame = true; #endif // UNIX_AMD64_ABI - const unsigned outgoingArgSpaceSize = preallocatedArgCount * REGSIZE_BYTES; + const unsigned outgoingArgSpaceSize = GetOutgoingArgByteSize(call->fgArgInfo->GetNextSlotByteOffset()); + +#if defined(DEBUG_ARG_SLOTS) + unsigned preallocatedArgCount = call->fgArgInfo->GetNextSlotNum(); + assert(outgoingArgSpaceSize == preallocatedArgCount * REGSIZE_BYTES); +#endif call->fgArgInfo->SetOutArgSize(max(outgoingArgSpaceSize, MIN_ARG_AREA_FOR_CALL)); #ifdef DEBUG if (verbose) { - printf("argSlots=%d, preallocatedArgCount=%d, nextSlotNum=%d, outgoingArgSpaceSize=%d\n", argSlots, - preallocatedArgCount, call->fgArgInfo->GetNextSlotNum(), outgoingArgSpaceSize); + const fgArgInfo* argInfo = call->fgArgInfo; +#if defined(DEBUG_ARG_SLOTS) + printf("argSlots=%d, preallocatedArgCount=%d, nextSlotNum=%d, nextSlotByteOffset=%d, " + "outgoingArgSpaceSize=%d\n", + argSlots, preallocatedArgCount, argInfo->GetNextSlotNum(), argInfo->GetNextSlotByteOffset(), + outgoingArgSpaceSize); +#else + printf("nextSlotByteOffset=%d, outgoingArgSpaceSize=%d\n", argInfo->GetNextSlotByteOffset(), + outgoingArgSpaceSize); +#endif } #endif } @@ -4122,7 +4200,7 @@ void Compiler::fgMorphMultiregStructArgs(GenTreeCall* call) continue; } - unsigned size = (fgEntryPtr->numRegs + fgEntryPtr->numSlots); + unsigned size = (fgEntryPtr->numRegs + fgEntryPtr->GetStackSlotsNumber()); if ((size > 1) || (fgEntryPtr->IsHfaArg() && argx->TypeGet() == TYP_STRUCT)) { foundStructArg = true; @@ -4216,7 +4294,7 @@ GenTree* Compiler::fgMorphMultiregStructArg(GenTree* arg, fgArgTabEntry* fgEntry #endif #ifdef TARGET_ARM - if ((fgEntryPtr->IsSplit() && fgEntryPtr->numSlots + fgEntryPtr->numRegs > 4) || + if ((fgEntryPtr->IsSplit() && fgEntryPtr->GetStackSlotsNumber() + fgEntryPtr->numRegs > 4) || (!fgEntryPtr->IsSplit() && fgEntryPtr->GetRegNum() == REG_STK)) #else if (fgEntryPtr->GetRegNum() == REG_STK) @@ -5738,8 +5816,8 @@ GenTree* Compiler::fgMorphStackArgForVarArgs(unsigned lclNum, var_types varType, // Create a node representing the local pointing to the base of the args GenTree* ptrArg = gtNewOperNode(GT_SUB, TYP_I_IMPL, gtNewLclvNode(lvaVarargsBaseOfStkArgs, TYP_I_IMPL), - gtNewIconNode(varDsc->lvStkOffs - codeGen->intRegState.rsCalleeRegArgCount * REGSIZE_BYTES - - lclOffs)); + gtNewIconNode(varDsc->GetStackOffset() - + codeGen->intRegState.rsCalleeRegArgCount * REGSIZE_BYTES - lclOffs)); // Access the argument through the local GenTree* tree; @@ -6688,15 +6766,17 @@ bool Compiler::fgCanFastTailCall(GenTreeCall* callee, const char** failReason) fgArgInfo* argInfo = callee->fgArgInfo; - size_t calleeArgStackSize = 0; - size_t callerArgStackSize = info.compArgStackSize; + unsigned calleeArgStackSize = 0; + unsigned callerArgStackSize = info.compArgStackSize; for (unsigned index = 0; index < argInfo->ArgCount(); ++index) { fgArgTabEntry* arg = argInfo->GetArgEntry(index, false); - calleeArgStackSize += arg->stackSize(); + calleeArgStackSize = roundUp(calleeArgStackSize, arg->byteAlignment); + calleeArgStackSize += arg->GetStackByteSize(); } + calleeArgStackSize = GetOutgoingArgByteSize(calleeArgStackSize); auto reportFastTailCallDecision = [&](const char* thisFailReason) { if (failReason != nullptr) diff --git a/src/coreclr/src/jit/scopeinfo.cpp b/src/coreclr/src/jit/scopeinfo.cpp index d4f9c3830d164..97dc968becb4a 100644 --- a/src/coreclr/src/jit/scopeinfo.cpp +++ b/src/coreclr/src/jit/scopeinfo.cpp @@ -487,7 +487,7 @@ CodeGenInterface::siVarLoc CodeGenInterface::getSiVarLoc(const LclVarDsc* varDsc // For stack vars, find the base register, and offset regNumber baseReg; - signed offset = varDsc->lvStkOffs; + signed offset = varDsc->GetStackOffset(); if (!varDsc->lvFramePointerBased) { @@ -603,7 +603,7 @@ CodeGenInterface::siVarLoc CodeGen::getSiVarLoc(const LclVarDsc* varDsc, const s // For stack vars, find the base register, and offset regNumber baseReg; - signed offset = varDsc->lvStkOffs; + signed offset = varDsc->GetStackOffset(); if (!varDsc->lvFramePointerBased) { @@ -1487,17 +1487,17 @@ NATIVE_OFFSET CodeGen::psiGetVarStackOffset(const LclVarDsc* lclVarDsc) const #ifdef TARGET_AMD64 // scOffset = offset from caller SP - REGSIZE_BYTES // TODO-Cleanup - scOffset needs to be understood. For now just matching with the existing definition. - stackOffset = - compiler->lvaToCallerSPRelativeOffset(lclVarDsc->lvStkOffs, lclVarDsc->lvFramePointerBased) + REGSIZE_BYTES; + stackOffset = compiler->lvaToCallerSPRelativeOffset(lclVarDsc->GetStackOffset(), lclVarDsc->lvFramePointerBased) + + REGSIZE_BYTES; #else // !TARGET_AMD64 if (doubleAlignOrFramePointerUsed()) { // REGSIZE_BYTES - for the pushed value of EBP - stackOffset = lclVarDsc->lvStkOffs - REGSIZE_BYTES; + stackOffset = lclVarDsc->GetStackOffset() - REGSIZE_BYTES; } else { - stackOffset = lclVarDsc->lvStkOffs - genTotalFrameSize(); + stackOffset = lclVarDsc->GetStackOffset() - genTotalFrameSize(); } #endif // !TARGET_AMD64 @@ -1867,7 +1867,7 @@ void CodeGen::psiMoveToStack(unsigned varNum) psiScope* newScope = psiNewPrologScope(scope->scLVnum, scope->scSlotNum); newScope->scRegister = false; newScope->u2.scBaseReg = (compiler->lvaTable[varNum].lvFramePointerBased) ? REG_FPBASE : REG_SPBASE; - newScope->u2.scOffset = compiler->lvaTable[varNum].lvStkOffs; + newScope->u2.scOffset = compiler->lvaTable[varNum].GetStackOffset(); psiEndPrologScope(scope); return; diff --git a/src/coreclr/src/jit/stacklevelsetter.cpp b/src/coreclr/src/jit/stacklevelsetter.cpp index fa48b8080ea1c..361a4faadf537 100644 --- a/src/coreclr/src/jit/stacklevelsetter.cpp +++ b/src/coreclr/src/jit/stacklevelsetter.cpp @@ -249,8 +249,9 @@ unsigned StackLevelSetter::PopArgumentsFromCall(GenTreeCall* call) { for (unsigned i = 0; i < argInfo->ArgCount(); ++i) { - fgArgTabEntry* argTab = argInfo->ArgTable()[i]; - if (argTab->numSlots != 0) + const fgArgTabEntry* argTab = argInfo->ArgTable()[i]; + const unsigned slotCount = argTab->GetStackSlotsNumber(); + if (slotCount != 0) { GenTree* node = argTab->GetNode(); assert(node->OperIsPutArgStkOrSplit()); @@ -258,13 +259,13 @@ unsigned StackLevelSetter::PopArgumentsFromCall(GenTreeCall* call) GenTreePutArgStk* putArg = node->AsPutArgStk(); #if !FEATURE_FIXED_OUT_ARGS - assert(argTab->numSlots == putArg->gtNumSlots); + assert(slotCount == putArg->gtNumSlots); #endif // !FEATURE_FIXED_OUT_ARGS - putArgNumSlots.Set(putArg, argTab->numSlots); + putArgNumSlots.Set(putArg, slotCount); - usedStackSlotsCount += argTab->numSlots; - AddStackLevel(argTab->numSlots); + usedStackSlotsCount += slotCount; + AddStackLevel(slotCount); } } } diff --git a/src/tests/Interop/PInvoke/Primitives/Int/PInvokeIntNative.cpp b/src/tests/Interop/PInvoke/Primitives/Int/PInvokeIntNative.cpp index 0ea023f5cc8ad..34bef70d1914f 100644 --- a/src/tests/Interop/PInvoke/Primitives/Int/PInvokeIntNative.cpp +++ b/src/tests/Interop/PInvoke/Primitives/Int/PInvokeIntNative.cpp @@ -30,6 +30,26 @@ extern "C" DLL_EXPORT int STDMETHODCALLTYPE Marshal_In(/*[in]*/int intValue) return intReturn; } +extern "C" DLL_EXPORT int STDMETHODCALLTYPE Marshal_InMany(/*[in]*/short i1, short i2, short i3, short i4, short i5, short i6, short i7, short i8, short i9, short i10, short i11, unsigned char i12, unsigned char i13, int i14, short i15) +{ + //Check the input + if(i1 != 1 || i2 != 2 || i3 != 3 || i4 != 4 || i5 != 5 || i6 != 6 || i7 != 7 || i8 != 8 || i9 != 9 || i10 != 10 || i11 != 11 || i12 != 12 || i13 != 13 || i14 != 14 || i15 != 15) + { + printf("Error in Function Marshal_InMany(Native Client)\n"); + + //Expected + printf("Expected: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15\n"); + + //Actual + printf("Actual: %hi, %hi, %hi, %hi, %hi, %hi, %hi, %hi, %hi, %hi, %hi, %i, %i, %i, %hi\n", i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, (int)i12, (int)i13, i14, i15); + + //Return the error value instead if verification failed + return intErrReturn; + } + + return i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9 + i10 + i11 + i12 + i13 + i14 + i15; +} + extern "C" DLL_EXPORT int STDMETHODCALLTYPE Marshal_InOut(/*[In,Out]*/int intValue) { //Check the input diff --git a/src/tests/Interop/PInvoke/Primitives/Int/PInvokeIntTest.cs b/src/tests/Interop/PInvoke/Primitives/Int/PInvokeIntTest.cs index 249a142e38a86..62a1d42277964 100644 --- a/src/tests/Interop/PInvoke/Primitives/Int/PInvokeIntTest.cs +++ b/src/tests/Interop/PInvoke/Primitives/Int/PInvokeIntTest.cs @@ -24,6 +24,9 @@ class ClientPInvokeIntNativeTest [DllImport("PInvokeIntNative")] private static extern int MarshalPointer_Out(out int pintValue); + [DllImport("PInvokeIntNative")] + private static extern int Marshal_InMany([In]short i1, [In]short i2, [In]short i3, [In]short i4, [In]short i5, [In]short i6, [In]short i7, [In]short i8, [In]short i9, [In]short i10, [In]short i11, [In]byte i12, [In]byte i13, [In]int i14, [In]short i15); + public static int Main(string[] args) { @@ -99,6 +102,12 @@ public static int Main(string[] args) Console.WriteLine("Out byref value is wrong."); } + if(120 != Marshal_InMany(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)) + { + failures++; + Console.WriteLine("InMany return value is wrong"); + } + return 100 + failures; } }