diff options
Diffstat (limited to 'src/jit/codegenarm64.cpp')
-rw-r--r-- | src/jit/codegenarm64.cpp | 728 |
1 files changed, 596 insertions, 132 deletions
diff --git a/src/jit/codegenarm64.cpp b/src/jit/codegenarm64.cpp index 2f8879ac0b..b6dc645112 100644 --- a/src/jit/codegenarm64.cpp +++ b/src/jit/codegenarm64.cpp @@ -2354,6 +2354,311 @@ 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: +// Returns true if the 'treeNode" is a GT_RETURN node of type struct. +// Otherwise 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); + + return varTypeIsStruct(treeNode); +} + +//------------------------------------------------------------------------ +// genStructReturn: Generates code for returning a struct. +// +// Arguments: +// treeNode - The GT_RETURN tree node. +// +// Return Value: +// None +// +// Assumption: +// op1 of GT_RETURN node is either GT_LCL_VAR or multi-reg GT_CALL +void +CodeGen::genStructReturn(GenTreePtr treeNode) +{ + assert(treeNode->OperGet() == GT_RETURN); + assert(isStructReturn(treeNode)); + GenTreePtr op1 = treeNode->gtGetOp1(); + + if (op1->OperGet() == GT_LCL_VAR) + { + GenTreeLclVarCommon* lclVar = op1->AsLclVarCommon(); + LclVarDsc* varDsc = &(compiler->lvaTable[lclVar->gtLclNum]); + var_types lclType = genActualType(varDsc->TypeGet()); + + // Currently only multireg TYP_STRUCT types such as HFA's and 16-byte structs are supported + // In the future we could have FEATURE_SIMD types like TYP_SIMD16 + assert(lclType == TYP_STRUCT); + assert(varDsc->lvIsMultiRegRet); + + ReturnTypeDesc retTypeDesc; + unsigned regCount; + + retTypeDesc.InitializeStructReturnType(compiler, varDsc->lvVerTypeInfo.GetClassHandle()); + regCount = retTypeDesc.GetReturnRegCount(); + + assert(regCount >= 2); + assert(op1->isContained()); + + // Copy var on stack into ABI return registers + int offset = 0; + for (unsigned i = 0; i < regCount; ++i) + { + var_types type = retTypeDesc.GetReturnRegType(i); + regNumber reg = retTypeDesc.GetABIReturnReg(i); + getEmitter()->emitIns_R_S(ins_Load(type), emitTypeSize(type), reg, lclVar->gtLclNum, offset); + offset += genTypeSize(type); + } + } + else // op1 must be multi-reg GT_CALL + { + assert(op1->IsMultiRegCall() || op1->IsCopyOrReloadOfMultiRegCall()); + + genConsumeRegs(op1); + + GenTree* actualOp1 = op1->gtSkipReloadOrCopy(); + GenTreeCall* call = actualOp1->AsCall(); + + ReturnTypeDesc* pRetTypeDesc; + unsigned regCount; + unsigned matchingCount = 0; + + pRetTypeDesc = call->GetReturnTypeDesc(); + regCount = pRetTypeDesc->GetReturnRegCount(); + + var_types regType [MAX_RET_REG_COUNT]; + regNumber returnReg [MAX_RET_REG_COUNT]; + regNumber allocatedReg[MAX_RET_REG_COUNT]; + regMaskTP srcRegsMask = 0; + regMaskTP dstRegsMask = 0; + bool needToShuffleRegs = false; // Set to true if we have to move any registers + + for (unsigned i = 0; i < regCount; ++i) + { + regType[i] = pRetTypeDesc->GetReturnRegType(i); + returnReg[i] = pRetTypeDesc->GetABIReturnReg(i); + + regNumber reloadReg = REG_NA; + if (op1->IsCopyOrReload()) + { + // GT_COPY/GT_RELOAD will have valid reg for those positions + // that need to be copied or reloaded. + reloadReg = op1->AsCopyOrReload()->GetRegNumByIdx(i); + } + + if (reloadReg != REG_NA) + { + allocatedReg[i] = reloadReg; + } + else + { + allocatedReg[i] = call->GetRegNumByIdx(i); + } + + if (returnReg[i] == allocatedReg[i]) + { + matchingCount++; + } + else // We need to move this value + { + // We want to move the value from allocatedReg[i] into returnReg[i] + // so record these two registers in the src and dst masks + // + srcRegsMask |= genRegMask(allocatedReg[i]); + dstRegsMask |= genRegMask(returnReg[i]); + + needToShuffleRegs = true; + } + } + + if (needToShuffleRegs) + { + assert(matchingCount < regCount); + + unsigned remainingRegCount = regCount - matchingCount; + regMaskTP extraRegMask = treeNode->gtRsvdRegs; + + while (remainingRegCount > 0) + { + // set 'available' to the 'dst' registers that are not currently holding 'src' registers + // + regMaskTP availableMask = dstRegsMask & ~srcRegsMask; + + regMaskTP dstMask; + regNumber srcReg; + regNumber dstReg; + var_types curType = TYP_UNKNOWN; + regNumber freeUpReg = REG_NA; + + if (availableMask == 0) + { + // Circular register dependencies + // So just free up the lowest register in dstRegsMask by moving it to the 'extra' register + + assert(dstRegsMask == srcRegsMask); // this has to be true for us to reach here + assert(extraRegMask != 0); // we require an 'extra' register + assert((extraRegMask & ~dstRegsMask) != 0); // it can't be part of dstRegsMask + + availableMask = extraRegMask & ~dstRegsMask; + + regMaskTP srcMask = genFindLowestBit(srcRegsMask); + freeUpReg = genRegNumFromMask(srcMask); + } + + dstMask = genFindLowestBit(availableMask); + dstReg = genRegNumFromMask(dstMask); + srcReg = REG_NA; + + if (freeUpReg != REG_NA) + { + // We will free up the srcReg by moving it to dstReg which is an extra register + // + srcReg = freeUpReg; + + // Find the 'srcReg' and set 'curType', change allocatedReg[] to dstReg + // and add the new register mask bit to srcRegsMask + // + for (unsigned i = 0; i < regCount; ++i) + { + if (allocatedReg[i] == srcReg) + { + curType = regType[i]; + allocatedReg[i] = dstReg; + srcRegsMask |= genRegMask(dstReg); + } + } + } + else // The normal case + { + // Find the 'srcReg' and set 'curType' + // + for (unsigned i = 0; i < regCount; ++i) + { + if (returnReg[i] == dstReg) + { + srcReg = allocatedReg[i]; + curType = regType[i]; + } + } + // After we perform this move we will have one less registers to setup + remainingRegCount--; + } + assert(curType != TYP_UNKNOWN); + + inst_RV_RV(ins_Copy(curType), dstReg, srcReg, curType); + + // Clear the appropriate bits in srcRegsMask and dstRegsMask + srcRegsMask &= ~genRegMask(srcReg); + dstRegsMask &= ~genRegMask(dstReg); + + } // while (remainingRegCount > 0) + + } // (needToShuffleRegs) + + } // op1 must be multi-reg GT_CALL + +} + + +//------------------------------------------------------------------------ +// 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 + + if (isStructReturn(treeNode)) + { + genStructReturn(treeNode); + } + else if (targetType != TYP_VOID) + { + assert(op1 != nullptr); + noway_assert(op1->gtRegNum != REG_NA); + + genConsumeReg(op1); + + regNumber retReg = varTypeIsFloating(treeNode) ? REG_FLOATRET : REG_INTRET; + + bool movRequired = (op1->gtRegNum != retReg); + + if (!movRequired) + { + if (op1->OperGet() == GT_LCL_VAR) + { + GenTreeLclVarCommon *lcl = op1->AsLclVarCommon(); + bool isRegCandidate = compiler->lvaTable[lcl->gtLclNum].lvIsRegCandidate(); + if (isRegCandidate && ((op1->gtFlags & GTF_SPILLED) == 0)) + { + assert(op1->InReg()); + + // 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()); + + if (genTypeSize(op1Type) < genTypeSize(lclType)) + { + movRequired = true; + } + } + } + } + + if (movRequired) + { + emitAttr movSize = EA_ATTR(genTypeSize(targetType)); + getEmitter()->emitIns_R_R(INS_mov, movSize, 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) + { + genProfilingLeaveCallback(); + } +#endif +} + /***************************************************************************** * * Generate code for a single node in the tree. @@ -2679,28 +2984,19 @@ CodeGen::genCodeForTreeNode(GenTreePtr treeNode) break; case GT_STORE_LCL_FLD: - case GT_STORE_LCL_VAR: { - if (targetType == TYP_STRUCT) - { - NYI("GT_STORE_LCL_VAR/FLD with TYP_STRUCT"); - } noway_assert(targetType != TYP_STRUCT); - GenTreeLclVarCommon* varNode = treeNode->AsLclVarCommon(); + // record the offset + unsigned offset = treeNode->gtLclFld.gtLclOffs; + // We must have a stack store with GT_STORE_LCL_FLD + noway_assert(!treeNode->InReg()); + noway_assert(targetReg == REG_NA); + + GenTreeLclVarCommon* varNode = treeNode->AsLclVarCommon(); unsigned varNum = varNode->gtLclNum; assert(varNum < compiler->lvaCount); LclVarDsc* varDsc = &(compiler->lvaTable[varNum]); - unsigned offset = 0; - - if (treeNode->gtOper == GT_STORE_LCL_FLD) - { - // record the offset, only used with GT_STORE_LCL_FLD - offset = treeNode->gtLclFld.gtLclOffs; - // We must have a stack store with GT_STORE_LCL_FLD - noway_assert(!treeNode->InReg()); - noway_assert(targetReg == REG_NA); - } // Ensure that lclVar nodes are typed correctly. assert(!varDsc->lvNormalizeOnStore() || targetType == genActualType(varDsc->TypeGet())); @@ -2722,114 +3018,99 @@ CodeGen::genCodeForTreeNode(GenTreePtr treeNode) } assert(dataReg != REG_NA); - if (targetReg == REG_NA) // store into stack based LclVar - { - // Only true gtLclVar subclass nodes currently have a gtLclILoffs instance field - // - if(treeNode->gtOper != GT_STORE_LCL_FLD) - { - inst_set_SV_var(varNode); - } + instruction ins = ins_Store(targetType); - instruction ins = ins_Store(targetType); - emitAttr attr = emitTypeSize(targetType); + emitAttr attr = emitTypeSize(targetType); - attr = emit->emitInsAdjustLoadStoreAttr(ins, attr); + attr = emit->emitInsAdjustLoadStoreAttr(ins, attr); - emit->emitIns_S_R(ins, attr, dataReg, varNum, offset); + emit->emitIns_S_R(ins, attr, dataReg, varNum, offset); - genUpdateLife(varNode); - - varDsc->lvRegNum = REG_STK; - } - else // store into register (i.e move into register) - { - if (dataReg != targetReg) - { - // Assign into targetReg when dataReg (from op1) is not the same register - inst_RV_RV(ins_Copy(targetType), targetReg, dataReg, targetType); - } - genProduceReg(treeNode); - } + genUpdateLife(varNode); + + varDsc->lvRegNum = REG_STK; } break; - case GT_RETFILT: - // 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. - if (targetType != TYP_VOID) + case GT_STORE_LCL_VAR: { - // For filters, the IL spec says the result is type int32. Further, the only specified legal values - // are 0 or 1, with the use of other values "undefined". - assert(targetType == TYP_INT); - } + GenTreeLclVarCommon* varNode = treeNode->AsLclVarCommon(); - __fallthrough; + unsigned varNum = varNode->gtLclNum; assert(varNum < compiler->lvaCount); + LclVarDsc* varDsc = &(compiler->lvaTable[varNum]); + unsigned offset = 0; - case GT_RETURN: - { - GenTreePtr op1 = treeNode->gtOp.gtOp1; - if (targetType == TYP_VOID) + // Ensure that lclVar nodes are typed correctly. + assert(!varDsc->lvNormalizeOnStore() || targetType == genActualType(varDsc->TypeGet())); + + GenTreePtr data = treeNode->gtOp.gtOp1->gtEffectiveVal(); + + // var = call, where call returns a multi-reg return value + // case is handled separately. + if (data->gtSkipReloadOrCopy()->IsMultiRegCall()) { - assert(op1 == nullptr); + genMultiRegCallStoreToLocal(treeNode); } else { - assert(op1 != nullptr); - noway_assert(op1->gtRegNum != REG_NA); + genConsumeRegs(data); + + regNumber dataReg = REG_NA; + if (data->isContainedIntOrIImmed()) + { + assert(data->IsIntegralConst(0)); + dataReg = REG_ZR; + } + else + { + assert(!data->isContained()); + genConsumeReg(data); + dataReg = data->gtRegNum; + } + assert(dataReg != REG_NA); - genConsumeReg(op1); + if (targetReg == REG_NA) // store into stack based LclVar + { + inst_set_SV_var(varNode); + + instruction ins = ins_Store(targetType); + emitAttr attr = emitTypeSize(targetType); - regNumber retReg = varTypeIsFloating(treeNode) ? REG_FLOATRET : REG_INTRET; + attr = emit->emitInsAdjustLoadStoreAttr(ins, attr); - bool movRequired = (op1->gtRegNum != retReg); + emit->emitIns_S_R(ins, attr, dataReg, varNum, offset); - if (!movRequired) + genUpdateLife(varNode); + + varDsc->lvRegNum = REG_STK; + } + else // store into register (i.e move into register) { - if (op1->OperGet() == GT_LCL_VAR) + if (dataReg != targetReg) { - GenTreeLclVarCommon *lcl = op1->AsLclVarCommon(); - bool isRegCandidate = compiler->lvaTable[lcl->gtLclNum].lvIsRegCandidate(); - if (isRegCandidate && ((op1->gtFlags & GTF_SPILLED) == 0)) - { - assert(op1->InReg()); - - // 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()); - - if (genTypeSize(op1Type) < genTypeSize(lclType)) - { - movRequired = true; - } - } + // Assign into targetReg when dataReg (from op1) is not the same register + inst_RV_RV(ins_Copy(targetType), targetReg, dataReg, targetType); } - } - - if (movRequired) - { - emitAttr movSize = EA_ATTR(genTypeSize(targetType)); - emit->emitIns_R_R(INS_mov, movSize, retReg, op1->gtRegNum); + genProduceReg(treeNode); } } + } + break; -#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) - { - genProfilingLeaveCallback(); - } -#endif + case GT_RETFILT: + // 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. + if (targetType != TYP_VOID) + { + // For filters, the IL spec says the result is type int32. Further, the only specified legal values + // are 0 or 1, with the use of other values "undefined". + assert(targetType == TYP_INT); } + + __fallthrough; + + case GT_RETURN: + genReturn(treeNode); break; case GT_LEA: @@ -3363,6 +3644,80 @@ CodeGen::genCodeForTreeNode(GenTreePtr treeNode) } } +//---------------------------------------------------------------------------------- +// genMultiRegCallStoreToLocal: store multi-reg return value of a call node to a local +// +// Arguments: +// treeNode - Gentree of GT_STORE_LCL_VAR +// +// Return Value: +// None +// +// Assumption: +// The child of store is a multi-reg call node. +// genProduceReg() on treeNode is made by caller of this routine. +// +void +CodeGen::genMultiRegCallStoreToLocal(GenTreePtr treeNode) +{ + assert(treeNode->OperGet() == GT_STORE_LCL_VAR); + + // Structs of size >=9 and <=16 are returned in two return registers on ARM64 and HFAs. + assert(varTypeIsStruct(treeNode)); + + // Assumption: current ARM64 implementation requires that a multi-reg struct + // var in 'var = call' is flagged as lvIsMultiRegRet to prevent it from + // being struct promoted. + unsigned lclNum = treeNode->AsLclVarCommon()->gtLclNum; + LclVarDsc* varDsc = &(compiler->lvaTable[lclNum]); + noway_assert(varDsc->lvIsMultiRegRet); + + GenTree* op1 = treeNode->gtGetOp1(); + GenTree* actualOp1 = op1->gtSkipReloadOrCopy(); + GenTreeCall* call = actualOp1->AsCall(); + assert(call->HasMultiRegRetVal()); + + genConsumeRegs(op1); + + ReturnTypeDesc* pRetTypeDesc = call->GetReturnTypeDesc(); + unsigned regCount = pRetTypeDesc->GetReturnRegCount(); + + if (treeNode->gtRegNum != REG_NA) + { + // Right now the only enregistrable structs supported are SIMD types. + assert(varTypeIsSIMD(treeNode)); + NYI("GT_STORE_LCL_VAR of a SIMD enregisterable struct"); + } + else + { + // Stack store + int offset = 0; + for (unsigned i = 0; i < regCount; ++i) + { + var_types type = pRetTypeDesc->GetReturnRegType(i); + regNumber reg = call->GetRegNumByIdx(i); + if (op1->IsCopyOrReload()) + { + // GT_COPY/GT_RELOAD will have valid reg for those positions + // that need to be copied or reloaded. + regNumber reloadReg = op1->AsCopyOrReload()->GetRegNumByIdx(i); + if (reloadReg != REG_NA) + { + reg = reloadReg; + } + } + + assert(reg != REG_NA); + getEmitter()->emitIns_S_R(ins_Store(type), emitTypeSize(type), reg, lclNum, offset); + offset += genTypeSize(type); + } + + varDsc->lvRegNum = REG_STK; + } +} + + + /*********************************************************************************************** * Generate code for localloc */ @@ -4540,6 +4895,7 @@ void CodeGen::genUnspillRegIfNeeded(GenTree *tree) { unspillTree = tree->gtOp.gtOp1; } + if (unspillTree->gtFlags & GTF_SPILLED) { if (genIsRegCandidateLocal(unspillTree)) @@ -4601,22 +4957,72 @@ void CodeGen::genUnspillRegIfNeeded(GenTree *tree) regSet.AddMaskVars(genGetRegMask(varDsc)); } + + gcInfo.gcMarkRegPtrVal(dstReg, unspillTree->TypeGet()); + } + else if (unspillTree->IsMultiRegCall()) + { + GenTreeCall* call = unspillTree->AsCall(); + ReturnTypeDesc* pRetTypeDesc = call->GetReturnTypeDesc(); + unsigned regCount = pRetTypeDesc->GetReturnRegCount(); + GenTreeCopyOrReload* reloadTree = nullptr; + if (tree->OperGet() == GT_RELOAD) + { + reloadTree = tree->AsCopyOrReload(); + } + + // In case of multi-reg call node, GTF_SPILLED flag on it indicates that + // one or more of its result regs are spilled. Call node needs to be + // queried to know which specific result regs to be unspilled. + for (unsigned i = 0; i < regCount; ++i) + { + unsigned flags = call->GetRegSpillFlagByIdx(i); + if ((flags & GTF_SPILLED) != 0) + { + var_types dstType = pRetTypeDesc->GetReturnRegType(i); + regNumber unspillTreeReg = call->GetRegNumByIdx(i); + + if (reloadTree != nullptr) + { + dstReg = reloadTree->GetRegNumByIdx(i); + if (dstReg == REG_NA) + { + dstReg = unspillTreeReg; + } + } + else + { + dstReg = unspillTreeReg; + } + + TempDsc* t = regSet.rsUnspillInPlace(call, unspillTreeReg, i); + getEmitter()->emitIns_R_S(ins_Load(dstType), + emitActualTypeSize(dstType), + dstReg, + t->tdTempNum(), + 0); + compiler->tmpRlsTemp(t); + gcInfo.gcMarkRegPtrVal(dstReg, dstType); + } + } + + unspillTree->gtFlags &= ~GTF_SPILLED; + unspillTree->SetInReg(); } else { TempDsc* t = regSet.rsUnspillInPlace(unspillTree, unspillTree->gtRegNum); getEmitter()->emitIns_R_S(ins_Load(unspillTree->gtType), - emitActualTypeSize(unspillTree->gtType), - dstReg, - t->tdTempNum(), - 0); + emitActualTypeSize(unspillTree->TypeGet()), + dstReg, + t->tdTempNum(), + 0); compiler->tmpRlsTemp(t); unspillTree->gtFlags &= ~GTF_SPILLED; unspillTree->SetInReg(); - } - - gcInfo.gcMarkRegPtrVal(dstReg, unspillTree->TypeGet()); + gcInfo.gcMarkRegPtrVal(dstReg, unspillTree->TypeGet()); + } } } @@ -4920,6 +5326,7 @@ void CodeGen::genEmitCall(int callType, INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO* sigInfo) void* addr, emitAttr retSize, + emitAttr secondRetSize, IL_OFFSETX ilOffset, regNumber base, bool isJump, @@ -4932,6 +5339,7 @@ void CodeGen::genEmitCall(int callType, addr, 0, retSize, + secondRetSize, gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, @@ -4950,6 +5358,7 @@ void CodeGen::genEmitCall(int callType, INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO* sigInfo) GenTreeIndir* indir, emitAttr retSize, + emitAttr secondRetSize, IL_OFFSETX ilOffset) { genConsumeAddress(indir->Addr()); @@ -4960,6 +5369,7 @@ void CodeGen::genEmitCall(int callType, nullptr, 0, retSize, + secondRetSize, gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, @@ -5098,16 +5508,29 @@ void CodeGen::genCallInstruction(GenTreePtr node) genDefineTempLabel(genCreateTempLabel()); } - // Determine return value size. + // Determine return value size(s). + ReturnTypeDesc* pRetTypeDesc = call->GetReturnTypeDesc(); emitAttr retSize = EA_PTRSIZE; - if (call->gtType == TYP_REF || - call->gtType == TYP_ARRAY) + emitAttr secondRetSize = EA_UNKNOWN; + + if (call->HasMultiRegRetVal()) { - retSize = EA_GCREF; + retSize = emitTypeSize(pRetTypeDesc->GetReturnRegType(0)); + secondRetSize = emitTypeSize(pRetTypeDesc->GetReturnRegType(1)); } - else if (call->gtType == TYP_BYREF) + else { - retSize = EA_BYREF; + assert(!varTypeIsStruct(call)); + + if (call->gtType == TYP_REF || + call->gtType == TYP_ARRAY) + { + retSize = EA_GCREF; + } + else if (call->gtType == TYP_BYREF) + { + retSize = EA_BYREF; + } } #ifdef DEBUGGING_SUPPORT @@ -5136,6 +5559,7 @@ void CodeGen::genCallInstruction(GenTreePtr node) INDEBUG_LDISASM_COMMA(sigInfo) nullptr, //addr retSize, + secondRetSize, ilOffset, genConsumeReg(target)); } @@ -5191,6 +5615,7 @@ void CodeGen::genCallInstruction(GenTreePtr node) INDEBUG_LDISASM_COMMA(sigInfo) nullptr, //addr retSize, + secondRetSize, ilOffset, REG_IP0); #else @@ -5200,6 +5625,7 @@ void CodeGen::genCallInstruction(GenTreePtr node) INDEBUG_LDISASM_COMMA(sigInfo) addr, retSize, + secondRetSize, ilOffset); #endif } @@ -5225,11 +5651,44 @@ void CodeGen::genCallInstruction(GenTreePtr node) var_types returnType = call->TypeGet(); if (returnType != TYP_VOID) { - regNumber returnReg = (varTypeIsFloating(returnType) ? REG_FLOATRET : REG_INTRET); - if (call->gtRegNum != returnReg) + regNumber returnReg; + + if (call->HasMultiRegRetVal()) { - inst_RV_RV(ins_Copy(returnType), call->gtRegNum, returnReg, returnType); + assert(pRetTypeDesc != nullptr); + unsigned regCount = pRetTypeDesc->GetReturnRegCount(); + + // If regs allocated to call node are different from ABI return + // regs in which the call has returned its result, move the result + // to regs allocated to call node. + for (unsigned i = 0; i < regCount; ++i) + { + var_types regType = pRetTypeDesc->GetReturnRegType(i); + returnReg = pRetTypeDesc->GetABIReturnReg(i); + regNumber allocatedReg = call->GetRegNumByIdx(i); + if (returnReg != allocatedReg) + { + inst_RV_RV(ins_Copy(regType), allocatedReg, returnReg, regType); + } + } + } + else + { + if (varTypeIsFloating(returnType)) + { + returnReg = REG_FLOATRET; + } + else + { + returnReg = REG_INTRET; + } + + if (call->gtRegNum != returnReg) + { + inst_RV_RV(ins_Copy(returnType), call->gtRegNum, returnReg, returnType); + } } + genProduceReg(call); } @@ -5375,14 +5834,18 @@ void CodeGen::genJmpMethod(GenTreePtr jmp) regSet.AddMaskVars(genRegMask(argReg)); gcInfo.gcMarkRegPtrVal(argReg, loadType); - if (varDsc->lvIsMultiregStruct()) + if (compiler->lvaIsMultiregStruct(varDsc)) { if (varDsc->lvIsHfa()) { NYI_ARM64("CodeGen::genJmpMethod with multireg HFA arg"); + // use genRegArgNextFloat + } + else + { + // Restore the second register. + argRegNext = genRegArgNext(argReg); } - // Restore the next register. - argRegNext = genMapRegArgNumToRegNum(genMapRegNumToRegArgNum(argReg, loadType) + 1, loadType); loadType = compiler->getJitGCType(varDsc->lvGcLayout[1]); loadSize = emitActualTypeSize(loadType); getEmitter()->emitIns_R_S(ins_Load(loadType), loadSize, argRegNext, varNum, TARGET_POINTER_SIZE); @@ -5404,7 +5867,7 @@ void CodeGen::genJmpMethod(GenTreePtr jmp) fixedIntArgMask |= genRegMask(argReg); - if (varDsc->lvIsMultiregStruct()) + if (compiler->lvaIsMultiregStruct(varDsc)) { assert(argRegNext != REG_NA); fixedIntArgMask |= genRegMask(argRegNext); @@ -6867,19 +7330,20 @@ void CodeGen::genEmitHelperCall(unsigned helper, } getEmitter()->emitIns_Call(callType, - compiler->eeFindHelper(helper), - INDEBUG_LDISASM_COMMA(nullptr) - addr, - argSize, - retSize, - gcInfo.gcVarPtrSetCur, - gcInfo.gcRegGCrefSetCur, - gcInfo.gcRegByrefSetCur, - BAD_IL_OFFSET, /* IL offset */ - callTarget, /* ireg */ - REG_NA, 0, 0, /* xreg, xmul, disp */ - false, /* isJump */ - emitter::emitNoGChelper(helper)); + compiler->eeFindHelper(helper), + INDEBUG_LDISASM_COMMA(nullptr) + addr, + argSize, + retSize, + EA_UNKNOWN, + gcInfo.gcVarPtrSetCur, + gcInfo.gcRegGCrefSetCur, + gcInfo.gcRegByrefSetCur, + BAD_IL_OFFSET, /* IL offset */ + callTarget, /* ireg */ + REG_NA, 0, 0, /* xreg, xmul, disp */ + false, /* isJump */ + emitter::emitNoGChelper(helper)); regMaskTP killMask = compiler->compHelperCallKillSet((CorInfoHelpFunc)helper); regTracker.rsTrashRegSet(killMask); |