From c9eaaacdd8800cc813c2588d47a49f65a69d6aae Mon Sep 17 00:00:00 2001 From: Sergey Andreenko Date: Wed, 7 Mar 2018 17:26:27 -0800 Subject: [PATCH] [RyuJIt] Clean and fix gen return (#16725) * create genSimpleReturn for arm64 * create genFloatReturn for x86 * create genLongReturn for x86 and arm * merge genReturn --- src/jit/codegenarm.cpp | 89 --------------------- src/jit/codegenarm64.cpp | 87 ++++++-------------- src/jit/codegencommon.cpp | 199 ++++++++++++++++++++++++++++++++++++++++++++++ src/jit/codegenlinear.h | 12 +++ src/jit/codegenxarch.cpp | 193 +++++++------------------------------------- 5 files changed, 266 insertions(+), 314 deletions(-) diff --git a/src/jit/codegenarm.cpp b/src/jit/codegenarm.cpp index b5f9d7c..60f11fb 100644 --- a/src/jit/codegenarm.cpp +++ b/src/jit/codegenarm.cpp @@ -240,95 +240,6 @@ void CodeGen::genCodeForBinary(GenTree* treeNode) } //------------------------------------------------------------------------ -// 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(GenTree* treeNode) -{ - assert(treeNode->OperGet() == GT_RETURN || treeNode->OperGet() == GT_RETFILT); - GenTree* op1 = treeNode->gtGetOp1(); - var_types targetType = treeNode->TypeGet(); - - // A void GT_RETFILT is the end of a finally. For non-void filter returns we need to load the result in the return - // register, if it's not already there. The processing is the same as GT_RETURN. For filters, the IL spec says the - // result is type int32. Further, the only legal values are 0 or 1; the use of other values is "undefined". - assert(!treeNode->OperIs(GT_RETFILT) || (targetType == TYP_VOID) || (targetType == TYP_INT)); - -#ifdef DEBUG - if (targetType == TYP_VOID) - { - assert(op1 == nullptr); - } -#endif - - 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 - { - 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; - if (varTypeIsFloating(treeNode) && (compiler->opts.compUseSoftFP || compiler->info.compIsVarArgs)) - { - if (targetType == TYP_FLOAT) - { - getEmitter()->emitIns_R_R(INS_vmov_f2i, EA_4BYTE, REG_INTRET, op1->gtRegNum); - } - else - { - assert(targetType == TYP_DOUBLE); - getEmitter()->emitIns_R_R_R(INS_vmov_d2i, EA_8BYTE, REG_INTRET, REG_NEXT(REG_INTRET), - op1->gtRegNum); - } - } - else if (op1->gtRegNum != retReg) - { - inst_RV_RV(ins_Move_Extend(targetType, true), retReg, op1->gtRegNum, targetType); - } - } - } -} - -//------------------------------------------------------------------------ // genLockedInstructions: Generate code for the locked operations. // // Notes: diff --git a/src/jit/codegenarm64.cpp b/src/jit/codegenarm64.cpp index 44a5661..efefbf1 100644 --- a/src/jit/codegenarm64.cpp +++ b/src/jit/codegenarm64.cpp @@ -1775,91 +1775,56 @@ void CodeGen::genCodeForStoreLclVar(GenTreeLclVar* tree) } //------------------------------------------------------------------------ -// genReturn: Generates code for return statement. -// In case of struct return, delegates to the genStructReturn method. +// genSimpleReturn: Generates code for simple return statement for arm64. +// +// Note: treeNode's and op1's registers are already consumed. // // Arguments: -// treeNode - The GT_RETURN or GT_RETFILT tree node. +// treeNode - The GT_RETURN or GT_RETFILT tree node with non-struct and non-void type // // Return Value: // None // -void CodeGen::genReturn(GenTree* treeNode) +void CodeGen::genSimpleReturn(GenTree* treeNode) { assert(treeNode->OperGet() == GT_RETURN || treeNode->OperGet() == GT_RETFILT); GenTree* op1 = treeNode->gtGetOp1(); var_types targetType = treeNode->TypeGet(); - // A void GT_RETFILT is the end of a finally. For non-void filter returns we need to load the result in the return - // register, if it's not already there. The processing is the same as GT_RETURN. For filters, the IL spec says the - // result is type int32. Further, the only legal values are 0 or 1; the use of other values is "undefined". - assert(!treeNode->OperIs(GT_RETFILT) || (targetType == TYP_VOID) || (targetType == TYP_INT)); - -#ifdef DEBUG - if (targetType == TYP_VOID) - { - assert(op1 == nullptr); - } -#endif - - if (isStructReturn(treeNode)) - { - genStructReturn(treeNode); - } - else if (targetType != TYP_VOID) - { - assert(op1 != nullptr); - noway_assert(op1->gtRegNum != REG_NA); - - genConsumeReg(op1); + assert(!isStructReturn(treeNode)); + assert(targetType != TYP_VOID); - regNumber retReg = varTypeIsFloating(treeNode) ? REG_FLOATRET : REG_INTRET; + regNumber retReg = varTypeIsFloating(treeNode) ? REG_FLOATRET : REG_INTRET; - bool movRequired = (op1->gtRegNum != retReg); + bool movRequired = (op1->gtRegNum != retReg); - if (!movRequired) + if (!movRequired) + { + if (op1->OperGet() == GT_LCL_VAR) { - if (op1->OperGet() == GT_LCL_VAR) + GenTreeLclVarCommon* lcl = op1->AsLclVarCommon(); + bool isRegCandidate = compiler->lvaTable[lcl->gtLclNum].lvIsRegCandidate(); + if (isRegCandidate && ((op1->gtFlags & GTF_SPILLED) == 0)) { - GenTreeLclVarCommon* lcl = op1->AsLclVarCommon(); - bool isRegCandidate = compiler->lvaTable[lcl->gtLclNum].lvIsRegCandidate(); - if (isRegCandidate && ((op1->gtFlags & GTF_SPILLED) == 0)) - { - // We may need to generate a zero-extending mov instruction to load the value from this GT_LCL_VAR + // We may need to generate a zero-extending mov instruction to load the value from this GT_LCL_VAR - unsigned lclNum = lcl->gtLclNum; - LclVarDsc* varDsc = &(compiler->lvaTable[lclNum]); - var_types op1Type = genActualType(op1->TypeGet()); - var_types lclType = genActualType(varDsc->TypeGet()); + unsigned lclNum = lcl->gtLclNum; + LclVarDsc* varDsc = &(compiler->lvaTable[lclNum]); + var_types op1Type = genActualType(op1->TypeGet()); + var_types lclType = genActualType(varDsc->TypeGet()); - if (genTypeSize(op1Type) < genTypeSize(lclType)) - { - movRequired = true; - } + if (genTypeSize(op1Type) < genTypeSize(lclType)) + { + movRequired = true; } } } - - if (movRequired) - { - emitAttr attr = emitActualTypeSize(targetType); - getEmitter()->emitIns_R_R(INS_mov, attr, retReg, op1->gtRegNum); - } } - -#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) + if (movRequired) { - genProfilingLeaveCallback(); + emitAttr attr = emitActualTypeSize(targetType); + getEmitter()->emitIns_R_R(INS_mov, attr, retReg, op1->gtRegNum); } -#endif } /*********************************************************************************************** diff --git a/src/jit/codegencommon.cpp b/src/jit/codegencommon.cpp index f57abb0..22491c8 100644 --- a/src/jit/codegencommon.cpp +++ b/src/jit/codegencommon.cpp @@ -12783,4 +12783,203 @@ GenTreeIntCon CodeGen::intForm(var_types type, ssize_t value) return i; } +#if defined(_TARGET_X86_) || defined(_TARGET_ARM_) +//------------------------------------------------------------------------ +// genLongReturn: Generates code for long return statement for x86 and arm. +// +// Note: treeNode's and op1's registers are already consumed. +// +// Arguments: +// treeNode - The GT_RETURN or GT_RETFILT tree node with LONG return type. +// +// Return Value: +// None +// +void CodeGen::genLongReturn(GenTree* treeNode) +{ + assert(treeNode->OperGet() == GT_RETURN || treeNode->OperGet() == GT_RETFILT); + assert(treeNode->TypeGet() == TYP_LONG); + GenTree* op1 = treeNode->gtGetOp1(); + var_types targetType = treeNode->TypeGet(); + + assert(op1 != nullptr); + assert(op1->OperGet() == GT_LONG); + GenTree* loRetVal = op1->gtGetOp1(); + GenTree* hiRetVal = op1->gtGetOp2(); + 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 // _TARGET_X86_ || _TARGET_ARM_ + +//------------------------------------------------------------------------ +// 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(GenTree* treeNode) +{ + assert(treeNode->OperGet() == GT_RETURN || treeNode->OperGet() == GT_RETFILT); + GenTree* op1 = treeNode->gtGetOp1(); + var_types targetType = treeNode->TypeGet(); + + // A void GT_RETFILT is the end of a finally. For non-void filter returns we need to load the result in the return + // register, if it's not already there. The processing is the same as GT_RETURN. For filters, the IL spec says the + // result is type int32. Further, the only legal values are 0 or 1; the use of other values is "undefined". + assert(!treeNode->OperIs(GT_RETFILT) || (targetType == TYP_VOID) || (targetType == TYP_INT)); + +#ifdef DEBUG + if (targetType == TYP_VOID) + { + assert(op1 == nullptr); + } +#endif // DEBUG + +#if defined(_TARGET_X86_) || defined(_TARGET_ARM_) + if (targetType == TYP_LONG) + { + genLongReturn(treeNode); + } + else +#endif // _TARGET_X86_ || _TARGET_ARM_ + { + 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); + +#if defined(_TARGET_ARM64_) + genSimpleReturn(treeNode); +#else // !_TARGET_ARM64_ +#if defined(_TARGET_X86_) + if (varTypeIsFloating(treeNode)) + { + genFloatReturn(treeNode); + } + else +#elif defined(_TARGET_ARM_) + if (varTypeIsFloating(treeNode) && (compiler->opts.compUseSoftFP || compiler->info.compIsVarArgs)) + { + if (targetType == TYP_FLOAT) + { + getEmitter()->emitIns_R_R(INS_vmov_f2i, EA_4BYTE, REG_INTRET, op1->gtRegNum); + } + else + { + assert(targetType == TYP_DOUBLE); + getEmitter()->emitIns_R_R_R(INS_vmov_d2i, EA_8BYTE, REG_INTRET, REG_NEXT(REG_INTRET), + op1->gtRegNum); + } + } + else +#endif // _TARGET_ARM_ + { + regNumber retReg = varTypeIsFloating(treeNode) ? REG_FLOATRET : REG_INTRET; + if (op1->gtRegNum != retReg) + { + inst_RV_RV(ins_Move_Extend(targetType, true), retReg, op1->gtRegNum, targetType); + } + } +#endif // !_TARGET_ARM64_ + } + } + +#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 + // BBJ_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. + ReturnTypeDesc retTypeDesc; + unsigned regCount = 0; + if (compiler->compMethodReturnsMultiRegRetType()) + { + if (varTypeIsLong(compiler->info.compRetNativeType)) + { + retTypeDesc.InitializeLongReturnType(compiler); + } + else // we must have a struct return type + { + retTypeDesc.InitializeStructReturnType(compiler, compiler->info.compMethodInfo->args.retTypeClass); + } + regCount = retTypeDesc.GetReturnRegCount(); + } + + if (varTypeIsGC(compiler->info.compRetType)) + { + gcInfo.gcMarkRegPtrVal(REG_INTRET, compiler->info.compRetType); + } + else if (compiler->compMethodReturnsMultiRegRetType()) + { + for (unsigned i = 0; i < regCount; ++i) + { + if (varTypeIsGC(retTypeDesc.GetReturnRegType(i))) + { + gcInfo.gcMarkRegPtrVal(retTypeDesc.GetABIReturnReg(i), retTypeDesc.GetReturnRegType(i)); + } + } + } + + genProfilingLeaveCallback(); + + if (varTypeIsGC(compiler->info.compRetType)) + { + gcInfo.gcMarkRegSetNpt(genRegMask(REG_INTRET)); + } + else if (compiler->compMethodReturnsMultiRegRetType()) + { + for (unsigned i = 0; i < regCount; ++i) + { + if (varTypeIsGC(retTypeDesc.GetReturnRegType(i))) + { + gcInfo.gcMarkRegSetNpt(genRegMask(retTypeDesc.GetABIReturnReg(i))); + } + } + } + } +#endif // PROFILING_SUPPORTED +} + #endif // !LEGACY_BACKEND diff --git a/src/jit/codegenlinear.h b/src/jit/codegenlinear.h index f1ff362..c36e423 100644 --- a/src/jit/codegenlinear.h +++ b/src/jit/codegenlinear.h @@ -327,6 +327,18 @@ void genMultiRegCallStoreToLocal(GenTree* treeNode); bool isStructReturn(GenTree* treeNode); void genStructReturn(GenTree* treeNode); +#if defined(_TARGET_X86_) || defined(_TARGET_ARM_) +void genLongReturn(GenTree* treeNode); +#endif // _TARGET_X86_ || _TARGET_ARM_ + +#if defined(_TARGET_X86_) +void genFloatReturn(GenTree* treeNode); +#endif // _TARGET_X86_ + +#if defined(_TARGET_ARM64_) +void genSimpleReturn(GenTree* treeNode); +#endif // _TARGET_ARM64_ + void genReturn(GenTree* treeNode); void genLclHeap(GenTree* tree); diff --git a/src/jit/codegenxarch.cpp b/src/jit/codegenxarch.cpp index b5cb2c8..0c00e4a 100644 --- a/src/jit/codegenxarch.cpp +++ b/src/jit/codegenxarch.cpp @@ -1252,189 +1252,54 @@ void CodeGen::genStructReturn(GenTree* treeNode) #endif } +#if defined(_TARGET_X86_) + //------------------------------------------------------------------------ -// genReturn: Generates code for return statement. -// In case of struct return, delegates to the genStructReturn method. +// genFloatReturn: Generates code for float return statement for x86. +// +// Note: treeNode's and op1's registers are already consumed. // // Arguments: -// treeNode - The GT_RETURN or GT_RETFILT tree node. +// treeNode - The GT_RETURN or GT_RETFILT tree node with float type. // // Return Value: // None // -void CodeGen::genReturn(GenTree* treeNode) +void CodeGen::genFloatReturn(GenTree* treeNode) { assert(treeNode->OperGet() == GT_RETURN || treeNode->OperGet() == GT_RETFILT); - GenTree* op1 = treeNode->gtGetOp1(); - var_types targetType = treeNode->TypeGet(); - - // A void GT_RETFILT is the end of a finally. For non-void filter returns we need to load the result in the return - // register, if it's not already there. The processing is the same as GT_RETURN. For filters, the IL spec says the - // result is type int32. Further, the only legal values are 0 or 1; the use of other values is "undefined". - assert(!treeNode->OperIs(GT_RETFILT) || (targetType == TYP_VOID) || (targetType == TYP_INT)); - -#ifdef DEBUG - if (targetType == TYP_VOID) - { - assert(op1 == nullptr); - } -#endif + assert(varTypeIsFloating(treeNode)); -#ifdef _TARGET_X86_ - if (treeNode->TypeGet() == TYP_LONG) + GenTree* op1 = treeNode->gtGetOp1(); + // Spill the return value register from an XMM register to the stack, then load it on the x87 stack. + // If it already has a home location, use that. Otherwise, we need a temp. + if (genIsRegCandidateLocal(op1) && compiler->lvaTable[op1->gtLclVarCommon.gtLclNum].lvOnFrame) { - 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) + if (compiler->lvaTable[op1->gtLclVarCommon.gtLclNum].lvRegNum != REG_STK) { - inst_RV_RV(ins_Copy(targetType), REG_LNGRET_HI, hiRetVal->gtRegNum, TYP_INT); + op1->gtFlags |= GTF_SPILL; + 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 -#endif // !defined(_TARGET_X86_) { - if (isStructReturn(treeNode)) - { - genStructReturn(treeNode); - } - else if (targetType != TYP_VOID) - { - assert(op1 != nullptr); - noway_assert(op1->gtRegNum != REG_NA); + // 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; - // !! 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)) - { - // Spill the return value register from an XMM register to the stack, then load it on the x87 stack. - // If it already has a home location, use that. Otherwise, we need a temp. - if (genIsRegCandidateLocal(op1) && compiler->lvaTable[op1->gtLclVarCommon.gtLclNum].lvOnFrame) - { - if (compiler->lvaTable[op1->gtLclVarCommon.gtLclNum].lvRegNum != REG_STK) - { - op1->gtFlags |= GTF_SPILL; - 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, op1->gtRegNum); - 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); - } - } - } + TempDsc* t = regSet.rsUnspillInPlace(op1, op1->gtRegNum); + inst_FS_ST(INS_fld, emitActualTypeSize(op1->gtType), t, 0); + op1->gtFlags &= ~GTF_SPILLED; + compiler->tmpRlsTemp(t); } - -#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 - // BBJ_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. - ReturnTypeDesc retTypeDesc; - unsigned regCount = 0; - if (compiler->compMethodReturnsMultiRegRetType()) - { - if (varTypeIsLong(compiler->info.compRetNativeType)) - { - retTypeDesc.InitializeLongReturnType(compiler); - } - else // we must have a struct return type - { - retTypeDesc.InitializeStructReturnType(compiler, compiler->info.compMethodInfo->args.retTypeClass); - } - regCount = retTypeDesc.GetReturnRegCount(); - } - - if (varTypeIsGC(compiler->info.compRetType)) - { - gcInfo.gcMarkRegPtrVal(REG_INTRET, compiler->info.compRetType); - } - else if (compiler->compMethodReturnsMultiRegRetType()) - { - for (unsigned i = 0; i < regCount; ++i) - { - if (varTypeIsGC(retTypeDesc.GetReturnRegType(i))) - { - gcInfo.gcMarkRegPtrVal(retTypeDesc.GetABIReturnReg(i), retTypeDesc.GetReturnRegType(i)); - } - } - } - - genProfilingLeaveCallback(); - - if (varTypeIsGC(compiler->info.compRetType)) - { - gcInfo.gcMarkRegSetNpt(genRegMask(REG_INTRET)); - } - else if (compiler->compMethodReturnsMultiRegRetType()) - { - for (unsigned i = 0; i < regCount; ++i) - { - if (varTypeIsGC(retTypeDesc.GetReturnRegType(i))) - { - gcInfo.gcMarkRegSetNpt(genRegMask(retTypeDesc.GetABIReturnReg(i))); - } - } - } - } -#endif } +#endif // _TARGET_X86_ //------------------------------------------------------------------------ // genCodeForCompare: Produce code for a GT_EQ/GT_NE/GT_LT/GT_LE/GT_GE/GT_GT/GT_TEST_EQ/GT_TEST_NE/GT_CMP node. -- 2.7.4