diff options
Diffstat (limited to 'src/jit/codegencommon.cpp')
-rw-r--r-- | src/jit/codegencommon.cpp | 303 |
1 files changed, 250 insertions, 53 deletions
diff --git a/src/jit/codegencommon.cpp b/src/jit/codegencommon.cpp index 0828a160c9..ea3cce6cc8 100644 --- a/src/jit/codegencommon.cpp +++ b/src/jit/codegencommon.cpp @@ -3648,7 +3648,7 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, RegState *regState) { #ifdef DEBUG - if (verbose) + if (verbose) printf("*************** In genFnPrologCalleeRegArgs() for %s regs\n", regState->rsIsFloat ? "float" : "int"); #endif @@ -3678,6 +3678,9 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, struct { unsigned varNum; // index into compiler->lvaTable[] for this register argument +#if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + var_types type; // the Jit type of this regArgTab entry +#endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) unsigned trashBy; // index into this regArgTab[] table of the register that will be copied to this register. // That is, for regArgTab[x].trashBy = y, argument register number 'y' will be copied to // argument register number 'x'. Only used when circular = true. @@ -3691,18 +3694,20 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, unsigned varNum; LclVarDsc * varDsc; - for (varNum = 0, varDsc = compiler->lvaTable; varNum < compiler->lvaCount; - varNum++ , varDsc++) + varNum++, varDsc++) { /* Is this variable a register arg? */ - - if (!varDsc->lvIsParam) + if (!varDsc->lvIsParam) + { continue; + } - if (!varDsc->lvIsRegArg) + if (!varDsc->lvIsRegArg) + { continue; + } // When we have a promoted struct we have two possible LclVars that can represent the incoming argument // in the regArgTab[], either the original TYP_STRUCT argument or the introduced lvStructField. @@ -3726,13 +3731,17 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, // For register arguments that are independent promoted structs we put the promoted field varNum in the regArgTab[] if (varDsc->lvPromoted) + { continue; + } } else { // For register arguments that are not independent promoted structs we put the parent struct varNum in the regArgTab[] if (varDsc->lvIsStructField) + { continue; + } } } @@ -3743,19 +3752,89 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, var_types regType = varDsc->TypeGet(); #endif // !_TARGET_ARM_ - if (isFloatRegType(regType) != doingFloat) - continue; +#if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + if (regType != TYP_STRUCT) +#endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + { + // A struct might be passed partially in XMM register for System V calls. + // So a single arg might use both register files. + if (isFloatRegType(regType) != doingFloat) + { + continue; + } + } - /* Bingo - add it to our table */ - - regArgNum = genMapRegNumToRegArgNum(varDsc->lvArgReg, regType); - noway_assert(regArgNum < regState->rsCalleeRegArgNum); - noway_assert(regArgTab[regArgNum].slot == 0); // we better not have added it already (there better not be multiple vars representing this argument register) + int slots = 0; - regArgTab[regArgNum].varNum = varNum; - regArgTab[regArgNum].slot = 1; +#if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR structDesc; + if (varDsc->TypeGet() == TYP_STRUCT) + { + CORINFO_CLASS_HANDLE typeHnd = varDsc->lvVerTypeInfo.GetClassHandle(); + assert(typeHnd != nullptr); + compiler->eeGetSystemVAmd64PassStructInRegisterDescriptor(typeHnd, &structDesc); + if (!structDesc.passedInRegisters) + { + // The var is not passed in registers. + continue; + } - int slots = 1; + unsigned firstRegSlot = 0; + for (unsigned slotCounter = 0; slotCounter < structDesc.eightByteCount; slotCounter++) + { + regNumber regNum = varDsc->lvRegNumForSlot(slotCounter); + + var_types regType = compiler->getEightByteType(structDesc, slotCounter); + + regArgNum = genMapRegNumToRegArgNum(regNum, regType); + + if ((!doingFloat && + ((structDesc.eightByteClassifications[slotCounter] == SystemVClassificationTypeInteger) || + (structDesc.eightByteClassifications[slotCounter] == SystemVClassificationTypeIntegerReference))) || + (doingFloat && structDesc.eightByteClassifications[slotCounter] == SystemVClassificationTypeSSE)) + { + // Store the reg for the first slot. + if (slots == 0) + { + firstRegSlot = regArgNum; + } + + // Bingo - add it to our table + noway_assert(regArgNum < regState->rsCalleeRegArgNum); + noway_assert(regArgTab[regArgNum].slot == 0); // we better not have added it already (there better not be multiple vars representing this argument register) + regArgTab[regArgNum].varNum = varNum; + regArgTab[regArgNum].slot = (char)(slotCounter + 1); + regArgTab[regArgNum].type = regType; + slots++; + } + } + + if (slots == 0) + { + continue; // Nothing to do for this regState set. + } + + regArgNum = firstRegSlot; + } + else +#endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + { + // Bingo - add it to our table + regArgNum = genMapRegNumToRegArgNum(varDsc->lvArgReg, regType); + noway_assert(regArgNum < regState->rsCalleeRegArgNum); + // we better not have added it already (there better not be multiple vars representing this argument register) + noway_assert(regArgTab[regArgNum].slot == 0); + + // Set the register type. +#if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + regArgTab[regArgNum].type = regType; +#endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + + regArgTab[regArgNum].varNum = varNum; + regArgTab[regArgNum].slot = 1; + + slots = 1; + } #ifdef _TARGET_ARM_ int lclSize = compiler->lvaLclSize(varNum); @@ -3778,9 +3857,23 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, for (int i = 0; i < slots; i ++) { +#if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + // For structs passed in registers on System V systems, + // get the regType from the table for each slot. + if (regType == TYP_STRUCT) + { + regType = regArgTab[regArgNum + i].type; + } +#endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) regNumber regNum = genMapRegArgNumToRegNum(regArgNum + i, regType); - assert((i > 0) || (regNum == varDsc->lvArgReg)); + // lvArgReg could be INT or FLOAT reg. So the following assertion doesn't hold. + // The type of the register depends on the classification of the first eightbyte + // of the struct. For information on classification refer to the System V x86_64 ABI at: + // http://www.x86-64.org/documentation/abi.pdf +#if !defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + assert((i > 0) || (regNum == varDsc->lvArgReg)); +#endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) // Is the arg dead on entry to the method ? if ((regArgMaskLive & genRegMask(regNum)) == 0) @@ -3831,8 +3924,8 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, /* If it goes on the stack or in a register that doesn't hold * an argument anymore -> CANNOT form a circular dependency */ - if ( varDsc->lvIsInReg() && - (genRegMask(regNum) & regArgMaskLive) ) + if (varDsc->lvIsInReg() && + (genRegMask(regNum) & regArgMaskLive)) { /* will trash another argument -> possible dependency * We may need several passes after the table is constructed @@ -3841,22 +3934,33 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, /* Maybe the argument stays in the register (IDEAL) */ if ((i == 0) && (varDsc->lvRegNum == regNum)) + { goto NON_DEP; + } +#if !defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + if ((i == 1) && (varDsc->TypeGet() == TYP_STRUCT) && + (varDsc->lvOtherReg == regNum)) + { + goto NON_DEP; + } +#endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) if ((i == 1) && (genActualType(varDsc->TypeGet()) == TYP_LONG) && - (varDsc->lvOtherReg == regNum)) + (varDsc->lvOtherReg == regNum)) + { goto NON_DEP; + } if ((i == 1) && (genActualType(varDsc->TypeGet()) == TYP_DOUBLE) && - (REG_NEXT(varDsc->lvRegNum) == regNum)) + (REG_NEXT(varDsc->lvRegNum) == regNum)) + { goto NON_DEP; - + } regArgTab[regArgNum+i].circular = true; } else { NON_DEP: - regArgTab[regArgNum+i].circular = false; /* mark the argument register as free */ @@ -3870,7 +3974,6 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, * such that R1->R2 (that is, R1 needs to be moved to R2), R2->R3, ..., Rn->R1 */ bool change = true; - if (regArgMaskLive) { /* Possible circular dependencies still exist; the previous pass was not enough @@ -3882,15 +3985,20 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, for (argNum = 0; argNum < regState->rsCalleeRegArgNum; argNum++) { - /* If we already marked the argument as non-circular then continue */ + // If we already marked the argument as non-circular then continue if (!regArgTab[argNum].circular) + { continue; + } if (regArgTab[argNum].slot == 0) // Not a register argument + { continue; + } - varNum = regArgTab[argNum].varNum; noway_assert(varNum < compiler->lvaCount); + varNum = regArgTab[argNum].varNum; + noway_assert(varNum < compiler->lvaCount); varDsc = compiler->lvaTable + varNum; noway_assert(varDsc->lvIsParam && varDsc->lvIsRegArg); @@ -3899,11 +4007,19 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, noway_assert(!regArgTab[argNum].stackArg); regNumber regNum = genMapRegArgNumToRegNum(argNum, varDsc->TypeGet()); + regNumber destRegNum; if (regArgTab[argNum].slot == 1) { destRegNum = varDsc->lvRegNum; } +#if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + else + { + assert(regArgTab[argNum].slot == 2); + destRegNum = varDsc->lvOtherReg; + } +#else // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) else if (regArgTab[argNum].slot == 2 && genActualType(varDsc->TypeGet()) == TYP_LONG) { @@ -3915,7 +4031,7 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, assert(varDsc->TypeGet() == TYP_DOUBLE); destRegNum = REG_NEXT(varDsc->lvRegNum); } - +#endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) if (genRegMask(destRegNum) & regArgMaskLive) { /* we are trashing a live argument register - record it */ @@ -3949,33 +4065,47 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, } #endif - // TODO-AMD64-Bug? - homing of float argument registers with circular dependencies. -#ifdef _TARGET_AMD64_ - NYI_IF((regArgMaskLive & RBM_FLTARG_REGS) != 0, "Homing of float argument registers with circular dependencies not implemented"); -#endif // _TARGET_AMD64_ + // LSRA allocates registers to incoming parameters in order and will not overwrite + // a register still holding a live parameter. +#ifndef LEGACY_BACKEND + noway_assert(((regArgMaskLive & RBM_FLTARG_REGS) == 0) && "Homing of float argument registers with circular dependencies not implemented."); +#endif // LEGACY_BACKEND /* Now move the arguments to their locations. * First consider ones that go on the stack since they may * free some registers. */ regArgMaskLive = regState->rsCalleeRegArgMaskLiveIn; // reset the live in to what it was at the start - for (argNum = 0; argNum < regState->rsCalleeRegArgNum; argNum++) { emitAttr size; - /* If the arg is dead on entry to the method, skip it */ + // If this is the wrong register file, just continue. +#if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + if (regArgTab[argNum].type == TYP_UNDEF) + { + // This could happen if the reg in regArgTab[argNum] is of the other register file - + // for System V register passed structs where the first reg is GPR and the second an XMM reg. + // The next register file processing will process it. + continue; + } +#endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + // If the arg is dead on entry to the method, skip it if (regArgTab[argNum].processed) + { continue; + } if (regArgTab[argNum].slot == 0) // Not a register argument + { continue; + } varNum = regArgTab[argNum].varNum; noway_assert(varNum < compiler->lvaCount); varDsc = compiler->lvaTable + varNum; - /* If not a stack arg go to the next one */ + // If not a stack arg go to the next one #ifndef _TARGET_64BIT_ if (varDsc->lvType == TYP_LONG) @@ -3993,7 +4123,9 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, #endif // !_TARGET_64BIT_ { if (!regArgTab[argNum].stackArg) + { continue; + } } #if defined(_TARGET_ARM_) @@ -4021,10 +4153,15 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, { size = EA_SIZE(varDsc->lvSize()); #if defined(_TARGET_AMD64_) - storeType = (var_types) ((size <= 4) ? TYP_INT : TYP_I_IMPL); +#ifndef FEATURE_UNIX_AMD64_STRUCT_PASSING + storeType = (var_types)((size <= 4) ? TYP_INT : TYP_I_IMPL); // Must be 1, 2, 4, or 8, or else it wouldn't be passed in a register noway_assert(EA_SIZE_IN_BYTES(size) <= 8); assert((EA_SIZE_IN_BYTES(size) & (EA_SIZE_IN_BYTES(size) - 1)) == 0); +#else // !FEATURE_UNIX_AMD64_STRUCT_PASSING + storeType = regArgTab[argNum].type; + size = emitActualTypeSize(storeType); +#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING #elif defined(_TARGET_ARM64_) // Must be <= 16 bytes or else it wouldn't be passed in registers noway_assert(EA_SIZE_IN_BYTES(size) <= 16); @@ -4060,7 +4197,7 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, regNumber srcRegNum = genMapRegArgNumToRegNum(argNum, storeType); - /* Stack argument - if the ref count is 0 don't care about it */ + // Stack argument - if the ref count is 0 don't care about it if (!varDsc->lvOnFrame) { @@ -4084,6 +4221,7 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, regArgTab[argNum].processed = true; regArgMaskLive &= ~genRegMask(srcRegNum); + #if defined(_TARGET_ARM_) if (storeType == TYP_DOUBLE) { @@ -4094,7 +4232,6 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, } /* Process any circular dependencies */ - if (regArgMaskLive) { unsigned begReg, destReg, srcReg; @@ -4105,21 +4242,39 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, if (doingFloat) { +#if defined(_TARGET_ARM_) || defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) #if defined(_TARGET_ARM_) insCopy = INS_vmov; - +#elif defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + insCopy = INS_mov; +#else +#error Error. Wrong architecture. +#endif // Compute xtraReg here when we have a float argument assert(xtraReg == REG_NA); regMaskTP fpAvailMask; fpAvailMask = RBM_FLT_CALLEE_TRASH & ~regArgMaskLive; +#if defined(_TARGET_ARM_) fpAvailMask &= RBM_DBL_REGS; +#elif defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + fpAvailMask &= RBM_ALLFLOAT; +#else +#error Error. Wrong architecture. +#endif + if (fpAvailMask == RBM_NONE) { fpAvailMask = RBM_ALLFLOAT & ~regArgMaskLive; +#if defined(_TARGET_ARM_) fpAvailMask &= RBM_DBL_REGS; +#elif defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + fpAvailMask &= RBM_ALLFLOAT; +#else +#error Error. Wrong architecture. +#endif } assert(fpAvailMask != RBM_NONE); @@ -4135,23 +4290,30 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, for (argNum = 0; argNum < regState->rsCalleeRegArgNum; argNum++) { - /* If not a circular dependency then continue */ - + // If not a circular dependency then continue if (!regArgTab[argNum].circular) + { continue; + } - /* If already processed the dependency then continue */ + // If already processed the dependency then continue if (regArgTab[argNum].processed) + { continue; + } if (regArgTab[argNum].slot == 0) // Not a register argument + { continue; - + } + destReg = begReg = argNum; - srcReg = regArgTab[argNum].trashBy; noway_assert(srcReg < regState->rsCalleeRegArgNum); + srcReg = regArgTab[argNum].trashBy; + noway_assert(srcReg < regState->rsCalleeRegArgNum); - varNumDest = regArgTab[destReg].varNum; noway_assert(varNumDest < compiler->lvaCount); + varNumDest = regArgTab[destReg].varNum; + noway_assert(varNumDest < compiler->lvaCount); varDscDest = compiler->lvaTable + varNumDest; noway_assert(varDscDest->lvIsParam && varDscDest->lvIsRegArg); @@ -4376,6 +4538,18 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, varDsc = compiler->lvaTable + varNum; regNumber regNum = genMapRegArgNumToRegNum(argNum, varDsc->TypeGet()); + // If this is the wrong register file, just continue. +#if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + if (regArgTab[argNum].type == TYP_UNDEF) + { + // This could happen if the reg in regArgTab[argNum] is of the other register file - + // for System V register passed structs where the first reg is GPR and the second an XMM reg. + // The next register file processing will process it. + regArgMaskLive &= ~genRegMask(regNum); + continue; + } +#endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + noway_assert(varDsc->lvIsParam && varDsc->lvIsRegArg); #ifndef _WIN64 //Right now we think that incoming arguments are not pointer sized. When we eventually @@ -4506,7 +4680,7 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, } #endif } - + noway_assert(regArgMaskLiveSave != regArgMaskLive); // if it doesn't change, we have an infinite loop } } @@ -6729,12 +6903,14 @@ void CodeGen::genProfilingEnterCallback(regNumber initReg, regNumber argReg = varDsc->lvArgReg; getEmitter()->emitIns_R_S(ins_Load(loadType), emitTypeSize(loadType), argReg, varNum, 0); +#if FEATURE_VARARG if (compiler->info.compIsVarArgs && varTypeIsFloating(loadType)) { regNumber intArgReg = compiler->getCallArgIntRegister(argReg); instruction ins = ins_CopyFloatToInt(loadType, TYP_LONG); inst_RV_RV(ins, argReg, intArgReg, loadType); } +#endif // FEATURE_VARARG } // If initReg is one of RBM_CALLEE_TRASH, then it needs to be zero'ed before using. @@ -8495,6 +8671,7 @@ void CodeGen::genFnProlog() #endif // !LEGACY_BACKEND RegState *regState; + FOREACH_REGISTER_FILE(regState) { if (regState->rsCalleeRegArgMaskLiveIn) @@ -10789,8 +10966,8 @@ void CodeGen::genRestoreCalleeSavedFltRegs(unsigned lclFrameSize) //------------------------------------------------------------------------ // ARM-specific methods used by both the classic and RyuJIT //------------------------------------------------------------------------ -#ifdef _TARGET_ARM_ -CORINFO_CLASS_HANDLE Compiler::GetHfaClassHandle(GenTreePtr tree) +#if defined(_TARGET_ARM_) || defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) +CORINFO_CLASS_HANDLE Compiler::GetStructClassHandle(GenTreePtr tree) { if (tree->TypeGet() == TYP_STRUCT) { @@ -10809,7 +10986,7 @@ CORINFO_CLASS_HANDLE Compiler::GetHfaClassHandle(GenTreePtr tree) case GT_RETURN: assert(tree->gtOp.gtOp1->gtOper == GT_LCL_VAR); - return GetHfaClassHandle(tree->gtOp.gtOp1); + return GetStructClassHandle(tree->gtOp.gtOp1); case GT_LDOBJ: return tree->gtLdObj.gtClass; @@ -10823,15 +11000,35 @@ CORINFO_CLASS_HANDLE Compiler::GetHfaClassHandle(GenTreePtr tree) case GT_ASG: assert(tree->gtOp.gtOp1->gtOper == GT_LCL_VAR || tree->gtOp.gtOp1->gtOper == GT_LCL_FLD); - return GetHfaClassHandle(tree->gtOp.gtOp1); - + return GetStructClassHandle(tree->gtOp.gtOp1); default: - unreached(); + return NO_CLASS_HANDLE; } } return NO_CLASS_HANDLE; } +#endif // defined(_TARGET_ARM_) || defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + +#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING +bool Compiler::IsRegisterPassable(CORINFO_CLASS_HANDLE hClass) +{ + if (hClass == NO_CLASS_HANDLE) + { + return false; + } + + SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR structDesc; + eeGetSystemVAmd64PassStructInRegisterDescriptor(hClass, &structDesc); + return structDesc.passedInRegisters; +} +bool Compiler::IsRegisterPassable(GenTreePtr tree) +{ + return IsRegisterPassable(GetStructClassHandle(tree)); +} +#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING + +#ifdef _TARGET_ARM_ bool Compiler::IsHfa(CORINFO_CLASS_HANDLE hClass) { return varTypeIsFloating(GetHfaType(hClass)); @@ -10839,12 +11036,12 @@ bool Compiler::IsHfa(CORINFO_CLASS_HANDLE hClass) bool Compiler::IsHfa(GenTreePtr tree) { - return IsHfa(GetHfaClassHandle(tree)); + return IsHfa(GetStructClassHandle(tree)); } var_types Compiler::GetHfaType(GenTreePtr tree) { - return (tree->TypeGet() == TYP_STRUCT) ? GetHfaType(GetHfaClassHandle(tree)) : TYP_UNDEF; + return (tree->TypeGet() == TYP_STRUCT) ? GetHfaType(GetStructClassHandle(tree)) : TYP_UNDEF; } unsigned Compiler::GetHfaSlots(GenTreePtr tree) |