diff --git a/src/inc/corinfo.h b/src/inc/corinfo.h index fef412678a53..ae44b0f73001 100644 --- a/src/inc/corinfo.h +++ b/src/inc/corinfo.h @@ -276,12 +276,26 @@ enum SystemVClassificationType : unsigned __int8 SystemVClassificationTypeMemory = 3, SystemVClassificationTypeInteger = 4, SystemVClassificationTypeIntegerReference = 5, - SystemVClassificationTypeSSE = 6, + SystemVClassificationTypeIntegerByRef = 6, + SystemVClassificationTypeSSE = 7, // SystemVClassificationTypeSSEUp = Unused, // Not supported by the CLR. // SystemVClassificationTypeX87 = Unused, // Not supported by the CLR. // SystemVClassificationTypeX87Up = Unused, // Not supported by the CLR. // SystemVClassificationTypeComplexX87 = Unused, // Not supported by the CLR. - SystemVClassificationTypeMAX = 7, + + // Internal flags - never returned outside of the classification implementation. + + // This value represents a very special type with two eightbytes. + // First ByRef, second Integer (platform int). + // The VM has a special Elem type for this type - ELEMENT_TYPE_TYPEDBYREF. + // This is the classification counterpart for that element type. It is used to detect + // the special TypedReference type and specialize its classification. + // This type is represented as a struct with two fields. The classification needs to do + // special handling of it since the source/methadata type of the fieds is IntPtr. + // The VM changes the first to ByRef. The second is left as IntPtr (TYP_I_IMPL really). The classification needs to match this and + // special handling is warranted (similar thing is done in the getGCLayout function for this type). + SystemVClassificationTypeTypedReference = 8, + SystemVClassificationTypeMAX = 9, }; // Represents classification information for a struct. @@ -298,6 +312,7 @@ struct SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR unsigned __int8 eightByteSizes[CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS]; // The size of the eightbytes (an eightbyte could include padding. This represents the no padding size of the eightbyte). unsigned __int8 eightByteOffsets[CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS]; // The start offset of the eightbytes (in bytes). + // Members //------------------------------------------------------------------------ // CopyFrom: Copies a struct classification into this one. @@ -318,7 +333,40 @@ struct SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR } } - // Members + //------------------------------------------------------------------------ + // IsIntegralSlot: Returns whether the eightbyte at slotIndex is of integral type. + // + // Arguments: + // 'slotIndex' the slot number we are determining if it is of integral type. + // + // Return value: + // returns true if we the eightbyte at index slotIndex is of integral type. + // + + bool IsIntegralSlot(unsigned slotIndex) const + { + return ((eightByteClassifications[slotIndex] == SystemVClassificationTypeInteger) || + (eightByteClassifications[slotIndex] == SystemVClassificationTypeIntegerReference) || + (eightByteClassifications[slotIndex] == SystemVClassificationTypeIntegerByRef)); + } + + //------------------------------------------------------------------------ + // IsSseSlot: Returns whether the eightbyte at slotIndex is SSE type. + // + // Arguments: + // 'slotIndex' the slot number we are determining if it is of SSE type. + // + // Return value: + // returns true if we the eightbyte at index slotIndex is of SSE type. + // + // Follows the rules of the AMD64 System V ABI specification at www.x86-64.org/documentation/abi.pdf. + // Please reffer to it for definitions/examples. + // + bool IsSseSlot(unsigned slotIndex) const + { + return (eightByteClassifications[slotIndex] == SystemVClassificationTypeSSE); + } + private: void Initialize() { diff --git a/src/jit/codegencommon.cpp b/src/jit/codegencommon.cpp index 4830342e8483..4fb8f3fc164f 100644 --- a/src/jit/codegencommon.cpp +++ b/src/jit/codegencommon.cpp @@ -3940,10 +3940,8 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, regArgNum = genMapRegNumToRegArgNum(regNum, regType); - if ((!doingFloat && - ((structDesc.eightByteClassifications[slotCounter] == SystemVClassificationTypeInteger) || - (structDesc.eightByteClassifications[slotCounter] == SystemVClassificationTypeIntegerReference))) || - (doingFloat && structDesc.eightByteClassifications[slotCounter] == SystemVClassificationTypeSSE)) + if ((!doingFloat && (structDesc.IsIntegralSlot(slotCounter))) || + (doingFloat && (structDesc.IsSseSlot(slotCounter)))) { // Store the reg for the first slot. if (slots == 0) diff --git a/src/jit/codegenlinear.h b/src/jit/codegenlinear.h index 14c79a55f497..3240d33bd62c 100644 --- a/src/jit/codegenlinear.h +++ b/src/jit/codegenlinear.h @@ -182,16 +182,26 @@ void genJmpMethod(GenTreePtr jmp); -#if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) - void genGetStructTypeSizeOffset(const SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR& structDesc, - var_types* type0, - var_types* type1, - emitAttr* size0, - emitAttr* size1, - unsigned __int8* offset0, - unsigned __int8* offset1); - bool genStoreRegisterReturnInLclVar(GenTreePtr treeNode); + + // Deals with codegen for muti-register struct returns. + bool isStructReturn(GenTreePtr treeNode); + void genStructReturn(GenTreePtr treeNode); + + // Codegen for GT_RETURN. + void genReturn(GenTreePtr treeNode); + +#if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + void getStructTypeOffset(const SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR& structDesc, + var_types* type0, + var_types* type1, + unsigned __int8* offset0, + unsigned __int8* offset1); + + void getStructReturnRegisters(var_types type0, + var_types type1, + regNumber* retRegPtr0, + regNumber* retRegPtr1); #endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) void genLclHeap(GenTreePtr tree); diff --git a/src/jit/codegenxarch.cpp b/src/jit/codegenxarch.cpp index 2426a7a6d551..66cad86ee85f 100644 --- a/src/jit/codegenxarch.cpp +++ b/src/jit/codegenxarch.cpp @@ -1412,6 +1412,259 @@ void CodeGen::genCodeForBinary(GenTree* treeNode) genProduceReg(treeNode); } +//------------------------------------------------------------------------ +// isStructReturn: Returns whether the 'treeNode' is returning a struct. +// +// Arguments: +// treeNode - The tree node to evaluate whether is a struct return. +// +// Return Value: +// For AMD64 *nix: returns true if the 'treeNode" is of type GT_RETURN and the +// return type is a struct or it is an implicit retBuf struct return. +// Otherwise returns false. +// For other platforms always returns false. +// +bool +CodeGen::isStructReturn(GenTreePtr treeNode) +{ + // This method could be called for 'treeNode' of GT_RET_FILT or GT_RETURN. + // For the GT_RET_FILT, the return is always + // a bool or a void, for the end of a finally block. + noway_assert(treeNode->OperGet() == GT_RETURN || treeNode->OperGet() == GT_RETFILT); + if (treeNode->OperGet() != GT_RETURN) + { + return false; + } + +#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING + return varTypeIsStruct(treeNode) || + (treeNode->TypeGet() == TYP_VOID && compiler->info.compRetBuffArg != BAD_VAR_NUM); +#else // !FEATURE_UNIX_AMD64_STRUCT_PASSING + assert(!varTypeIsStruct(treeNode)); + return false; +#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING +} + +//------------------------------------------------------------------------ +// genStructReturn: Generates code for returning a struct. +// +// Arguments: +// treeNode - The GT_RETURN tree node. +// +// Return Value: +// None +// +void +CodeGen::genStructReturn(GenTreePtr treeNode) +{ + assert(treeNode->OperGet() == GT_RETURN); + GenTreePtr op1 = treeNode->gtGetOp1(); + var_types targetType = treeNode->TypeGet(); + +#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING + if (targetType == TYP_VOID) + { + assert(op1 == nullptr); + if (compiler->info.compRetBuffArg != BAD_VAR_NUM) + { + // System V AMD64 spec requires that when a struct is returned by a hidden + // argument the RAX should contain the value of the hidden retbuf arg. + getEmitter()->emitIns_R_S(INS_mov, EA_BYREF, REG_RAX, compiler->info.compRetBuffArg, 0); + } + } + else + { + noway_assert((op1->OperGet() == GT_LCL_VAR) || + (op1->OperGet() == GT_CALL)); + + if (op1->OperGet() == GT_LCL_VAR) + { + assert(op1->isContained()); + + GenTreeLclVarCommon* lclVarPtr = op1->AsLclVarCommon(); + LclVarDsc* varDsc = &(compiler->lvaTable[lclVarPtr->gtLclNum]); + assert(varDsc->lvDontPromote); + + CORINFO_CLASS_HANDLE typeHnd = varDsc->lvVerTypeInfo.GetClassHandle(); + assert(typeHnd != nullptr); + + SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR structDesc; + compiler->eeGetSystemVAmd64PassStructInRegisterDescriptor(typeHnd, &structDesc); + assert(structDesc.passedInRegisters); + assert(structDesc.eightByteCount == CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS); + + regNumber retReg0 = REG_NA; + unsigned __int8 offset0 = 0; + regNumber retReg1 = REG_NA; + unsigned __int8 offset1 = 0; + + var_types type0 = TYP_UNKNOWN; + var_types type1 = TYP_UNKNOWN; + + getStructTypeOffset(structDesc, &type0, &type1, &offset0, &offset1); + getStructReturnRegisters(type0, type1, &retReg0, &retReg1); + + // Move the values into the return registers. + // + + assert(retReg0 != REG_NA && retReg1 != REG_NA); + + getEmitter()->emitIns_R_S(ins_Load(type0), emitTypeSize(type0), retReg0, lclVarPtr->gtLclNum, offset0); + getEmitter()->emitIns_R_S(ins_Load(type1), emitTypeSize(type1), retReg1, lclVarPtr->gtLclNum, offset1); + } + + // Nothing to do if the op1 of the return statement is a GT_CALL. The call already has the return + // registers in the proper return registers. + // This assumes that registers never get spilled. There is an Issue 2966 created to track the need + // for handling the GT_CALL case of two register returns and handle it properly for stress modes + // and potential other changes that may break this assumption. + } +#else + assert("!unreached"); +#endif +} + +//------------------------------------------------------------------------ +// genReturn: Generates code for return statement. +// In case of struct return, delegates to the genStructReturn method. +// +// Arguments: +// treeNode - The GT_RETURN or GT_RETFILT tree node. +// +// Return Value: +// None +// +void +CodeGen::genReturn(GenTreePtr treeNode) +{ + assert(treeNode->OperGet() == GT_RETURN || treeNode->OperGet() == GT_RETFILT); + GenTreePtr op1 = treeNode->gtGetOp1(); + var_types targetType = treeNode->TypeGet(); + +#ifdef DEBUG + if (targetType == TYP_VOID) + { + assert(op1 == nullptr); + } +#endif + +#ifdef _TARGET_X86_ + if (treeNode->TypeGet() == TYP_LONG) + { + assert(op1 != nullptr); + noway_assert(op1->OperGet() == GT_LONG); + GenTree* loRetVal = op1->gtGetOp1(); + GenTree* hiRetVal = op1->gtGetOp2(); + noway_assert((loRetVal->gtRegNum != REG_NA) && (hiRetVal->gtRegNum != REG_NA)); + + genConsumeReg(loRetVal); + genConsumeReg(hiRetVal); + if (loRetVal->gtRegNum != REG_LNGRET_LO) + { + inst_RV_RV(ins_Copy(targetType), REG_LNGRET_LO, loRetVal->gtRegNum, TYP_INT); + } + if (hiRetVal->gtRegNum != REG_LNGRET_HI) + { + inst_RV_RV(ins_Copy(targetType), REG_LNGRET_HI, hiRetVal->gtRegNum, TYP_INT); + } + } + else +#endif // !defined(_TARGET_X86_) + { + if (isStructReturn(treeNode)) + { + genStructReturn(treeNode); + } + else if (targetType != TYP_VOID) + { + assert(op1 != nullptr); + noway_assert(op1->gtRegNum != REG_NA); + + // !! NOTE !! genConsumeReg will clear op1 as GC ref after it has + // consumed a reg for the operand. This is because the variable + // is dead after return. But we are issuing more instructions + // like "profiler leave callback" after this consumption. So + // if you are issuing more instructions after this point, + // remember to keep the variable live up until the new method + // exit point where it is actually dead. + genConsumeReg(op1); + + regNumber retReg = varTypeIsFloating(treeNode) ? REG_FLOATRET : REG_INTRET; +#ifdef _TARGET_X86_ + if (varTypeIsFloating(treeNode)) + { + if (genIsRegCandidateLocal(op1) && !compiler->lvaTable[op1->gtLclVarCommon.gtLclNum].lvRegister) + { + // Store local variable to its home location, if necessary. + if ((op1->gtFlags & GTF_REG_VAL) != 0) + { + op1->gtFlags &= ~GTF_REG_VAL; + inst_TT_RV(ins_Store(op1->gtType, compiler->isSIMDTypeLocalAligned(op1->gtLclVarCommon.gtLclNum)), op1, op1->gtRegNum); + } + // Now, load it to the fp stack. + getEmitter()->emitIns_S(INS_fld, emitTypeSize(op1), op1->AsLclVarCommon()->gtLclNum, 0); + } + else + { + // Spill the value, which should be in a register, then load it to the fp stack. + // TODO-X86-CQ: Deal with things that are already in memory (don't call genConsumeReg yet). + op1->gtFlags |= GTF_SPILL; + regSet.rsSpillTree(op1->gtRegNum, op1); + op1->gtFlags |= GTF_SPILLED; + op1->gtFlags &= ~GTF_SPILL; + + TempDsc* t = regSet.rsUnspillInPlace(op1); + inst_FS_ST(INS_fld, emitActualTypeSize(op1->gtType), t, 0); + op1->gtFlags &= ~GTF_SPILLED; + compiler->tmpRlsTemp(t); + } + } + else +#endif // _TARGET_X86_ + { + if (op1->gtRegNum != retReg) + { + inst_RV_RV(ins_Copy(targetType), retReg, op1->gtRegNum, targetType); + } + } + } + } + +#ifdef PROFILING_SUPPORTED + // !! Note !! + // TODO-AMD64-Unix: If the profiler hook is implemented on *nix, make sure for 2 register returned structs + // the RAX and RDX needs to be kept alive. Make the necessary changes in lowerxarch.cpp + // in the handling of the GT_RETURN statement. + // Such structs containing GC pointers need to be handled by calling gcInfo.gcMarkRegSetNpt + // for the return registers containing GC refs. + + // There will be a single return block while generating profiler ELT callbacks. + // + // Reason for not materializing Leave callback as a GT_PROF_HOOK node after GT_RETURN: + // In flowgraph and other places assert that the last node of a block marked as + // GT_RETURN is either a GT_RETURN or GT_JMP or a tail call. It would be nice to + // maintain such an invariant irrespective of whether profiler hook needed or not. + // Also, there is not much to be gained by materializing it as an explicit node. + if (compiler->compCurBB == compiler->genReturnBB) + { + // !! NOTE !! + // Since we are invalidating the assumption that we would slip into the epilog + // right after the "return", we need to preserve the return reg's GC state + // across the call until actual method return. + if (varTypeIsGC(compiler->info.compRetType)) + { + gcInfo.gcMarkRegPtrVal(REG_INTRET, compiler->info.compRetType); + } + + genProfilingLeaveCallback(); + + if (varTypeIsGC(compiler->info.compRetType)) + { + gcInfo.gcMarkRegSetNpt(REG_INTRET); + } + } +#endif +} /***************************************************************************** * @@ -1650,9 +1903,7 @@ CodeGen::genCodeForTreeNode(GenTreePtr treeNode) case GT_STORE_LCL_FLD: { -#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING if (!genStoreRegisterReturnInLclVar(treeNode)) -#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING { noway_assert(targetType != TYP_STRUCT); noway_assert(!treeNode->InReg()); @@ -1676,9 +1927,7 @@ CodeGen::genCodeForTreeNode(GenTreePtr treeNode) case GT_STORE_LCL_VAR: { -#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING if (!genStoreRegisterReturnInLclVar(treeNode)) -#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING { noway_assert(targetType != TYP_STRUCT); assert(!varTypeIsFloating(targetType) || (targetType == treeNode->gtGetOp1()->TypeGet())); @@ -1720,6 +1969,7 @@ CodeGen::genCodeForTreeNode(GenTreePtr treeNode) op1->ResetReuseRegVal(); containedOp1 = true; } + if (containedOp1) { // Currently, we assume that the contained source of a GT_STORE_LCL_VAR writing to a register @@ -1758,302 +2008,7 @@ CodeGen::genCodeForTreeNode(GenTreePtr treeNode) __fallthrough; case GT_RETURN: - { - GenTreePtr op1 = treeNode->gtOp.gtOp1; - if (targetType == TYP_VOID) - { -#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING - if (compiler->info.compRetBuffArg != BAD_VAR_NUM) - { - // System V AMD64 spec requires that when a struct is returned by a hidden - // argument the RAX should contain the value of the hidden retbuf arg. - emit->emitIns_R_S(INS_mov, EA_BYREF, REG_RAX, compiler->info.compRetBuffArg, 0); - } -#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING - - assert(op1 == nullptr); - } -#if !defined(_TARGET_64BIT_) - else if (treeNode->TypeGet() == TYP_LONG) - { - assert(op1 != nullptr); - noway_assert(op1->OperGet() == GT_LONG); - GenTree* loRetVal = op1->gtGetOp1(); - GenTree* hiRetVal = op1->gtGetOp2(); - noway_assert((loRetVal->gtRegNum != REG_NA) && (hiRetVal->gtRegNum != REG_NA)); - - genConsumeReg(loRetVal); - genConsumeReg(hiRetVal); - if (loRetVal->gtRegNum != REG_LNGRET_LO) - { - inst_RV_RV(ins_Copy(targetType), REG_LNGRET_LO, loRetVal->gtRegNum, TYP_INT); - } - if (hiRetVal->gtRegNum != REG_LNGRET_HI) - { - inst_RV_RV(ins_Copy(targetType), REG_LNGRET_HI, hiRetVal->gtRegNum, TYP_INT); - } - } -#endif // !defined(_TARGET_64BIT_) - else - { -#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING - if (varTypeIsStruct(treeNode) && - treeNode->gtOp.gtOp1->OperGet() == GT_LCL_VAR) - { - GenTreeLclVarCommon* lclVarPtr = treeNode->gtOp.gtOp1->AsLclVarCommon(); - LclVarDsc* varDsc = &(compiler->lvaTable[lclVarPtr->gtLclNum]); - assert(varDsc->lvDontPromote); - - CORINFO_CLASS_HANDLE typeHnd = varDsc->lvVerTypeInfo.GetClassHandle(); - assert(typeHnd != nullptr); - - SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR structDesc; - compiler->eeGetSystemVAmd64PassStructInRegisterDescriptor(typeHnd, &structDesc); - assert(structDesc.passedInRegisters); - assert(structDesc.eightByteCount == CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS); - - regNumber retReg0 = REG_NA; - emitAttr size0 = EA_UNKNOWN; - unsigned offset0 = structDesc.eightByteOffsets[0]; - regNumber retReg1 = REG_NA; - emitAttr size1 = EA_UNKNOWN; - unsigned offset1 = structDesc.eightByteOffsets[1]; - - bool firstIntUsed = false; - bool firstFloatUsed = false; - - var_types type0 = TYP_UNKNOWN; - var_types type1 = TYP_UNKNOWN; - - // Set the first eightbyte data - switch (structDesc.eightByteClassifications[0]) - { - case SystemVClassificationTypeInteger: - if (structDesc.eightByteSizes[0] <= 4) - { - retReg0 = REG_INTRET; - size0 = EA_4BYTE; - type0 = TYP_INT; - firstIntUsed = true; - } - else if (structDesc.eightByteSizes[0] <= 8) - { - retReg0 = REG_LNGRET; - size0 = EA_8BYTE; - type0 = TYP_LONG; - firstIntUsed = true; - } - else - { - assert(false && "Bad int type."); - } - break; - case SystemVClassificationTypeIntegerReference: - assert(structDesc.eightByteSizes[0] == REGSIZE_BYTES); - retReg0 = REG_LNGRET; - size0 = EA_GCREF; - type0 = TYP_REF; - firstIntUsed = true; - break; - case SystemVClassificationTypeSSE: - if (structDesc.eightByteSizes[0] <= 4) - { - retReg0 = REG_FLOATRET; - size0 = EA_4BYTE; - type0 = TYP_FLOAT; - firstFloatUsed = true; - } - else if (structDesc.eightByteSizes[0] <= 8) - { - retReg0 = REG_DOUBLERET; - size0 = EA_8BYTE; - type0 = TYP_DOUBLE; - firstFloatUsed = true; - } - else - { - assert(false && "Bat float type."); // Not possible. - } - break; - default: - assert(false && "Bad EightByte classification."); - break; - } - - // Set the second eight byte data - switch (structDesc.eightByteClassifications[1]) - { - case SystemVClassificationTypeInteger: - if (structDesc.eightByteSizes[1] <= 4) - { - if (firstIntUsed) - { - retReg1 = REG_INTRET_1; - } - else - { - retReg1 = REG_INTRET; - } - type1 = TYP_INT; - size1 = EA_4BYTE; - } - else if (structDesc.eightByteSizes[1] <= 8) - { - if (firstIntUsed) - { - retReg1 = REG_LNGRET_1; - } - else - { - retReg1 = REG_LNGRET; - } - type1 = TYP_LONG; - size1 = EA_8BYTE; - } - else - { - assert(false && "Bad int type."); - } - break; - case SystemVClassificationTypeIntegerReference: - assert(structDesc.eightByteSizes[1] == REGSIZE_BYTES); - if (firstIntUsed) - { - retReg1 = REG_LNGRET_1; - } - else - { - retReg1 = REG_LNGRET; - } - type1 = TYP_REF; - size1 = EA_GCREF; - break; - case SystemVClassificationTypeSSE: - if (structDesc.eightByteSizes[1] <= 4) - { - if (firstFloatUsed) - { - retReg1 = REG_FLOATRET_1; - } - else - { - retReg1 = REG_FLOATRET; - } - type1 = TYP_FLOAT; - size1 = EA_4BYTE; - } - else if (structDesc.eightByteSizes[1] <= 8) - { - if (firstFloatUsed) - { - retReg1 = REG_DOUBLERET_1; - } - else - { - retReg1 = REG_DOUBLERET; - } - type1 = TYP_DOUBLE; - size1 = EA_8BYTE; - } - else - { - assert(false && "Bat float type."); // Not possible. - } - break; - default: - assert(false && "Bad EightByte classification."); - break; - } - - // Move the values into the return registers. - // - emit->emitIns_R_S(ins_Load(type0), size0, retReg0, lclVarPtr->gtLclNum, offset0); - emit->emitIns_R_S(ins_Load(type1), size1, retReg1, lclVarPtr->gtLclNum, offset1); - } - else -#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING - { - assert(op1 != nullptr); - noway_assert(op1->gtRegNum != REG_NA); - - // !! NOTE !! genConsumeReg will clear op1 as GC ref after it has - // consumed a reg for the operand. This is because the variable - // is dead after return. But we are issuing more instructions - // like "profiler leave callback" after this consumption. So - // if you are issuing more instructions after this point, - // remember to keep the variable live up until the new method - // exit point where it is actually dead. - genConsumeReg(op1); - - regNumber retReg = varTypeIsFloating(treeNode) ? REG_FLOATRET : REG_INTRET; -#ifdef _TARGET_X86_ - if (varTypeIsFloating(treeNode)) - { - if (genIsRegCandidateLocal(op1) && !compiler->lvaTable[op1->gtLclVarCommon.gtLclNum].lvRegister) - { - // Store local variable to its home location, if necessary. - if ((op1->gtFlags & GTF_REG_VAL) != 0) - { - op1->gtFlags &= ~GTF_REG_VAL; - inst_TT_RV(ins_Store(op1->gtType, compiler->isSIMDTypeLocalAligned(op1->gtLclVarCommon.gtLclNum)), op1, op1->gtRegNum); - } - // Now, load it to the fp stack. - getEmitter()->emitIns_S(INS_fld, emitTypeSize(op1), op1->AsLclVarCommon()->gtLclNum, 0); - } - else - { - // Spill the value, which should be in a register, then load it to the fp stack. - // TODO-X86-CQ: Deal with things that are already in memory (don't call genConsumeReg yet). - op1->gtFlags |= GTF_SPILL; - regSet.rsSpillTree(op1->gtRegNum, op1); - op1->gtFlags |= GTF_SPILLED; - op1->gtFlags &= ~GTF_SPILL; - - TempDsc* t = regSet.rsUnspillInPlace(op1); - inst_FS_ST(INS_fld, emitActualTypeSize(op1->gtType), t, 0); - op1->gtFlags &= ~GTF_SPILLED; - compiler->tmpRlsTemp(t); - } - } - else -#endif // _TARGET_X86_ - { - if (op1->gtRegNum != retReg) - { - inst_RV_RV(ins_Copy(targetType), retReg, op1->gtRegNum, targetType); - } - } - } - } - -#ifdef PROFILING_SUPPORTED - // There will be a single return block while generating profiler ELT callbacks. - // - // Reason for not materializing Leave callback as a GT_PROF_HOOK node after GT_RETURN: - // In flowgraph and other places assert that the last node of a block marked as - // GT_RETURN is either a GT_RETURN or GT_JMP or a tail call. It would be nice to - // maintain such an invariant irrespective of whether profiler hook needed or not. - // Also, there is not much to be gained by materializing it as an explicit node. - if (compiler->compCurBB == compiler->genReturnBB) - { - // !! NOTE !! - // Since we are invalidating the assumption that we would slip into the epilog - // right after the "return", we need to preserve the return reg's GC state - // across the call until actual method return. - if (varTypeIsGC(compiler->info.compRetType)) - { - gcInfo.gcMarkRegPtrVal(REG_INTRET, compiler->info.compRetType); - } - - genProfilingLeaveCallback(); - - if (varTypeIsGC(compiler->info.compRetType)) - { - gcInfo.gcMarkRegSetNpt(REG_INTRET); - } - } -#endif - } + genReturn(treeNode); break; case GT_LEA: @@ -2629,7 +2584,6 @@ CodeGen::genCodeForTreeNode(GenTreePtr treeNode) } } -#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING //------------------------------------------------------------------------ // genStoreRegisterReturnInLclVar: This method handles storing double register return struct value to a // local homing stack location. @@ -2638,15 +2592,14 @@ CodeGen::genCodeForTreeNode(GenTreePtr treeNode) // treeNode - the tree which should be homed in local frame stack location. // // Return Value: -// It returns true if this is a struct and storing of the returned +// For System V AMD64 sistems it returns true if this is a struct and storing of the returned // register value is handled. It returns false otherwise. -// +// For all other targets returns false. bool CodeGen::genStoreRegisterReturnInLclVar(GenTreePtr treeNode) { - - +#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING if (varTypeIsStruct(treeNode)) { GenTreeLclVarCommon* lclVarPtr = treeNode->AsLclVarCommon(); @@ -2689,84 +2642,97 @@ CodeGen::genStoreRegisterReturnInLclVar(GenTreePtr treeNode) regNumber retReg0 = REG_NA; regNumber retReg1 = REG_NA; - emitAttr size0 = EA_UNKNOWN; - emitAttr size1 = EA_UNKNOWN; - unsigned __int8 offset0 = 0; unsigned __int8 offset1 = 0; var_types type0 = TYP_UNKNOWN; var_types type1 = TYP_UNKNOWN; - bool firstIntUsed = false; - bool firstFloatUsed = false; + getStructTypeOffset(structDesc, &type0, &type1, &offset0, &offset1); + getStructReturnRegisters(type0, type1, &retReg0, &retReg1); + + assert(retReg0 != REG_NA && retReg1 != REG_NA); + + getEmitter()->emitIns_S_R(ins_Store(type0), emitTypeSize(type0), retReg0, lclVarPtr->gtLclNum, offset0); + getEmitter()->emitIns_S_R(ins_Store(type1), emitTypeSize(type1), retReg1, lclVarPtr->gtLclNum, offset1); + + return true; + } +#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING + + return false; +} - genGetStructTypeSizeOffset(structDesc, &type0, &type1, &size0, &size1, &offset0, &offset1); +#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING +//------------------------------------------------------------------------ +// getStructReturnRegisters: Returns the return registers for a specific struct types. +// +// Arguments: +// type0 - the type of the first eightbyte to be returned. +// type1 - the type of the second eightbyte to be returned. +// retRegPtr0 - returns the register for the first eightbyte. +// retRegPtr1 - returns the register for the second eightbyte. +// - if (type0 != TYP_UNKNOWN) +void +CodeGen::getStructReturnRegisters(var_types type0, + var_types type1, + regNumber* retRegPtr0, + regNumber* retRegPtr1) +{ + *retRegPtr0 = REG_NA; + *retRegPtr1 = REG_NA; + + bool firstIntUsed = false; + bool firstFloatUsed = false; + + if (type0 != TYP_UNKNOWN) + { + if (varTypeIsIntegralOrI(type0)) { - if (structDesc.eightByteClassifications[0] == SystemVClassificationTypeIntegerReference || - structDesc.eightByteClassifications[0] == SystemVClassificationTypeInteger) - { - retReg0 = REG_INTRET; - firstIntUsed = true; - } - else if (structDesc.eightByteClassifications[0] == SystemVClassificationTypeSSE) + *retRegPtr0 = REG_INTRET; + firstIntUsed = true; + } + else if (varTypeIsFloating(type0)) + { + *retRegPtr0 = REG_FLOATRET; + firstFloatUsed = true; + } + else + { + unreached(); + } + } + + if (type1 != TYP_UNKNOWN) + { + if (varTypeIsIntegralOrI(type1)) + { + if (firstIntUsed) { - retReg0 = REG_FLOATRET; - firstFloatUsed = true; + *retRegPtr1 = REG_INTRET_1; } else { - assert(false && "Invalid eightbyte type"); + *retRegPtr1 = REG_INTRET; } } - - if (type1 != TYP_UNKNOWN) + else if (varTypeIsFloating(type1)) { - if (structDesc.eightByteClassifications[1] == SystemVClassificationTypeIntegerReference || - structDesc.eightByteClassifications[1] == SystemVClassificationTypeInteger) + if (firstFloatUsed) { - if (firstIntUsed) - { - retReg1 = REG_INTRET_1; - } - else - { - retReg1 = REG_INTRET; - } - } - else if (structDesc.eightByteClassifications[1] == SystemVClassificationTypeSSE) - { - if (firstFloatUsed) - { - retReg1 = REG_FLOATRET_1; - } - else - { - retReg1 = REG_FLOATRET; - } + *retRegPtr1 = REG_FLOATRET_1; } else { - assert(false && "Invalid eightbyte type"); + *retRegPtr1 = REG_FLOATRET; } } - - if (retReg0 != REG_NA) - { - getEmitter()->emitIns_S_R(ins_Store(type0), size0, retReg0, lclVarPtr->gtLclNum, offset0); - } - - if (retReg1 != REG_NA) + else { - getEmitter()->emitIns_S_R(ins_Store(type1), size1, retReg1, lclVarPtr->gtLclNum, offset1); + unreached(); } - - return true; } - - return false; } #endif // FEATURE_UNIX_AMD64_STRUCT_PASSING @@ -5032,12 +4998,12 @@ void CodeGen::genConsumePutStructArgStk(GenTreePutArgStk* putArgNode, regNumber GenTreeLclVarCommon* lclNode = src->AsLclVarCommon(); // Generate LEA instruction to load the LclVar address in RSI. - getEmitter()->emitIns_R_S(INS_lea, EA_PTRSIZE, srcReg, lclNode->gtLclNum, 0); + getEmitter()->emitIns_R_S(INS_lea, emitTypeSize(src), srcReg, lclNode->gtLclNum, 0); } else { assert(src->gtRegNum != REG_NA); - getEmitter()->emitIns_R_R(INS_mov, EA_PTRSIZE, srcReg, src->gtRegNum); + getEmitter()->emitIns_R_R(INS_mov, emitTypeSize(src), srcReg, src->gtRegNum); } } @@ -5955,29 +5921,23 @@ void CodeGen::genCallInstruction(GenTreePtr node) #if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) //------------------------------------------------------------------------ -// genGetStructTypeSizeOffset: Gets the type, size and offset of the eightbytes of a struct for System V systems. +// getStructTypeOffset: Gets the type, size and offset of the eightbytes of a struct for System V systems. // // Arguments: // 'structDesc' struct description // 'type0' returns the type of the first eightbyte. // 'type1' returns the type of the second eightbyte. -// 'size0' returns the size of the first eightbyte. -// 'size1' returns the size of the second eightbyte. // 'offset0' returns the offset of the first eightbyte. // 'offset1' returns the offset of the second eightbyte. // -void CodeGen::genGetStructTypeSizeOffset(const SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR& structDesc, +void CodeGen::getStructTypeOffset(const SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR& structDesc, var_types* type0, var_types* type1, - emitAttr* size0, - emitAttr* size1, unsigned __int8* offset0, unsigned __int8* offset1) { - *size0 = EA_UNKNOWN; *offset0 = structDesc.eightByteOffsets[0]; - *size1 = EA_UNKNOWN; *offset1 = structDesc.eightByteOffsets[1]; *type0 = TYP_UNKNOWN; @@ -5986,97 +5946,13 @@ void CodeGen::genGetStructTypeSizeOffset(const SYSTEMV_AMD64_CORINFO_STRUCT_REG_ // Set the first eightbyte data if (structDesc.eightByteCount >= 1) { - switch (structDesc.eightByteClassifications[0]) - { - case SystemVClassificationTypeInteger: - if (structDesc.eightByteSizes[0] <= 4) - { - *size0 = EA_4BYTE; - *type0 = TYP_INT; - } - else if (structDesc.eightByteSizes[0] <= 8) - { - *size0 = EA_8BYTE; - *type0 = TYP_LONG; - } - else - { - assert(false && "Bad int type."); - } - break; - case SystemVClassificationTypeIntegerReference: - assert(structDesc.eightByteSizes[0] == REGSIZE_BYTES); - *size0 = EA_GCREF; - *type0 = TYP_REF; - break; - case SystemVClassificationTypeSSE: - if (structDesc.eightByteSizes[0] <= 4) - { - *size0 = EA_4BYTE; - *type0 = TYP_FLOAT; - } - else if (structDesc.eightByteSizes[0] <= 8) - { - *size0 = EA_8BYTE; - *type0 = TYP_DOUBLE; - } - else - { - assert(false && "Bat float type."); // Not possible. - } - break; - default: - assert(false && "Bad EightByte classification."); - break; - } + *type0 = compiler->getEightByteType(structDesc, 0); } // Set the second eight byte data if (structDesc.eightByteCount == 2) { - switch (structDesc.eightByteClassifications[1]) - { - case SystemVClassificationTypeInteger: - if (structDesc.eightByteSizes[1] <= 4) - { - *type1 = TYP_INT; - *size1 = EA_4BYTE; - } - else if (structDesc.eightByteSizes[1] <= 8) - { - *type1 = TYP_LONG; - *size1 = EA_8BYTE; - } - else - { - assert(false && "Bad int type."); - } - break; - case SystemVClassificationTypeIntegerReference: - assert(structDesc.eightByteSizes[1] == REGSIZE_BYTES); - *type1 = TYP_REF; - *size1 = EA_GCREF; - break; - case SystemVClassificationTypeSSE: - if (structDesc.eightByteSizes[1] <= 4) - { - *type1 = TYP_FLOAT; - *size1 = EA_4BYTE; - } - else if (structDesc.eightByteSizes[1] <= 8) - { - *type1 = TYP_DOUBLE; - *size1 = EA_8BYTE; - } - else - { - assert(false && "Bat float type."); // Not possible. - } - break; - default: - assert(false && "Bad EightByte classification."); - break; - } + *type1 = compiler->getEightByteType(structDesc, 1); } } #endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) @@ -6203,15 +6079,13 @@ void CodeGen::genJmpMethod(GenTreePtr jmp) compiler->eeGetSystemVAmd64PassStructInRegisterDescriptor(typeHnd, &structDesc); assert(structDesc.passedInRegisters); - emitAttr size0 = EA_UNKNOWN; - emitAttr size1 = EA_UNKNOWN; unsigned __int8 offset0 = 0; unsigned __int8 offset1 = 0; var_types type0 = TYP_UNKNOWN; var_types type1 = TYP_UNKNOWN; // Get the eightbyte data - genGetStructTypeSizeOffset(structDesc, &type0, &type1, &size0, &size1, &offset0, &offset1); + getStructTypeOffset(structDesc, &type0, &type1, &offset0, &offset1); // Move the values into the right registers. // @@ -6222,14 +6096,14 @@ void CodeGen::genJmpMethod(GenTreePtr jmp) // and after which reg life and gc info will be recomputed for the new block in genCodeForBBList(). if (type0 != TYP_UNKNOWN) { - getEmitter()->emitIns_R_S(ins_Load(type0), size0, varDsc->lvArgReg, varNum, offset0); + getEmitter()->emitIns_R_S(ins_Load(type0), emitTypeSize(type0), varDsc->lvArgReg, varNum, offset0); regSet.rsMaskVars |= genRegMask(varDsc->lvArgReg); gcInfo.gcMarkRegPtrVal(varDsc->lvArgReg, type0); } if (type1 != TYP_UNKNOWN) { - getEmitter()->emitIns_R_S(ins_Load(type1), size1, varDsc->lvOtherArgReg, varNum, offset1); + getEmitter()->emitIns_R_S(ins_Load(type1), emitTypeSize(type1), varDsc->lvOtherArgReg, varNum, offset1); regSet.rsMaskVars |= genRegMask(varDsc->lvOtherArgReg); gcInfo.gcMarkRegPtrVal(varDsc->lvOtherArgReg, type1); } @@ -8113,6 +7987,7 @@ CodeGen::genPutArgStk(GenTreePtr treeNode) } #if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + //--------------------------------------------------------------------- // genPutStructArgStk - generate code for copying a struct arg on the stack by value. // In case there are references to heap object in the struct, @@ -8172,9 +8047,11 @@ CodeGen::genPutStructArgStk(GenTreePtr treeNode, unsigned baseVarNum) // They may now contain gc pointers (depending on their type; gcMarkRegPtrVal will "do the right thing"). genConsumePutStructArgStk(putArgStk, REG_RDI, REG_RSI, REG_NA, baseVarNum); GenTreePtr dstAddr = putArgStk; - GenTreePtr srcAddr = putArgStk->gtOp.gtOp1; + GenTreePtr src = putArgStk->gtOp.gtOp1; + assert(src->OperGet() == GT_LDOBJ); + GenTreePtr srcAddr = src->gtGetOp1(); + gcInfo.gcMarkRegPtrVal(REG_RSI, srcAddr->TypeGet()); - gcInfo.gcMarkRegPtrVal(REG_RDI, dstAddr->TypeGet()); unsigned slots = putArgStk->gtNumSlots; @@ -8219,28 +8096,38 @@ CodeGen::genPutStructArgStk(GenTreePtr treeNode, unsigned baseVarNum) } } break; + + case TYPE_GC_REF: // Is an object ref + case TYPE_GC_BYREF: // Is an interior pointer - promote it but don't scan it + { + // We have a GC (byref or ref) pointer + // TODO-Amd64-Unix: Here a better solution (for code size and CQ) would be to use movsq instruction, + // but the logic for emitting a GC info record is not available (it is internal for the emitter only.) + // See emitGCVarLiveUpd function. If we could call it separately, we could do instGen(INS_movsq); and emission of gc info. + + getEmitter()->emitIns_R_AR(ins_Load(srcAddr->TypeGet()), emitTypeSize(srcAddr), REG_RCX, REG_RSI, 0); + getEmitter()->emitIns_S_R(ins_Store(srcAddr->TypeGet()), + emitTypeSize(srcAddr), + REG_RCX, + baseVarNum, + ((copiedSlots + putArgStk->gtSlotNum) * TARGET_POINTER_SIZE)); + + getEmitter()->emitIns_R_I(INS_add, emitTypeSize(srcAddr), REG_RSI, TARGET_POINTER_SIZE); + getEmitter()->emitIns_R_I(INS_add, EA_8BYTE, REG_RDI, TARGET_POINTER_SIZE); + copiedSlots++; + gcPtrCount--; + i++; + } + break; + default: - // We have a GC pointer - // TODO-Amd64-Unix: Here a better solution (for code size and CQ) would be to use movsq instruction, - // but the logic for emitting a GC info record is not available (it is internal for the emitter only.) - // See emitGCVarLiveUpd function. If we could call it separately, we could do instGen(INS_movsq); and emission of gc info. - - getEmitter()->emitIns_R_AR(ins_Load(TYP_REF), EA_GCREF, REG_RCX, REG_RSI, 0); - getEmitter()->emitIns_S_R(ins_Store(TYP_REF), - EA_GCREF, - REG_RCX, - baseVarNum, - ((copiedSlots + putArgStk->gtSlotNum) * TARGET_POINTER_SIZE)); - getEmitter()->emitIns_R_I(INS_add, EA_8BYTE, REG_RSI, TARGET_POINTER_SIZE); - getEmitter()->emitIns_R_I(INS_add, EA_8BYTE, REG_RDI, TARGET_POINTER_SIZE); - copiedSlots++; - gcPtrCount--; - i++; + unreached(); + break; } } + assert(gcPtrCount == 0); gcInfo.gcMarkRegSetNpt(RBM_RSI); - gcInfo.gcMarkRegSetNpt(RBM_RDI); } } #endif //defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) diff --git a/src/jit/compiler.cpp b/src/jit/compiler.cpp index b57f09489b70..8f5829b2665c 100644 --- a/src/jit/compiler.cpp +++ b/src/jit/compiler.cpp @@ -5737,6 +5737,9 @@ var_types Compiler::GetTypeFromClassificationAndSizes(SystemVClassificationType case SystemVClassificationTypeIntegerReference: type = TYP_REF; break; + case SystemVClassificationTypeIntegerByRef: + type = TYP_BYREF; + break; case SystemVClassificationTypeSSE: if (size <= 4) { @@ -5794,6 +5797,10 @@ var_types Compiler::getEightByteType(const SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASS assert(len == REGSIZE_BYTES); eightByteType = TYP_REF; break; + case SystemVClassificationTypeIntegerByRef: + assert(len == REGSIZE_BYTES); + eightByteType = TYP_BYREF; + break; case SystemVClassificationTypeSSE: if (structDesc.eightByteSizes[slotNum] <= 4) { diff --git a/src/jit/ee_il_dll.cpp b/src/jit/ee_il_dll.cpp index 55d3caeb81e2..0f9deac99266 100644 --- a/src/jit/ee_il_dll.cpp +++ b/src/jit/ee_il_dll.cpp @@ -977,14 +977,15 @@ void Compiler::dumpSystemVClassificationType(SystemVClassificationType ct) { switch (ct) { - case SystemVClassificationTypeUnknown: printf("UNKNOWN"); break; - case SystemVClassificationTypeStruct: printf("Struct"); break; - case SystemVClassificationTypeNoClass: printf("NoClass"); break; - case SystemVClassificationTypeMemory: printf("Memory"); break; - case SystemVClassificationTypeInteger: printf("Integer"); break; - case SystemVClassificationTypeIntegerReference: printf("IntegerReference"); break; - case SystemVClassificationTypeSSE: printf("SSE"); break; - default: printf("ILLEGAL"); break; + case SystemVClassificationTypeUnknown: printf("UNKNOWN"); break; + case SystemVClassificationTypeStruct: printf("Struct"); break; + case SystemVClassificationTypeNoClass: printf("NoClass"); break; + case SystemVClassificationTypeMemory: printf("Memory"); break; + case SystemVClassificationTypeInteger: printf("Integer"); break; + case SystemVClassificationTypeIntegerReference: printf("IntegerReference"); break; + case SystemVClassificationTypeIntegerByRef: printf("IntegerByReference"); break; + case SystemVClassificationTypeSSE: printf("SSE"); break; + default: printf("ILLEGAL"); break; } } #endif // DEBUG diff --git a/src/jit/lclvars.cpp b/src/jit/lclvars.cpp index afe8aaf4084e..9e6d787f77ba 100644 --- a/src/jit/lclvars.cpp +++ b/src/jit/lclvars.cpp @@ -666,16 +666,16 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo * varDscInfo) for (unsigned int i = 0; i < structDesc.eightByteCount; i++) { - switch (structDesc.eightByteClassifications[i]) + if (structDesc.IsIntegralSlot(i)) { - case SystemVClassificationTypeInteger: - case SystemVClassificationTypeIntegerReference: intRegCount++; - break; - case SystemVClassificationTypeSSE: + } + else if (structDesc.IsSseSlot(i)) + { floatRegCount++; - break; - default: + } + else + { assert(false && "Invalid eightbyte classification type."); break; } diff --git a/src/jit/lowerxarch.cpp b/src/jit/lowerxarch.cpp index dc9cff44bbcb..02747fba56a1 100644 --- a/src/jit/lowerxarch.cpp +++ b/src/jit/lowerxarch.cpp @@ -298,23 +298,32 @@ void Lowering::TreeNodeInfoInit(GenTree* stmt) #endif // !defined(_TARGET_64BIT_) { #ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING - if (varTypeIsStruct(tree) && - tree->gtOp.gtOp1->OperGet() == GT_LCL_VAR) + if (varTypeIsStruct(tree)) { + noway_assert((tree->gtOp.gtOp1->OperGet() == GT_LCL_VAR) || + (tree->gtOp.gtOp1->OperGet() == GT_CALL)); + + if (tree->gtOp.gtOp1->OperGet() == GT_LCL_VAR) + { #ifdef DEBUG - GenTreeLclVarCommon* lclVarPtr = tree->gtOp.gtOp1->AsLclVarCommon(); - LclVarDsc* varDsc = &(compiler->lvaTable[lclVarPtr->gtLclNum]); - assert(varDsc->lvDontPromote); + GenTreeLclVarCommon* lclVarPtr = tree->gtOp.gtOp1->AsLclVarCommon(); + LclVarDsc* varDsc = &(compiler->lvaTable[lclVarPtr->gtLclNum]); + assert(varDsc->lvDontPromote); #endif // DEBUG - // If this is a two eightbyte return, make the var - // contained by the return expression. The code gen will put - // the values in the right registers for return. - info->srcCount = (tree->TypeGet() == TYP_VOID) ? 0 : 1; - info->dstCount = 0; - MakeSrcContained(tree, tree->gtOp.gtOp1); - break; + // If this is a two eightbyte return, make the var + // contained by the return expression. The code gen will put + // the values in the right registers for return. + info->srcCount = (tree->TypeGet() == TYP_VOID) ? 0 : 1; + info->dstCount = 0; + MakeSrcContained(tree, tree->gtOp.gtOp1); + break; + } + + // If the return gtOp1 is GT_CALL, just fallthrough. The return registers should already be set properly by the GT_CALL. } #endif // FEATURE_UNIX_AMD64_STRUCT_PASSING + // TODO-AMD64-Unix: When the GT_CALL for multi-register return structs is changed to use 2 destinations, + // change the code below to use 2 src for such op1s (this is the case of op1 being a GT_CALL). info->srcCount = (tree->TypeGet() == TYP_VOID) ? 0 : 1; info->dstCount = 0; diff --git a/src/jit/morph.cpp b/src/jit/morph.cpp index 0936151ee701..846332c37490 100644 --- a/src/jit/morph.cpp +++ b/src/jit/morph.cpp @@ -3455,12 +3455,11 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* callNode) { for (unsigned int i = 0; i < structDesc.eightByteCount; i++) { - if (structDesc.eightByteClassifications[i] == SystemVClassificationTypeInteger || - structDesc.eightByteClassifications[i] == SystemVClassificationTypeIntegerReference) + if (structDesc.IsIntegralSlot(i)) { structIntRegs++; } - else if (structDesc.eightByteClassifications[i] == SystemVClassificationTypeSSE) + else if (structDesc.IsSseSlot(i)) { structFloatRegs++; } @@ -3552,8 +3551,7 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* callNode) unsigned int curFloatReg = nextFltArgRegNum; for (unsigned int i = 0; i < structDesc.eightByteCount; i++) { - if (structDesc.eightByteClassifications[i] == SystemVClassificationTypeInteger || - structDesc.eightByteClassifications[i] == SystemVClassificationTypeIntegerReference) + if (structDesc.IsIntegralSlot(i)) { if (i == 0) { @@ -3582,7 +3580,7 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* callNode) curIntReg++; } - else if (structDesc.eightByteClassifications[i] == SystemVClassificationTypeSSE) + else if (structDesc.IsSseSlot(i)) { if (i == 0) { diff --git a/src/vm/argdestination.h b/src/vm/argdestination.h index 7837e5108614..803d5f245457 100644 --- a/src/vm/argdestination.h +++ b/src/vm/argdestination.h @@ -144,7 +144,8 @@ class ArgDestination if (eightByteSize == 8) { _ASSERTE((eightByteClassification == SystemVClassificationTypeInteger) || - (eightByteClassification == SystemVClassificationTypeIntegerReference)); + (eightByteClassification == SystemVClassificationTypeIntegerReference) || + (eightByteClassification == SystemVClassificationTypeIntegerByRef)); _ASSERTE(IS_ALIGNED((SIZE_T)genRegDest, 8)); *(UINT64*)genRegDest = *(UINT64*)src; @@ -193,7 +194,8 @@ class ArgDestination if (eightByteClassification != SystemVClassificationTypeSSE) { - if (eightByteClassification == SystemVClassificationTypeIntegerReference) + if ((eightByteClassification == SystemVClassificationTypeIntegerReference) || + (eightByteClassification == SystemVClassificationTypeIntegerByRef)) { _ASSERTE(eightByteSize == 8); _ASSERTE(IS_ALIGNED((SIZE_T)genRegDest, 8)); diff --git a/src/vm/callingconvention.h b/src/vm/callingconvention.h index 97b58cc0424c..c79b2a38e175 100644 --- a/src/vm/callingconvention.h +++ b/src/vm/callingconvention.h @@ -985,6 +985,7 @@ int ArgIteratorTemplate::GetNextOffset() { case SystemVClassificationTypeInteger: case SystemVClassificationTypeIntegerReference: + case SystemVClassificationTypeIntegerByRef: cGenRegs++; break; case SystemVClassificationTypeSSE: diff --git a/src/vm/jitinterface.cpp b/src/vm/jitinterface.cpp index 519ccc266ce2..a8706dc85289 100644 --- a/src/vm/jitinterface.cpp +++ b/src/vm/jitinterface.cpp @@ -2583,7 +2583,8 @@ bool CEEInfo::getSystemVAmd64PassStructInRegisterDescriptor( // Make sure this is a value type. if (th.IsValueType()) { - _ASSERTE(CorInfoType2UnixAmd64Classification(th.GetInternalCorElementType()) == SystemVClassificationTypeStruct); + _ASSERTE((CorInfoType2UnixAmd64Classification(th.GetInternalCorElementType()) == SystemVClassificationTypeStruct) || + (CorInfoType2UnixAmd64Classification(th.GetInternalCorElementType()) == SystemVClassificationTypeTypedReference)); // The useNativeLayout in this case tracks whether the classification // is for a native layout of the struct or not. diff --git a/src/vm/methodtable.cpp b/src/vm/methodtable.cpp index f06138ad6077..d423b095898d 100644 --- a/src/vm/methodtable.cpp +++ b/src/vm/methodtable.cpp @@ -2288,14 +2288,16 @@ const char* GetSystemVClassificationTypeName(SystemVClassificationType t) { switch (t) { - case SystemVClassificationTypeUnknown: return "Unknown"; - case SystemVClassificationTypeStruct: return "Struct"; - case SystemVClassificationTypeNoClass: return "NoClass"; - case SystemVClassificationTypeMemory: return "Memory"; - case SystemVClassificationTypeInteger: return "Integer"; - case SystemVClassificationTypeIntegerReference: return "IntegerReference"; - case SystemVClassificationTypeSSE: return "SSE"; - default: return "ERROR"; + case SystemVClassificationTypeUnknown: return "Unknown"; + case SystemVClassificationTypeStruct: return "Struct"; + case SystemVClassificationTypeNoClass: return "NoClass"; + case SystemVClassificationTypeMemory: return "Memory"; + case SystemVClassificationTypeInteger: return "Integer"; + case SystemVClassificationTypeIntegerReference: return "IntegerReference"; + case SystemVClassificationTypeIntegerByRef: return "IntegerByReference"; + case SystemVClassificationTypeSSE: return "SSE"; + case SystemVClassificationTypeTypedReference: return "TypedReference"; + default: return "ERROR"; } }; #endif // _DEBUG && LOGGING @@ -2320,6 +2322,7 @@ static SystemVClassificationType ReClassifyField(SystemVClassificationType origi { _ASSERTE((newFieldClassification == SystemVClassificationTypeInteger) || (newFieldClassification == SystemVClassificationTypeIntegerReference) || + (newFieldClassification == SystemVClassificationTypeIntegerByRef) || (newFieldClassification == SystemVClassificationTypeSSE)); switch (newFieldClassification) @@ -2350,6 +2353,11 @@ static SystemVClassificationType ReClassifyField(SystemVClassificationType origi _ASSERTE(originalClassification == SystemVClassificationTypeIntegerReference); return SystemVClassificationTypeIntegerReference; + case SystemVClassificationTypeIntegerByRef: + // IntegerByReference can only merge with IntegerByReference. + _ASSERTE(originalClassification == SystemVClassificationTypeIntegerByRef); + return SystemVClassificationTypeIntegerByRef; + default: _ASSERTE(false); // Unexpected type. return SystemVClassificationTypeUnknown; @@ -2425,7 +2433,6 @@ bool MethodTable::ClassifyEightBytesWithManagedLayout(SystemVStructRegisterPassi LPCUTF8 fieldName; pField->GetName_NoThrow(&fieldName); #endif // _DEBUG - if (fieldClassificationType == SystemVClassificationTypeStruct) { TypeHandle th = pField->GetApproxFieldTypeHandleThrowing(); @@ -2458,6 +2465,67 @@ bool MethodTable::ClassifyEightBytesWithManagedLayout(SystemVStructRegisterPassi continue; } + if (fieldClassificationType == SystemVClassificationTypeTypedReference || + CorInfoType2UnixAmd64Classification(GetClass_NoLogging()->GetInternalCorElementType()) == SystemVClassificationTypeTypedReference) + { + // The TypedReference is a very special type. + // In source/metadata it has two fields - Type and Value and both are defined of type IntPtr. + // When the VM creates a layout of the type it changes the type of the Value to ByRef type and the + // type of the Type field is left to IntPtr (TYPE_I internally - native int type.) + // This requires a special treatment of this type. The code below handles the both fields (and this entire type). + + for (unsigned i = 0; i < 2; i++) + { + fieldSize = 8; + fieldOffset = (i == 0 ? 0 : 8); + normalizedFieldOffset = fieldOffset + startOffsetOfStruct; + fieldClassificationType = (i == 0 ? SystemVClassificationTypeIntegerByRef : SystemVClassificationTypeInteger); + if ((normalizedFieldOffset % fieldSize) != 0) + { + // The spec requires that struct values on the stack from register passed fields expects + // those fields to be at their natural alignment. + + LOG((LF_JIT, LL_EVERYTHING, " %*sxxxx Field %d %s: offset %d (normalized %d), size %d not at natural alignment; not enregistering struct\n", + nestingLevel * 5, "", fieldNum, fieldNum, (i == 0 ? "Value" : "Type"), fieldOffset, normalizedFieldOffset, fieldSize)); + return false; + } + + helperPtr->largestFieldOffset = (int)normalizedFieldOffset; + + // Set the data for a new field. + + // The new field classification must not have been initialized yet. + _ASSERTE(helperPtr->fieldClassifications[helperPtr->currentUniqueOffsetField] == SystemVClassificationTypeNoClass); + + // There are only a few field classifications that are allowed. + _ASSERTE((fieldClassificationType == SystemVClassificationTypeInteger) || + (fieldClassificationType == SystemVClassificationTypeIntegerReference) || + (fieldClassificationType == SystemVClassificationTypeIntegerByRef) || + (fieldClassificationType == SystemVClassificationTypeSSE)); + + helperPtr->fieldClassifications[helperPtr->currentUniqueOffsetField] = fieldClassificationType; + helperPtr->fieldSizes[helperPtr->currentUniqueOffsetField] = fieldSize; + helperPtr->fieldOffsets[helperPtr->currentUniqueOffsetField] = normalizedFieldOffset; + + LOG((LF_JIT, LL_EVERYTHING, " %*s**** Field %d %s: offset %d (normalized %d), size %d, currentUniqueOffsetField %d, field type classification %s, chosen field classification %s\n", + nestingLevel * 5, "", fieldNum, (i == 0 ? "Value" : "Type"), fieldOffset, normalizedFieldOffset, fieldSize, helperPtr->currentUniqueOffsetField, + GetSystemVClassificationTypeName(fieldClassificationType), + GetSystemVClassificationTypeName(helperPtr->fieldClassifications[helperPtr->currentUniqueOffsetField]))); + + helperPtr->currentUniqueOffsetField++; + _ASSERTE(helperPtr->currentUniqueOffsetField < SYSTEMV_MAX_NUM_FIELDS_IN_REGISTER_PASSED_STRUCT); +#ifdef _DEBUG + ++fieldNum; +#endif // _DEBUG + } + + // Both fields of the special TypedReference struct are handled. + pField = pFieldEnd; + + // Done classifying the System.TypedReference struct fields. + continue; + } + if ((normalizedFieldOffset % fieldSize) != 0) { // The spec requires that struct values on the stack from register passed fields expects @@ -2522,6 +2590,7 @@ bool MethodTable::ClassifyEightBytesWithManagedLayout(SystemVStructRegisterPassi // There are only a few field classifications that are allowed. _ASSERTE((fieldClassificationType == SystemVClassificationTypeInteger) || (fieldClassificationType == SystemVClassificationTypeIntegerReference) || + (fieldClassificationType == SystemVClassificationTypeIntegerByRef) || (fieldClassificationType == SystemVClassificationTypeSSE)); helperPtr->fieldClassifications[helperPtr->currentUniqueOffsetField] = fieldClassificationType; @@ -2589,6 +2658,7 @@ bool MethodTable::ClassifyEightBytesWithNativeLayout(SystemVStructRegisterPassin nestingLevel * 5, "", this->GetDebugClassName())); return false; } + #ifdef _DEBUG LOG((LF_JIT, LL_EVERYTHING, "%*s**** Classify for native struct %s (%p), startOffset %d, total struct size %d\n", nestingLevel * 5, "", this->GetDebugClassName(), this, startOffsetOfStruct, helperPtr->structSize)); @@ -2926,6 +2996,7 @@ bool MethodTable::ClassifyEightBytesWithNativeLayout(SystemVStructRegisterPassin // There are only a few field classifications that are allowed. _ASSERTE((fieldClassificationType == SystemVClassificationTypeInteger) || (fieldClassificationType == SystemVClassificationTypeIntegerReference) || + (fieldClassificationType == SystemVClassificationTypeIntegerByRef) || (fieldClassificationType == SystemVClassificationTypeSSE)); helperPtr->fieldClassifications[helperPtr->currentUniqueOffsetField] = fieldClassificationType; @@ -3023,7 +3094,9 @@ void MethodTable::AssignClassifiedEightByteTypes(SystemVStructRegisterPassingHe else if ((helperPtr->eightByteClassifications[currentEightByte] == SystemVClassificationTypeInteger) || (fieldClassificationType == SystemVClassificationTypeInteger)) { - _ASSERTE(fieldClassificationType != SystemVClassificationTypeIntegerReference); + _ASSERTE((fieldClassificationType != SystemVClassificationTypeIntegerReference) && + (fieldClassificationType != SystemVClassificationTypeIntegerByRef)); + helperPtr->eightByteClassifications[currentEightByte] = SystemVClassificationTypeInteger; } else if ((helperPtr->eightByteClassifications[currentEightByte] == SystemVClassificationTypeIntegerReference) || @@ -3031,6 +3104,11 @@ void MethodTable::AssignClassifiedEightByteTypes(SystemVStructRegisterPassingHe { helperPtr->eightByteClassifications[currentEightByte] = SystemVClassificationTypeIntegerReference; } + else if ((helperPtr->eightByteClassifications[currentEightByte] == SystemVClassificationTypeIntegerByRef) || + (fieldClassificationType == SystemVClassificationTypeIntegerByRef)) + { + helperPtr->eightByteClassifications[currentEightByte] = SystemVClassificationTypeIntegerByRef; + } else { helperPtr->eightByteClassifications[currentEightByte] = SystemVClassificationTypeSSE; diff --git a/src/vm/methodtable.h b/src/vm/methodtable.h index a7cfce779af7..534cbd3f2b2f 100644 --- a/src/vm/methodtable.h +++ b/src/vm/methodtable.h @@ -630,43 +630,43 @@ inline SystemVClassificationType CorInfoType2UnixAmd64Classification(CorElementType eeType) { static const SystemVClassificationType toSystemVAmd64ClassificationTypeMap[] = { - SystemVClassificationTypeUnknown, // ELEMENT_TYPE_END - SystemVClassificationTypeUnknown, // ELEMENT_TYPE_VOID - SystemVClassificationTypeInteger, // ELEMENT_TYPE_BOOLEAN - SystemVClassificationTypeInteger, // ELEMENT_TYPE_CHAR - SystemVClassificationTypeInteger, // ELEMENT_TYPE_I1 - SystemVClassificationTypeInteger, // ELEMENT_TYPE_U1 - SystemVClassificationTypeInteger, // ELEMENT_TYPE_I2 - SystemVClassificationTypeInteger, // ELEMENT_TYPE_U2 - SystemVClassificationTypeInteger, // ELEMENT_TYPE_I4 - SystemVClassificationTypeInteger, // ELEMENT_TYPE_U4 - SystemVClassificationTypeInteger, // ELEMENT_TYPE_I8 - SystemVClassificationTypeInteger, // ELEMENT_TYPE_U8 - SystemVClassificationTypeSSE, // ELEMENT_TYPE_R4 - SystemVClassificationTypeSSE, // ELEMENT_TYPE_R8 - SystemVClassificationTypeIntegerReference, // ELEMENT_TYPE_STRING - SystemVClassificationTypeInteger, // ELEMENT_TYPE_PTR - SystemVClassificationTypeIntegerReference, // ELEMENT_TYPE_BYREF - SystemVClassificationTypeStruct, // ELEMENT_TYPE_VALUETYPE - SystemVClassificationTypeIntegerReference, // ELEMENT_TYPE_CLASS - SystemVClassificationTypeIntegerReference, // ELEMENT_TYPE_VAR - (type variable) - SystemVClassificationTypeIntegerReference, // ELEMENT_TYPE_ARRAY - SystemVClassificationTypeIntegerReference, // ELEMENT_TYPE_GENERICINST - SystemVClassificationTypeStruct, // ELEMENT_TYPE_TYPEDBYREF - SystemVClassificationTypeUnknown, // ELEMENT_TYPE_VALUEARRAY_UNSUPPORTED - SystemVClassificationTypeInteger, // ELEMENT_TYPE_I - SystemVClassificationTypeInteger, // ELEMENT_TYPE_U - SystemVClassificationTypeUnknown, // ELEMENT_TYPE_R_UNSUPPORTED + SystemVClassificationTypeUnknown, // ELEMENT_TYPE_END + SystemVClassificationTypeUnknown, // ELEMENT_TYPE_VOID + SystemVClassificationTypeInteger, // ELEMENT_TYPE_BOOLEAN + SystemVClassificationTypeInteger, // ELEMENT_TYPE_CHAR + SystemVClassificationTypeInteger, // ELEMENT_TYPE_I1 + SystemVClassificationTypeInteger, // ELEMENT_TYPE_U1 + SystemVClassificationTypeInteger, // ELEMENT_TYPE_I2 + SystemVClassificationTypeInteger, // ELEMENT_TYPE_U2 + SystemVClassificationTypeInteger, // ELEMENT_TYPE_I4 + SystemVClassificationTypeInteger, // ELEMENT_TYPE_U4 + SystemVClassificationTypeInteger, // ELEMENT_TYPE_I8 + SystemVClassificationTypeInteger, // ELEMENT_TYPE_U8 + SystemVClassificationTypeSSE, // ELEMENT_TYPE_R4 + SystemVClassificationTypeSSE, // ELEMENT_TYPE_R8 + SystemVClassificationTypeIntegerReference, // ELEMENT_TYPE_STRING + SystemVClassificationTypeInteger, // ELEMENT_TYPE_PTR + SystemVClassificationTypeIntegerByRef, // ELEMENT_TYPE_BYREF + SystemVClassificationTypeStruct, // ELEMENT_TYPE_VALUETYPE + SystemVClassificationTypeIntegerReference, // ELEMENT_TYPE_CLASS + SystemVClassificationTypeIntegerReference, // ELEMENT_TYPE_VAR (type variable) + SystemVClassificationTypeIntegerReference, // ELEMENT_TYPE_ARRAY + SystemVClassificationTypeIntegerReference, // ELEMENT_TYPE_GENERICINST + SystemVClassificationTypeTypedReference, // ELEMENT_TYPE_TYPEDBYREF + SystemVClassificationTypeUnknown, // ELEMENT_TYPE_VALUEARRAY_UNSUPPORTED + SystemVClassificationTypeInteger, // ELEMENT_TYPE_I + SystemVClassificationTypeInteger, // ELEMENT_TYPE_U + SystemVClassificationTypeUnknown, // ELEMENT_TYPE_R_UNSUPPORTED // put the correct type when we know our implementation - SystemVClassificationTypeInteger, // ELEMENT_TYPE_FNPTR - SystemVClassificationTypeIntegerReference, // ELEMENT_TYPE_OBJECT - SystemVClassificationTypeIntegerReference, // ELEMENT_TYPE_SZARRAY - SystemVClassificationTypeIntegerReference, // ELEMENT_TYPE_MVAR - - SystemVClassificationTypeUnknown, // ELEMENT_TYPE_CMOD_REQD - SystemVClassificationTypeUnknown, // ELEMENT_TYPE_CMOD_OPT - SystemVClassificationTypeUnknown, // ELEMENT_TYPE_INTERNAL + SystemVClassificationTypeInteger, // ELEMENT_TYPE_FNPTR + SystemVClassificationTypeIntegerReference, // ELEMENT_TYPE_OBJECT + SystemVClassificationTypeIntegerReference, // ELEMENT_TYPE_SZARRAY + SystemVClassificationTypeIntegerReference, // ELEMENT_TYPE_MVAR + + SystemVClassificationTypeUnknown, // ELEMENT_TYPE_CMOD_REQD + SystemVClassificationTypeUnknown, // ELEMENT_TYPE_CMOD_OPT + SystemVClassificationTypeUnknown, // ELEMENT_TYPE_INTERNAL }; _ASSERTE(sizeof(toSystemVAmd64ClassificationTypeMap) == ELEMENT_TYPE_MAX); @@ -674,7 +674,9 @@ SystemVClassificationType CorInfoType2UnixAmd64Classification(CorElementType eeT // spot check of the map _ASSERTE((SystemVClassificationType)toSystemVAmd64ClassificationTypeMap[ELEMENT_TYPE_I4] == SystemVClassificationTypeInteger); _ASSERTE((SystemVClassificationType)toSystemVAmd64ClassificationTypeMap[ELEMENT_TYPE_PTR] == SystemVClassificationTypeInteger); - _ASSERTE((SystemVClassificationType)toSystemVAmd64ClassificationTypeMap[ELEMENT_TYPE_TYPEDBYREF] == SystemVClassificationTypeStruct); + _ASSERTE((SystemVClassificationType)toSystemVAmd64ClassificationTypeMap[ELEMENT_TYPE_VALUETYPE] == SystemVClassificationTypeStruct); + _ASSERTE((SystemVClassificationType)toSystemVAmd64ClassificationTypeMap[ELEMENT_TYPE_TYPEDBYREF] == SystemVClassificationTypeTypedReference); + _ASSERTE((SystemVClassificationType)toSystemVAmd64ClassificationTypeMap[ELEMENT_TYPE_BYREF] == SystemVClassificationTypeIntegerByRef); return (((int)eeType) < ELEMENT_TYPE_MAX) ? (toSystemVAmd64ClassificationTypeMap[eeType]) : SystemVClassificationTypeUnknown; }; diff --git a/src/vm/threadsuspend.cpp b/src/vm/threadsuspend.cpp index 705ae835c700..83b5eed507e1 100644 --- a/src/vm/threadsuspend.cpp +++ b/src/vm/threadsuspend.cpp @@ -7348,7 +7348,8 @@ void STDCALL OnHijackStructInRegsWorker(HijackArgs * pArgs) int orefCount = 0; for (int i = 0; i < eeClass->GetNumberEightBytes(); i++) { - if (eeClass->GetEightByteClassification(i) == SystemVClassificationTypeIntegerReference) + if ((eeClass->GetEightByteClassification(i) == SystemVClassificationTypeIntegerReference) || + (eeClass->GetEightByteClassification(i) == SystemVClassificationTypeIntegerByRef)) { oref[orefCount++] = ObjectToOBJECTREF(*(Object **) &pArgs->ReturnValue[i]); } @@ -7396,7 +7397,8 @@ void STDCALL OnHijackStructInRegsWorker(HijackArgs * pArgs) orefCount = 0; for (int i = 0; i < eeClass->GetNumberEightBytes(); i++) { - if (eeClass->GetEightByteClassification(i) == SystemVClassificationTypeIntegerReference) + if ((eeClass->GetEightByteClassification(i) == SystemVClassificationTypeIntegerReference) || + (eeClass->GetEightByteClassification(i) == SystemVClassificationTypeIntegerByRef)) { *((OBJECTREF *) &pArgs->ReturnValue[i]) = oref[orefCount++]; }