diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index 8646d9191edcf1..47abb2f25ac06c 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -1335,10 +1335,10 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX void genPutStructArgStk(GenTreePutArgStk* treeNode); - unsigned genMove8IfNeeded(unsigned size, regNumber tmpReg, GenTree* srcAddr, unsigned offset); - unsigned genMove4IfNeeded(unsigned size, regNumber tmpReg, GenTree* srcAddr, unsigned offset); - unsigned genMove2IfNeeded(unsigned size, regNumber tmpReg, GenTree* srcAddr, unsigned offset); - unsigned genMove1IfNeeded(unsigned size, regNumber tmpReg, GenTree* srcAddr, unsigned offset); + unsigned genMove8IfNeeded(unsigned size, regNumber tmpReg, GenTree* src, unsigned offset); + unsigned genMove4IfNeeded(unsigned size, regNumber tmpReg, GenTree* src, unsigned offset); + unsigned genMove2IfNeeded(unsigned size, regNumber tmpReg, GenTree* src, unsigned offset); + unsigned genMove1IfNeeded(unsigned size, regNumber tmpReg, GenTree* src, unsigned offset); void genCodeForLoadOffset(instruction ins, emitAttr size, regNumber dst, GenTree* base, unsigned offset); void genStoreRegToStackArg(var_types type, regNumber reg, int offset); void genStructPutArgRepMovs(GenTreePutArgStk* putArgStkNode); diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index 6a44f1db324de1..f2528f6f3c628d 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -1762,21 +1762,21 @@ void CodeGen::genConsumePutStructArgStk(GenTreePutArgStk* putArgNode, regNumber sizeReg) { // The putArgNode children are always contained. We should not consume any registers. - assert(putArgNode->gtGetOp1()->isContained()); + assert(putArgNode->Data()->isContained()); - // Get the source address. - GenTree* src = putArgNode->gtGetOp1(); + // Get the source. + GenTree* src = putArgNode->Data(); + regNumber srcAddrReg = REG_NA; assert(varTypeIsStruct(src)); - assert((src->gtOper == GT_OBJ) || ((src->gtOper == GT_IND && varTypeIsSIMD(src)))); - GenTree* srcAddr = src->gtGetOp1(); + assert(src->OperIs(GT_OBJ) || src->OperIsLocalRead() || (src->OperIs(GT_IND) && varTypeIsSIMD(src))); assert(dstReg != REG_NA); assert(srcReg != REG_NA); - // Consume the registers only if they are not contained or set to REG_NA. - if (srcAddr->GetRegNum() != REG_NA) + // Consume the register for the source address if needed. + if (src->OperIsIndir()) { - genConsumeReg(srcAddr); + srcAddrReg = genConsumeReg(src->AsIndir()->Addr()); } // If the op1 is already in the dstReg - nothing to do. @@ -1798,22 +1798,17 @@ void CodeGen::genConsumePutStructArgStk(GenTreePutArgStk* putArgNode, } #endif // !TARGET_X86 - if (srcAddr->OperIsLocalAddr()) + if (srcAddrReg != REG_NA) { - // The OperLocalAddr is always contained. - assert(srcAddr->isContained()); - const GenTreeLclVarCommon* lclNode = srcAddr->AsLclVarCommon(); - - // Generate LEA instruction to load the LclVar address in RSI. - // Source is known to be on the stack. Use EA_PTRSIZE. - unsigned int offset = lclNode->GetLclOffs(); - GetEmitter()->emitIns_R_S(INS_lea, EA_PTRSIZE, srcReg, lclNode->GetLclNum(), offset); + // Source is not known to be on the stack. Use EA_BYREF. + GetEmitter()->emitIns_Mov(INS_mov, EA_BYREF, srcReg, srcAddrReg, /* canSkip */ true); } else { - assert(srcAddr->GetRegNum() != REG_NA); - // Source is not known to be on the stack. Use EA_BYREF. - GetEmitter()->emitIns_Mov(INS_mov, EA_BYREF, srcReg, srcAddr->GetRegNum(), /* canSkip */ true); + // Generate LEA instruction to load the LclVar address in RSI. + // Source is known to be on the stack. Use EA_PTRSIZE. + GetEmitter()->emitIns_R_S(INS_lea, EA_PTRSIZE, srcReg, src->AsLclVarCommon()->GetLclNum(), + src->AsLclVarCommon()->GetLclOffs()); } if (sizeReg != REG_NA) diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 4435e2aea5f691..e4123e64f5291a 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -3053,21 +3053,19 @@ void CodeGen::genCodeForInitBlkHelper(GenTreeBlk* initBlkNode) #ifdef FEATURE_PUT_STRUCT_ARG_STK // Generate code for a load from some address + offset -// baseNode: tree node which can be either a local address or arbitrary node -// offset: distance from the baseNode from which to load -void CodeGen::genCodeForLoadOffset(instruction ins, emitAttr size, regNumber dst, GenTree* baseNode, unsigned offset) +// base: tree node which can be either a local or an indir +// offset: distance from the "base" location from which to load +// +void CodeGen::genCodeForLoadOffset(instruction ins, emitAttr size, regNumber dst, GenTree* base, unsigned offset) { - emitter* emit = GetEmitter(); - - if (baseNode->OperIsLocalAddr()) + if (base->OperIsLocalRead()) { - const GenTreeLclVarCommon* lclVar = baseNode->AsLclVarCommon(); - offset += lclVar->GetLclOffs(); - emit->emitIns_R_S(ins, size, dst, lclVar->GetLclNum(), offset); + GetEmitter()->emitIns_R_S(ins, size, dst, base->AsLclVarCommon()->GetLclNum(), + offset + base->AsLclVarCommon()->GetLclOffs()); } else { - emit->emitIns_R_AR(ins, size, dst, baseNode->GetRegNum(), offset); + GetEmitter()->emitIns_R_AR(ins, size, dst, base->AsIndir()->Addr()->GetRegNum(), offset); } } #endif // FEATURE_PUT_STRUCT_ARG_STK @@ -3338,7 +3336,7 @@ void CodeGen::genCodeForCpBlkRepMovs(GenTreeBlk* cpBlkNode) // Arguments: // size - The size of bytes remaining to be moved // longTmpReg - The tmp register to be used for the long value -// srcAddr - The address of the source struct +// src - The source struct node (LCL/OBJ) // offset - The current offset being copied // // Return Value: @@ -3350,7 +3348,7 @@ void CodeGen::genCodeForCpBlkRepMovs(GenTreeBlk* cpBlkNode) // On x86, longTmpReg must be an xmm reg; on x64 it must be an integer register. // This is checked by genStoreRegToStackArg. // -unsigned CodeGen::genMove8IfNeeded(unsigned size, regNumber longTmpReg, GenTree* srcAddr, unsigned offset) +unsigned CodeGen::genMove8IfNeeded(unsigned size, regNumber longTmpReg, GenTree* src, unsigned offset) { #ifdef TARGET_X86 instruction longMovIns = INS_movq; @@ -3359,7 +3357,7 @@ unsigned CodeGen::genMove8IfNeeded(unsigned size, regNumber longTmpReg, GenTree* #endif // !TARGET_X86 if ((size & 8) != 0) { - genCodeForLoadOffset(longMovIns, EA_8BYTE, longTmpReg, srcAddr, offset); + genCodeForLoadOffset(longMovIns, EA_8BYTE, longTmpReg, src, offset); genStoreRegToStackArg(TYP_LONG, longTmpReg, offset); return 8; } @@ -3372,7 +3370,7 @@ unsigned CodeGen::genMove8IfNeeded(unsigned size, regNumber longTmpReg, GenTree* // Arguments: // size - The size of bytes remaining to be moved // intTmpReg - The tmp register to be used for the long value -// srcAddr - The address of the source struct +// src - The source struct node (LCL/OBJ) // offset - The current offset being copied // // Return Value: @@ -3384,11 +3382,11 @@ unsigned CodeGen::genMove8IfNeeded(unsigned size, regNumber longTmpReg, GenTree* // intTmpReg must be an integer register. // This is checked by genStoreRegToStackArg. // -unsigned CodeGen::genMove4IfNeeded(unsigned size, regNumber intTmpReg, GenTree* srcAddr, unsigned offset) +unsigned CodeGen::genMove4IfNeeded(unsigned size, regNumber intTmpReg, GenTree* src, unsigned offset) { if ((size & 4) != 0) { - genCodeForLoadOffset(INS_mov, EA_4BYTE, intTmpReg, srcAddr, offset); + genCodeForLoadOffset(INS_mov, EA_4BYTE, intTmpReg, src, offset); genStoreRegToStackArg(TYP_INT, intTmpReg, offset); return 4; } @@ -3401,7 +3399,7 @@ unsigned CodeGen::genMove4IfNeeded(unsigned size, regNumber intTmpReg, GenTree* // Arguments: // size - The size of bytes remaining to be moved // intTmpReg - The tmp register to be used for the long value -// srcAddr - The address of the source struct +// src - The source struct node (LCL/OBJ) // offset - The current offset being copied // // Return Value: @@ -3413,11 +3411,11 @@ unsigned CodeGen::genMove4IfNeeded(unsigned size, regNumber intTmpReg, GenTree* // intTmpReg must be an integer register. // This is checked by genStoreRegToStackArg. // -unsigned CodeGen::genMove2IfNeeded(unsigned size, regNumber intTmpReg, GenTree* srcAddr, unsigned offset) +unsigned CodeGen::genMove2IfNeeded(unsigned size, regNumber intTmpReg, GenTree* src, unsigned offset) { if ((size & 2) != 0) { - genCodeForLoadOffset(INS_mov, EA_2BYTE, intTmpReg, srcAddr, offset); + genCodeForLoadOffset(INS_mov, EA_2BYTE, intTmpReg, src, offset); genStoreRegToStackArg(TYP_SHORT, intTmpReg, offset); return 2; } @@ -3430,7 +3428,7 @@ unsigned CodeGen::genMove2IfNeeded(unsigned size, regNumber intTmpReg, GenTree* // Arguments: // size - The size of bytes remaining to be moved // intTmpReg - The tmp register to be used for the long value -// srcAddr - The address of the source struct +// src - The source struct node (LCL/OBJ) // offset - The current offset being copied // // Return Value: @@ -3442,11 +3440,11 @@ unsigned CodeGen::genMove2IfNeeded(unsigned size, regNumber intTmpReg, GenTree* // intTmpReg must be an integer register. // This is checked by genStoreRegToStackArg. // -unsigned CodeGen::genMove1IfNeeded(unsigned size, regNumber intTmpReg, GenTree* srcAddr, unsigned offset) +unsigned CodeGen::genMove1IfNeeded(unsigned size, regNumber intTmpReg, GenTree* src, unsigned offset) { if ((size & 1) != 0) { - genCodeForLoadOffset(INS_mov, EA_1BYTE, intTmpReg, srcAddr, offset); + genCodeForLoadOffset(INS_mov, EA_1BYTE, intTmpReg, src, offset); genStoreRegToStackArg(TYP_BYTE, intTmpReg, offset); return 1; } @@ -3470,20 +3468,21 @@ unsigned CodeGen::genMove1IfNeeded(unsigned size, regNumber intTmpReg, GenTree* // void CodeGen::genStructPutArgUnroll(GenTreePutArgStk* putArgNode) { - GenTree* src = putArgNode->AsOp()->gtOp1; + GenTree* src = putArgNode->Data(); // We will never call this method for SIMD types, which are stored directly in genPutStructArgStk(). - assert(src->isContained() && src->OperIs(GT_OBJ) && src->TypeIs(TYP_STRUCT)); + assert(src->isContained() && src->TypeIs(TYP_STRUCT) && (src->OperIs(GT_OBJ) || src->OperIsLocalRead())); + #ifdef TARGET_X86 assert(!m_pushStkArg); #endif - if (src->AsOp()->gtOp1->isUsedFromReg()) + if (src->OperIs(GT_OBJ)) { - genConsumeReg(src->AsOp()->gtOp1); + genConsumeReg(src->AsObj()->Addr()); } unsigned loadSize = putArgNode->GetArgLoadSize(); - assert(!src->AsObj()->GetLayout()->HasGCPtr() && (loadSize <= CPBLK_UNROLL_LIMIT)); + assert(!src->GetLayout(compiler)->HasGCPtr() && (loadSize <= CPBLK_UNROLL_LIMIT)); unsigned offset = 0; regNumber xmmTmpReg = REG_NA; @@ -3518,7 +3517,7 @@ void CodeGen::genStructPutArgUnroll(GenTreePutArgStk* putArgNode) // this probably needs to be changed. // Load - genCodeForLoadOffset(INS_movdqu, EA_8BYTE, xmmTmpReg, src->gtGetOp1(), offset); + genCodeForLoadOffset(INS_movdqu, EA_8BYTE, xmmTmpReg, src, offset); // Store genStoreRegToStackArg(TYP_STRUCT, xmmTmpReg, offset); @@ -3528,10 +3527,10 @@ void CodeGen::genStructPutArgUnroll(GenTreePutArgStk* putArgNode) // Fill the remainder (15 bytes or less) if there's one. if ((loadSize % XMM_REGSIZE_BYTES) != 0) { - offset += genMove8IfNeeded(loadSize, longTmpReg, src->AsOp()->gtOp1, offset); - offset += genMove4IfNeeded(loadSize, intTmpReg, src->AsOp()->gtOp1, offset); - offset += genMove2IfNeeded(loadSize, intTmpReg, src->AsOp()->gtOp1, offset); - offset += genMove1IfNeeded(loadSize, intTmpReg, src->AsOp()->gtOp1, offset); + offset += genMove8IfNeeded(loadSize, longTmpReg, src, offset); + offset += genMove4IfNeeded(loadSize, intTmpReg, src, offset); + offset += genMove2IfNeeded(loadSize, intTmpReg, src, offset); + offset += genMove1IfNeeded(loadSize, intTmpReg, src, offset); assert(offset == loadSize); } } @@ -3548,8 +3547,7 @@ void CodeGen::genStructPutArgUnroll(GenTreePutArgStk* putArgNode) void CodeGen::genStructPutArgRepMovs(GenTreePutArgStk* putArgNode) { GenTree* src = putArgNode->gtGetOp1(); - assert(src->TypeGet() == TYP_STRUCT); - assert(!src->AsObj()->GetLayout()->HasGCPtr()); + assert(src->TypeIs(TYP_STRUCT) && !src->GetLayout(compiler)->HasGCPtr()); // Make sure we got the arguments of the cpblk operation in the right registers, and that // 'src' is contained as expected. @@ -3583,27 +3581,22 @@ void CodeGen::genStructPutArgPush(GenTreePutArgStk* putArgNode) // future. assert(m_pushStkArg); - GenTree* src = putArgNode->Data(); - GenTree* srcAddr = putArgNode->Data()->AsObj()->Addr(); - - regNumber srcAddrReg = srcAddr->GetRegNum(); - const bool srcAddrInReg = srcAddrReg != REG_NA; - - unsigned srcLclNum = 0; - unsigned srcLclOffset = 0; - if (srcAddrInReg) + GenTree* src = putArgNode->Data(); + regNumber srcAddrReg = REG_NA; + unsigned srcLclNum = BAD_VAR_NUM; + unsigned srcLclOffs = BAD_LCL_OFFSET; + if (src->OperIsLocalRead()) { - srcAddrReg = genConsumeReg(srcAddr); + assert(src->isContained()); + srcLclNum = src->AsLclVarCommon()->GetLclNum(); + srcLclOffs = src->AsLclVarCommon()->GetLclOffs(); } else { - assert(srcAddr->OperIsLocalAddr()); - - srcLclNum = srcAddr->AsLclVarCommon()->GetLclNum(); - srcLclOffset = srcAddr->AsLclVarCommon()->GetLclOffs(); + srcAddrReg = genConsumeReg(src->AsObj()->Addr()); } - ClassLayout* layout = src->AsObj()->GetLayout(); + ClassLayout* layout = src->GetLayout(compiler); const unsigned loadSize = putArgNode->GetArgLoadSize(); assert(((loadSize < XMM_REGSIZE_BYTES) || layout->HasGCPtr()) && ((loadSize % TARGET_POINTER_SIZE) == 0)); const unsigned numSlots = loadSize / TARGET_POINTER_SIZE; @@ -3612,13 +3605,13 @@ void CodeGen::genStructPutArgPush(GenTreePutArgStk* putArgNode) { emitAttr slotAttr = emitTypeSize(layout->GetGCPtrType(i)); const unsigned byteOffset = i * TARGET_POINTER_SIZE; - if (srcAddrInReg) + if (srcAddrReg != REG_NA) { GetEmitter()->emitIns_AR_R(INS_push, slotAttr, REG_NA, srcAddrReg, byteOffset); } else { - GetEmitter()->emitIns_S(INS_push, slotAttr, srcLclNum, srcLclOffset + byteOffset); + GetEmitter()->emitIns_S(INS_push, slotAttr, srcLclNum, srcLclOffs + byteOffset); } AddStackLevel(TARGET_POINTER_SIZE); @@ -3643,10 +3636,9 @@ void CodeGen::genStructPutArgPartialRepMovs(GenTreePutArgStk* putArgNode) // They may now contain gc pointers (depending on their type; gcMarkRegPtrVal will "do the right thing"). genConsumePutStructArgStk(putArgNode, REG_RDI, REG_RSI, REG_NA); - GenTreeObj* src = putArgNode->gtGetOp1()->AsObj(); - ClassLayout* layout = src->GetLayout(); - const bool srcIsLocal = src->Addr()->OperIsLocalAddr(); - const emitAttr srcAddrAttr = srcIsLocal ? EA_PTRSIZE : EA_BYREF; + GenTree* src = putArgNode->Data(); + ClassLayout* layout = src->GetLayout(compiler); + const emitAttr srcAddrAttr = src->OperIsLocalRead() ? EA_PTRSIZE : EA_BYREF; #if DEBUG unsigned numGCSlotsCopied = 0; @@ -5533,7 +5525,7 @@ void CodeGen::genCall(GenTreeCall* call) #ifdef FEATURE_PUT_STRUCT_ARG_STK if (source->TypeIs(TYP_STRUCT) && !source->OperIs(GT_FIELD_LIST)) { - unsigned loadSize = source->AsObj()->GetLayout()->GetSize(); + unsigned loadSize = source->GetLayout(compiler)->GetSize(); assert(argSize == roundUp(loadSize, TARGET_POINTER_SIZE)); } #endif // FEATURE_PUT_STRUCT_ARG_STK @@ -7790,11 +7782,11 @@ bool CodeGen::genAdjustStackForPutArgStk(GenTreePutArgStk* putArgStk) { case GenTreePutArgStk::Kind::RepInstr: case GenTreePutArgStk::Kind::Unroll: - assert(!source->AsObj()->GetLayout()->HasGCPtr()); + assert(!source->GetLayout(compiler)->HasGCPtr()); break; case GenTreePutArgStk::Kind::Push: - assert(source->OperIs(GT_FIELD_LIST) || source->AsObj()->GetLayout()->HasGCPtr() || + assert(source->OperIs(GT_FIELD_LIST) || source->GetLayout(compiler)->HasGCPtr() || (argSize < XMM_REGSIZE_BYTES)); break; @@ -8308,14 +8300,11 @@ void CodeGen::genPutStructArgStk(GenTreePutArgStk* putArgStk) assert(targetType == TYP_STRUCT); - ClassLayout* layout = source->AsObj()->GetLayout(); - switch (putArgStk->gtPutArgStkKind) { case GenTreePutArgStk::Kind::RepInstr: genStructPutArgRepMovs(putArgStk); break; - #ifndef TARGET_X86 case GenTreePutArgStk::Kind::PartialRepInstr: genStructPutArgPartialRepMovs(putArgStk); diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 559a86c26a11b5..655e0acf2d4853 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -567,14 +567,47 @@ void Compiler::fgWalkAllTreesPre(fgWalkPreFn* visitor, void* pCallBackData) } } +//----------------------------------------------------------- +// GetLayout: Get the struct layout for this node. +// +// Arguments: +// compiler - The Compiler instance +// +// Return Value: +// The struct layout of this node; it must have one. +// +// Notes: +// This is the "general" method for getting the layout, +// the more efficient node-specific ones should be used +// in case the node's oper is known. +// +ClassLayout* GenTree::GetLayout(Compiler* compiler) const +{ + assert(varTypeIsStruct(TypeGet())); + + switch (OperGet()) + { + case GT_LCL_VAR: + return compiler->lvaGetDesc(AsLclVar())->GetLayout(); + + case GT_LCL_FLD: + return AsLclFld()->GetLayout(); + + case GT_OBJ: + case GT_BLK: + return AsBlk()->GetLayout(); + + default: + unreached(); + } +} + //----------------------------------------------------------- // CopyReg: Copy the _gtRegNum/gtRegTag fields. // // Arguments: // from - GenTree node from which to copy // -// Return Value: -// None void GenTree::CopyReg(GenTree* from) { _gtRegNum = from->_gtRegNum; diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index b4057c9d033ed7..67b9841780aa99 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -764,6 +764,8 @@ struct GenTree return gtType; } + ClassLayout* GetLayout(Compiler* compiler) const; + #ifdef DEBUG genTreeOps gtOperSave; // Only used to save gtOper when we destroy a node, to aid debugging. #endif diff --git a/src/coreclr/jit/jit.h b/src/coreclr/jit/jit.h index d4ea7ab0dcb910..cf0e7996500c55 100644 --- a/src/coreclr/jit/jit.h +++ b/src/coreclr/jit/jit.h @@ -336,7 +336,8 @@ typedef unsigned IL_OFFSET; const IL_OFFSET BAD_IL_OFFSET = 0xffffffff; -const unsigned BAD_VAR_NUM = UINT_MAX; +const unsigned BAD_VAR_NUM = UINT_MAX; +const uint16_t BAD_LCL_OFFSET = UINT16_MAX; // Code can't be more than 2^31 in any direction. This is signed, so it should be used for anything that is // relative to something else. diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index f6626dc3e6f7b0..1f005e86ecaf5e 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -388,16 +388,6 @@ GenTree* Lowering::LowerNode(GenTree* node) break; #endif -#if !defined(TARGET_ARMARCH) && !defined(TARGET_LOONGARCH64) - // TODO-ARMARCH-CQ: We should contain this as long as the offset fits. - case GT_OBJ: - if (node->AsObj()->Addr()->OperIsLocalAddr()) - { - node->AsObj()->Addr()->SetContained(); - } - break; -#endif // !TARGET_ARMARCH - case GT_KEEPALIVE: node->gtGetOp1()->SetRegOptional(); break; @@ -1206,8 +1196,6 @@ GenTree* Lowering::NewPutArg(GenTreeCall* call, GenTree* arg, CallArg* callArg, #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. callArg->CheckIsStruct(); #endif @@ -1245,64 +1233,15 @@ GenTree* Lowering::NewPutArg(GenTreeCall* call, GenTree* arg, CallArg* callArg, #endif call, putInIncomingArgArea); -#ifdef FEATURE_PUT_STRUCT_ARG_STK - // If the ArgTabEntry indicates that this arg is a struct - // get and store the number of slots that are references. - // This is later used in the codegen for PUT_ARG_STK implementation - // for struct to decide whether and how many single eight-byte copies - // to be done (only for reference slots), so gcinfo is emitted. - // For non-reference slots faster/smaller size instructions are used - - // pair copying using XMM registers or rep mov instructions. +#if defined(DEBUG) && defined(FEATURE_PUT_STRUCT_ARG_STK) if (callArg->AbiInfo.IsStruct) { - // We use GT_OBJ only for non-lclVar, non-SIMD, non-FIELD_LIST struct arguments. - if (arg->OperIsLocal()) - { - // This must have a type with a known size (SIMD or has been morphed to a primitive type). - assert(arg->TypeGet() != TYP_STRUCT); - } - else if (arg->OperIs(GT_OBJ)) + // We use GT_OBJ only for non-SIMD struct arguments. + if (arg->OperIs(GT_OBJ)) { assert(!varTypeIsSIMD(arg)); - -#ifdef TARGET_X86 - // On x86 VM lies about the type of a struct containing a pointer sized - // integer field by returning the type of its field as the type of struct. - // Such struct can be passed in a register depending its position in - // parameter list. VM does this unwrapping only one level and therefore - // a type like Struct Foo { Struct Bar { int f}} awlays needs to be - // passed on stack. Also, VM doesn't lie about type of such a struct - // when it is a field of another struct. That is VM doesn't lie about - // the type of Foo.Bar - // - // We now support the promotion of fields that are of type struct. - // However we only support a limited case where the struct field has a - // single field and that single field must be a scalar type. Say Foo.Bar - // field is getting passed as a parameter to a call, Since it is a TYP_STRUCT, - // as per x86 ABI it should always be passed on stack. Therefore GenTree - // node under a PUTARG_STK could be GT_OBJ(GT_LCL_VAR_ADDR(v1)), where - // local v1 could be a promoted field standing for Foo.Bar. Note that - // the type of v1 will be the type of field of Foo.Bar.f when Foo is - // promoted. That is v1 will be a scalar type. In this case we need to - // pass v1 on stack instead of in a register. - // - // TODO-PERF: replace GT_OBJ(GT_LCL_VAR_ADDR(v1)) with v1 if v1 is - // a scalar type and the width of GT_OBJ matches the type size of v1. - // Note that this cannot be done till call node arguments are morphed - // because we should not lose the fact that the type of argument is - // a struct so that the arg gets correctly marked to be passed on stack. - GenTree* objOp1 = arg->gtGetOp1(); - if (objOp1->OperGet() == GT_LCL_VAR_ADDR) - { - unsigned lclNum = objOp1->AsLclVarCommon()->GetLclNum(); - if (comp->lvaTable[lclNum].lvType != TYP_STRUCT) - { - comp->lvaSetVarDoNotEnregister(lclNum DEBUGARG(DoNotEnregisterReason::VMNeedsStackAddr)); - } - } -#endif // TARGET_X86 } - else if (!arg->OperIs(GT_FIELD_LIST)) + else if (!arg->TypeIs(TYP_STRUCT)) { #ifdef TARGET_ARM assert((callArg->AbiInfo.GetStackSlotsNumber() == 1) || @@ -1312,7 +1251,7 @@ GenTree* Lowering::NewPutArg(GenTreeCall* call, GenTree* arg, CallArg* callArg, #endif } } -#endif // FEATURE_PUT_STRUCT_ARG_STK +#endif // defined(DEBUG) && defined(FEATURE_PUT_STRUCT_ARG_STK) } } diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index ee9b370d61866b..fdf781dfd6ddbe 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -473,7 +473,7 @@ void Lowering::ContainBlockStoreAddress(GenTreeBlk* blkNode, unsigned size, GenT // void Lowering::LowerPutArgStk(GenTreePutArgStk* putArgStk) { - GenTree* src = putArgStk->gtGetOp1(); + GenTree* src = putArgStk->Data(); bool srcIsLocal = src->OperIsLocalRead(); if (src->OperIs(GT_FIELD_LIST)) @@ -544,9 +544,11 @@ void Lowering::LowerPutArgStk(GenTreePutArgStk* putArgStk) #ifdef FEATURE_PUT_STRUCT_ARG_STK if (src->TypeIs(TYP_STRUCT)) { - ClassLayout* layout = src->AsObj()->GetLayout(); + assert(src->OperIs(GT_OBJ) || src->OperIsLocalRead()); + + ClassLayout* layout = src->GetLayout(comp); var_types regType = layout->GetRegisterType(); - srcIsLocal |= src->AsObj()->Addr()->OperIsLocalAddr(); + srcIsLocal |= src->OperIs(GT_OBJ) && src->AsObj()->Addr()->OperIsLocalAddr(); if (regType == TYP_UNDEF) { @@ -594,14 +596,25 @@ void Lowering::LowerPutArgStk(GenTreePutArgStk* putArgStk) #endif // !TARGET_X86 } - // Always mark the OBJ and ADDR as contained trees by the putarg_stk. The codegen will deal with this tree. - MakeSrcContained(putArgStk, src); if (src->OperIs(GT_OBJ) && src->AsObj()->Addr()->OperIsLocalAddr()) { - // If the source address is the address of a lclVar, make the source address contained to avoid - // unnecessary copies. - MakeSrcContained(putArgStk, src->AsObj()->Addr()); + // TODO-ADDR: always perform this transformation in local morph and delete this code. + GenTreeLclVarCommon* lclAddrNode = src->AsObj()->Addr()->AsLclVarCommon(); + BlockRange().Remove(lclAddrNode); + + src->ChangeOper(GT_LCL_FLD); + src->AsLclFld()->SetLclNum(lclAddrNode->GetLclNum()); + src->AsLclFld()->SetLclOffs(lclAddrNode->GetLclOffs()); + src->AsLclFld()->SetLayout(layout); } + else if (src->OperIs(GT_LCL_VAR)) + { + comp->lvaSetVarDoNotEnregister(src->AsLclVar()->GetLclNum() + DEBUGARG(DoNotEnregisterReason::IsStructArg)); + } + + // Always mark the OBJ/LCL_VAR/LCL_FLD as contained trees. + MakeSrcContained(putArgStk, src); } else { @@ -613,9 +626,13 @@ void Lowering::LowerPutArgStk(GenTreePutArgStk* putArgStk) regType = TYP_INT; } - src->SetOper(GT_IND); src->ChangeType(regType); - LowerIndir(src->AsIndir()); + + if (src->OperIs(GT_OBJ)) + { + src->SetOper(GT_IND); + LowerIndir(src->AsIndir()); + } } }