From 7153e441ef21e9bb5e022229c0808b964b9199e0 Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Tue, 22 May 2018 13:50:38 -0700 Subject: Remove JIT LEGACY_BACKEND code (#18064) Remove JIT LEGACY_BACKEND code All code related to the LEGACY_BACKEND JIT is removed. This includes all code related to x87 floating-point code generation. Almost 50,000 lines of code have been removed. Remove legacyjit/legacynonjit directories Remove reg pairs Remove tiny instruction descriptors Remove compCanUseSSE2 (it's always true) Remove unused FEATURE_FP_REGALLOC --- src/jit/CMakeLists.txt | 34 - src/jit/DIRS.proj | 10 +- src/jit/assertionprop.cpp | 3 - src/jit/block.cpp | 20 - src/jit/block.h | 24 - src/jit/codegen.h | 49 +- src/jit/codegenarm.cpp | 4 - src/jit/codegenarm64.cpp | 4 - src/jit/codegenarmarch.cpp | 4 - src/jit/codegenclassic.h | 598 - src/jit/codegencommon.cpp | 1016 +- src/jit/codegeninterface.h | 85 +- src/jit/codegenlegacy.cpp | 22278 ----------------------------- src/jit/codegenlinear.cpp | 36 +- src/jit/codegenlinear.h | 4 - src/jit/codegenxarch.cpp | 4 - src/jit/compiler.cpp | 590 +- src/jit/compiler.h | 464 +- src/jit/compiler.hpp | 193 +- src/jit/compphases.h | 5 - src/jit/crossgen/jit_crossgen.nativeproj | 3 - src/jit/decomposelongs.cpp | 2 - src/jit/ee_il_dll.cpp | 4 +- src/jit/emit.cpp | 78 +- src/jit/emit.h | 221 +- src/jit/emitarm.cpp | 76 +- src/jit/emitarm64.cpp | 2 - src/jit/emitfmtsxarch.h | 49 - src/jit/emitpub.h | 4 - src/jit/emitxarch.cpp | 555 +- src/jit/emitxarch.h | 84 +- src/jit/error.cpp | 4 +- src/jit/error.h | 14 +- src/jit/flowgraph.cpp | 327 +- src/jit/fp.h | 73 - src/jit/gcencode.cpp | 25 +- src/jit/gcinfo.cpp | 31 - src/jit/gentree.cpp | 881 +- src/jit/gentree.h | 274 +- src/jit/gtlist.h | 42 +- src/jit/gtstructs.h | 18 +- src/jit/importer.cpp | 118 +- src/jit/inline.h | 5 - src/jit/instr.cpp | 2135 +-- src/jit/instrsxarch.h | 70 +- src/jit/jit.h | 6 +- src/jit/jit.settings.targets | 43 +- src/jit/jitconfigvalues.h | 8 +- src/jit/jiteh.cpp | 10 - src/jit/jitgcinfo.h | 4 +- src/jit/lclvars.cpp | 188 +- src/jit/legacyjit/.gitmirror | 1 - src/jit/legacyjit/CMakeLists.txt | 65 - src/jit/legacynonjit/.gitmirror | 1 - src/jit/legacynonjit/CMakeLists.txt | 71 - src/jit/legacynonjit/legacynonjit.def | 7 - src/jit/lir.h | 2 - src/jit/liveness.cpp | 747 +- src/jit/lower.cpp | 7 +- src/jit/lowerarm.cpp | 34 - src/jit/lowerarm64.cpp | 34 - src/jit/lowerarmarch.cpp | 4 - src/jit/lowerxarch.cpp | 8 +- src/jit/lsra.cpp | 7 +- src/jit/lsra.h | 4 +- src/jit/lsraarm.cpp | 4 - src/jit/lsraarm64.cpp | 4 - src/jit/lsraarmarch.cpp | 4 - src/jit/lsrabuild.cpp | 10 +- src/jit/lsraxarch.cpp | 9 +- src/jit/morph.cpp | 1068 +- src/jit/optcse.cpp | 11 - src/jit/optimizer.cpp | 84 +- src/jit/rangecheck.cpp | 33 - src/jit/rationalize.cpp | 2 - src/jit/regalloc.cpp | 6653 +-------- src/jit/regalloc.h | 81 - src/jit/register.h | 8 - src/jit/registerfp.cpp | 1515 -- src/jit/registerfp.h | 26 - src/jit/registerxmm.h | 48 - src/jit/regset.cpp | 3364 +---- src/jit/regset.h | 293 +- src/jit/sharedfloat.cpp | 500 - src/jit/simd.cpp | 4 - src/jit/simdcodegenxarch.cpp | 7 +- src/jit/stackfp.cpp | 4506 ------ src/jit/stacklevelsetter.cpp | 4 - src/jit/target.h | 405 +- src/jit/treelifeupdater.cpp | 76 +- src/jit/utils.cpp | 33 +- src/jit/utils.h | 4 - src/jit/valuenum.cpp | 50 - src/jit/varset.h | 6 - 94 files changed, 1313 insertions(+), 49270 deletions(-) delete mode 100644 src/jit/codegenclassic.h delete mode 100644 src/jit/codegenlegacy.cpp delete mode 100644 src/jit/fp.h delete mode 100644 src/jit/legacyjit/.gitmirror delete mode 100644 src/jit/legacyjit/CMakeLists.txt delete mode 100644 src/jit/legacynonjit/.gitmirror delete mode 100644 src/jit/legacynonjit/CMakeLists.txt delete mode 100644 src/jit/legacynonjit/legacynonjit.def delete mode 100644 src/jit/lowerarm.cpp delete mode 100644 src/jit/lowerarm64.cpp delete mode 100644 src/jit/registerfp.cpp delete mode 100644 src/jit/registerfp.h delete mode 100644 src/jit/registerxmm.h delete mode 100644 src/jit/sharedfloat.cpp delete mode 100644 src/jit/stackfp.cpp diff --git a/src/jit/CMakeLists.txt b/src/jit/CMakeLists.txt index d686c9f017..d211b3dc55 100644 --- a/src/jit/CMakeLists.txt +++ b/src/jit/CMakeLists.txt @@ -64,7 +64,6 @@ set( JIT_SOURCES register_arg_convention.cpp regset.cpp scopeinfo.cpp - sharedfloat.cpp sideeffects.cpp sm.cpp smdata.cpp @@ -95,7 +94,6 @@ if (WIN32) block.h blockset.h codegen.h - codegenclassic.h codegeninterface.h codegenlinear.h compiler.h @@ -120,7 +118,6 @@ if (WIN32) emitpub.h emitxarch.h error.h - fp.h gentree.h gtlist.h gtstructs.h @@ -166,8 +163,6 @@ if (WIN32) register.h registerarm.h registerarm64.h - registerfp.h - registerxmm.h reglist.h regpair.h regset.h @@ -197,20 +192,6 @@ if (WIN32) ) endif(WIN32) -# The following defines all the source files used by the "legacy" back-end (#ifdef LEGACY_BACKEND). -# It is always safe to include both legacy and non-legacy files in the build, as everything is properly -# #ifdef'ed, though it makes the build slightly slower to do so. Note there is only a legacy backend for -# x86 and ARM. - -set(JIT_ARM_LEGACY_SOURCES - codegenlegacy.cpp - registerfp.cpp -) -set(JIT_I386_LEGACY_SOURCES - codegenlegacy.cpp - stackfp.cpp -) - # Define all the architecture-specific source files set( JIT_AMD64_SOURCES @@ -227,13 +208,11 @@ set( JIT_AMD64_SOURCES ) set( JIT_ARM_SOURCES - ${JIT_ARM_LEGACY_SOURCES} codegenarmarch.cpp codegenarm.cpp decomposelongs.cpp emitarm.cpp lowerarmarch.cpp - lowerarm.cpp lsraarmarch.cpp lsraarm.cpp targetarm.cpp @@ -241,7 +220,6 @@ set( JIT_ARM_SOURCES ) set( JIT_I386_SOURCES - ${JIT_I386_LEGACY_SOURCES} codegenxarch.cpp decomposelongs.cpp emitxarch.cpp @@ -260,7 +238,6 @@ set( JIT_ARM64_SOURCES codegenarm64.cpp emitarm64.cpp lowerarmarch.cpp - lowerarm64.cpp lsraarmarch.cpp lsraarm64.cpp simd.cpp @@ -378,11 +355,6 @@ endif (FEATURE_MERGE_JIT_AND_ENGINE) add_subdirectory(standalone) -if (CLR_CMAKE_TARGET_ARCH_ARM) - # Build arm32 legacy_backend to run on both x86 host (crossgen build) and arm host (native). - add_subdirectory(legacyjit) -endif (CLR_CMAKE_TARGET_ARCH_ARM) - if (CLR_CMAKE_PLATFORM_ARCH_I386 OR CLR_CMAKE_PLATFORM_ARCH_AMD64) # On x86, build RyuJIT/ARM32 cross-compiling altjit. # On amd64, build RyuJIT/ARM64 cross-compiling altjit. @@ -400,9 +372,3 @@ if ((CLR_CMAKE_PLATFORM_ARCH_I386 OR CLR_CMAKE_PLATFORM_ARCH_AMD64) AND WIN32) # On amd64, build Linux/AMD64 altjit. This enables UNIX_AMD64_ABI. add_subdirectory(linuxnonjit) endif () - -if (CLR_CMAKE_PLATFORM_ARCH_I386 AND WIN32) - # On Windows x86, build altjit generating Windows/ARM32 code using LEGACY_BACKEND. - # (Note: we could also create linuxlegacynonjit for generating Linux/ARM32 code using LEGACY_BACKEND, if needed.) - add_subdirectory(legacynonjit) -endif (CLR_CMAKE_PLATFORM_ARCH_I386 AND WIN32) diff --git a/src/jit/DIRS.proj b/src/jit/DIRS.proj index f49d4d6d26..fdcfe7fc56 100644 --- a/src/jit/DIRS.proj +++ b/src/jit/DIRS.proj @@ -18,21 +18,13 @@ - + - - - - Use cdq if EAX,EDX are available - and we need the result to be in those registers. - cdq is smaller so we use it for SMALL_CODE - */ - - if ((needReg & (RBM_EAX | RBM_EDX)) == (RBM_EAX | RBM_EDX) && - (regSet.rsRegMaskFree() & RBM_EDX)) - { - genCodeForTree(op1, RBM_EAX); - regSet.rsMarkRegUsed(op1); - - /* If we have to spill EDX, might as well use the faster - sar as the spill will increase code size anyway */ - - if (op1->gtRegNum != REG_EAX || !(regSet.rsRegMaskFree() & RBM_EDX)) - { - hiRegMask = regSet.rsRegMaskFree(); - goto USE_SAR_FOR_CAST; - } - - regSet.rsGrabReg(RBM_EDX); - regTracker.rsTrackRegTrash(REG_EDX); - - /* Convert the int in EAX into a long in EDX:EAX */ - - instGen(INS_cdq); - - /* The result is in EDX:EAX */ - - regPair = REG_PAIR_EAXEDX; - } - else -#endif - { - /* use the sar instruction to sign-extend a 32-bit integer */ - - // Does needReg have exactly two bits on and thus - // specifies the exact register pair that we want to use - if (!genMaxOneBit(needReg)) - { - regPair = regSet.rsFindRegPairNo(needReg); - if ((regPair == REG_PAIR_NONE) || (needReg != genRegPairMask(regPair))) - goto ANY_FREE_REG_SIGNED; - loRegMask = genRegMask(genRegPairLo(regPair)); - if ((loRegMask & regSet.rsRegMaskCanGrab()) == 0) - goto ANY_FREE_REG_SIGNED; - hiRegMask = genRegMask(genRegPairHi(regPair)); - } - else - { - ANY_FREE_REG_SIGNED: - loRegMask = needReg; - hiRegMask = RBM_NONE; - } - - genComputeReg(op1, loRegMask, RegSet::ANY_REG, RegSet::KEEP_REG); -#ifdef _TARGET_XARCH_ - USE_SAR_FOR_CAST: -#endif - noway_assert(op1->InReg()); - - regLo = op1->gtRegNum; - loRegMask = genRegMask(regLo); - regSet.rsLockUsedReg(loRegMask); - regHi = regSet.rsPickReg(hiRegMask); - regSet.rsUnlockUsedReg(loRegMask); - - regPair = gen2regs2pair(regLo, regHi); - -#ifdef _TARGET_ARM_ - /* Copy the lo32 bits from regLo to regHi and sign-extend it */ - // Use one instruction instead of two - getEmitter()->emitIns_R_R_I(INS_SHIFT_RIGHT_ARITHM, EA_4BYTE, regHi, regLo, 31); -#else - /* Copy the lo32 bits from regLo to regHi and sign-extend it */ - inst_RV_RV(INS_mov, regHi, regLo, TYP_INT); - inst_RV_SH(INS_SHIFT_RIGHT_ARITHM, EA_4BYTE, regHi, 31); -#endif - - /* The value in the upper register is trashed */ - - regTracker.rsTrackRegTrash(regHi); - } - - /* We can now free up the operand */ - genReleaseReg(op1); - - // conv.ovf.u8 could overflow if the original number was negative - if (tree->gtOverflow() && TYP_ULONG == tree->CastToType()) - { - regNumber hiReg = genRegPairHi(regPair); - instGen_Compare_Reg_To_Zero(EA_4BYTE, hiReg); // set flags - emitJumpKind jmpLTS = genJumpKindForOper(GT_LT, CK_SIGNED); - genJumpToThrowHlpBlk(jmpLTS, SCK_OVERFLOW); - } - } - goto DONE; - - case TYP_FLOAT: - case TYP_DOUBLE: - -#if 0 - /* Load the FP value onto the coprocessor stack */ - - genCodeForTreeFlt(op1); - - /* Allocate a temp for the long value */ - - temp = compiler->tmpGetTemp(TYP_LONG); - - /* Store the FP value into the temp */ - - inst_FS_ST(INS_fistpl, sizeof(int), temp, 0); - genFPstkLevel--; - - /* Pick a register pair for the value */ - - regPair = regSet.rsPickRegPair(needReg); - - /* Figure out which registers the value is in */ - - regLo = genRegPairLo(regPair); - regHi = genRegPairHi(regPair); - - /* The value in the register pair is about to be trashed */ - - regTracker.rsTrackRegTrash(regLo); - regTracker.rsTrackRegTrash(regHi); - - /* Load the converted value into the registers */ - - inst_RV_ST(INS_mov, EA_4BYTE, regLo, temp, 0); - inst_RV_ST(INS_mov, EA_4BYTE, regHi, temp, 4); - - /* We no longer need the temp */ - - compiler->tmpRlsTemp(temp); - goto DONE; -#else - NO_WAY("Cast from TYP_FLOAT or TYP_DOUBLE supposed to be done via a helper call"); - break; -#endif - case TYP_LONG: - case TYP_ULONG: - { - noway_assert(tree->gtOverflow()); // conv.ovf.u8 or conv.ovf.i8 - - genComputeRegPair(op1, REG_PAIR_NONE, RBM_ALLINT & ~needReg, RegSet::FREE_REG); - regPair = op1->gtRegPair; - - // Do we need to set the sign-flag, or can we checked if it is set? - // and not do this "test" if so. - - if (op1->InReg()) - { - regNumber hiReg = genRegPairHi(op1->gtRegPair); - noway_assert(hiReg != REG_STK); - instGen_Compare_Reg_To_Zero(EA_4BYTE, hiReg); // set flags - } - else - { - inst_TT_IV(INS_cmp, op1, 0, sizeof(int)); - } - - emitJumpKind jmpLTS = genJumpKindForOper(GT_LT, CK_SIGNED); - genJumpToThrowHlpBlk(jmpLTS, SCK_OVERFLOW); - } - goto DONE; - - default: -#ifdef DEBUG - compiler->gtDispTree(tree); -#endif - NO_WAY("unexpected cast to long"); - } - break; - - case GT_RETURN: - - /* TODO: - * This code is cloned from the regular processing of GT_RETURN values. We have to remember to - * call genPInvokeMethodEpilog anywhere that we have a GT_RETURN statement. We should really - * generate trees for the PInvoke prolog and epilog so we can remove these special cases. - */ - - // TODO: this should be done AFTER we called exit mon so that - // we are sure that we don't have to keep 'this' alive - - if (compiler->info.compCallUnmanaged && (compiler->compCurBB == compiler->genReturnBB)) - { - /* either it's an "empty" statement or the return statement - of a synchronized method - */ - - genPInvokeMethodEpilog(); - } - -#if CPU_LONG_USES_REGPAIR - /* There must be a long return value */ - - noway_assert(op1); - - /* Evaluate the return value into EDX:EAX */ - - genEvalIntoFreeRegPair(op1, REG_LNGRET, avoidReg); - - noway_assert(op1->InReg()); - noway_assert(op1->gtRegPair == REG_LNGRET); - -#else - NYI("64-bit return"); -#endif - -#ifdef PROFILING_SUPPORTED - // The profiling hook does not trash registers, so it's safe to call after we emit the code for - // the GT_RETURN tree. - - if (compiler->compCurBB == compiler->genReturnBB) - { - genProfilingLeaveCallback(); - } -#endif - return; - - case GT_QMARK: - noway_assert(!"inliner-generated ?: for longs NYI"); - NO_WAY("inliner-generated ?: for longs NYI"); - break; - - case GT_COMMA: - - if (tree->gtFlags & GTF_REVERSE_OPS) - { - // Generate op2 - genCodeForTreeLng(op2, needReg, avoidReg); - genUpdateLife(op2); - - noway_assert(op2->InReg()); - - regSet.rsMarkRegPairUsed(op2); - - // Do side effects of op1 - genEvalSideEffects(op1); - - // Recover op2 if spilled - genRecoverRegPair(op2, REG_PAIR_NONE, RegSet::KEEP_REG); - - genReleaseRegPair(op2); - - genUpdateLife(tree); - - regPair = op2->gtRegPair; - } - else - { - noway_assert((tree->gtFlags & GTF_REVERSE_OPS) == 0); - - /* Generate side effects of the first operand */ - - genEvalSideEffects(op1); - genUpdateLife(op1); - - /* Is the value of the second operand used? */ - - if (tree->gtType == TYP_VOID) - { - /* The right operand produces no result */ - - genEvalSideEffects(op2); - genUpdateLife(tree); - return; - } - - /* Generate the second operand, i.e. the 'real' value */ - - genCodeForTreeLng(op2, needReg, avoidReg); - - /* The result of 'op2' is also the final result */ - - regPair = op2->gtRegPair; - } - - goto DONE; - - case GT_BOX: - { - /* Generate the operand, i.e. the 'real' value */ - - genCodeForTreeLng(op1, needReg, avoidReg); - - /* The result of 'op1' is also the final result */ - - regPair = op1->gtRegPair; - } - - goto DONE; - - case GT_NOP: - if (op1 == NULL) - return; - - genCodeForTreeLng(op1, needReg, avoidReg); - regPair = op1->gtRegPair; - goto DONE; - - default: - break; - } - -#ifdef DEBUG - compiler->gtDispTree(tree); -#endif - noway_assert(!"unexpected 64-bit operator"); - } - - /* See what kind of a special operator we have here */ - - switch (oper) - { - regMaskTP retMask; - case GT_CALL: - retMask = genCodeForCall(tree->AsCall(), true); - if (retMask == RBM_NONE) - regPair = REG_PAIR_NONE; - else - regPair = regSet.rsFindRegPairNo(retMask); - break; - - default: -#ifdef DEBUG - compiler->gtDispTree(tree); -#endif - NO_WAY("unexpected long operator"); - } - -DONE: - - genUpdateLife(tree); - - /* Here we've computed the value of 'tree' into 'regPair' */ - - noway_assert(regPair != DUMMY_INIT(REG_PAIR_CORRUPT)); - - genMarkTreeInRegPair(tree, regPair); -} -#ifdef _PREFAST_ -#pragma warning(pop) -#endif - -/***************************************************************************** - * - * Generate code for a mod of a long by an int. - */ - -regPairNo CodeGen::genCodeForLongModInt(GenTree* tree, regMaskTP needReg) -{ -#ifdef _TARGET_X86_ - - regPairNo regPair; - regMaskTP addrReg; - - genTreeOps oper = tree->OperGet(); - GenTree* op1 = tree->gtOp.gtOp1; - GenTree* op2 = tree->gtOp.gtOp2; - - /* Codegen only for Unsigned MOD */ - noway_assert(oper == GT_UMOD); - - /* op2 must be a long constant in the range 2 to 0x3fffffff */ - - noway_assert((op2->gtOper == GT_CNS_LNG) && (op2->gtLngCon.gtLconVal >= 2) && - (op2->gtLngCon.gtLconVal <= 0x3fffffff)); - int val = (int)op2->gtLngCon.gtLconVal; - - op2->ChangeOperConst(GT_CNS_INT); // it's effectively an integer constant - - op2->gtType = TYP_INT; - op2->gtIntCon.gtIconVal = val; - - /* Which operand are we supposed to compute first? */ - - if (tree->gtFlags & GTF_REVERSE_OPS) - { - /* Compute the second operand into a scratch register, other - than EAX or EDX */ - - needReg = regSet.rsMustExclude(needReg, RBM_PAIR_TMP); - - /* Special case: if op2 is a local var we are done */ - - if (op2->gtOper == GT_LCL_VAR || op2->gtOper == GT_LCL_FLD || op2->gtOper == GT_CLS_VAR) - { - addrReg = genMakeRvalueAddressable(op2, needReg, RegSet::KEEP_REG, false); - } - else - { - genComputeReg(op2, needReg, RegSet::ANY_REG, RegSet::KEEP_REG); - - noway_assert(op2->InReg()); - addrReg = genRegMask(op2->gtRegNum); - } - - /* Compute the first operand into EAX:EDX */ - - genComputeRegPair(op1, REG_PAIR_TMP, RBM_NONE, RegSet::KEEP_REG, true); - noway_assert(op1->InReg()); - noway_assert(op1->gtRegPair == REG_PAIR_TMP); - - /* And recover the second argument while locking the first one */ - - addrReg = genKeepAddressable(op2, addrReg, RBM_PAIR_TMP); - } - else - { - /* Compute the first operand into EAX:EDX */ - - genComputeRegPair(op1, REG_PAIR_EAXEDX, RBM_NONE, RegSet::KEEP_REG, true); - noway_assert(op1->InReg()); - noway_assert(op1->gtRegPair == REG_PAIR_TMP); - - /* Compute the second operand into a scratch register, other - than EAX or EDX */ - - needReg = regSet.rsMustExclude(needReg, RBM_PAIR_TMP); - - /* Special case: if op2 is a local var we are done */ - - if (op2->gtOper == GT_LCL_VAR || op2->gtOper == GT_LCL_FLD || op2->gtOper == GT_CLS_VAR) - { - addrReg = genMakeRvalueAddressable(op2, needReg, RegSet::KEEP_REG, false); - } - else - { - genComputeReg(op2, needReg, RegSet::ANY_REG, RegSet::KEEP_REG); - - noway_assert(op2->InReg()); - addrReg = genRegMask(op2->gtRegNum); - } - - /* Recover the first argument */ - - genRecoverRegPair(op1, REG_PAIR_EAXEDX, RegSet::KEEP_REG); - - /* And recover the second argument while locking the first one */ - - addrReg = genKeepAddressable(op2, addrReg, RBM_PAIR_TMP); - } - - /* At this point, EAX:EDX contains the 64bit dividend and op2->gtRegNum - contains the 32bit divisor. We want to generate the following code: - - ========================== - Unsigned (GT_UMOD) - - cmp edx, op2->gtRegNum - jb lab_no_overflow - - mov temp, eax - mov eax, edx - xor edx, edx - div op2->g2RegNum - mov eax, temp - - lab_no_overflow: - idiv - ========================== - This works because (a * 2^32 + b) % c = ((a % c) * 2^32 + b) % c - */ - - BasicBlock* lab_no_overflow = genCreateTempLabel(); - - // grab a temporary register other than eax, edx, and op2->gtRegNum - - regNumber tempReg = regSet.rsGrabReg(RBM_ALLINT & ~(RBM_PAIR_TMP | genRegMask(op2->gtRegNum))); - - // EAX and tempReg will be trashed by the mov instructions. Doing - // this early won't hurt, and might prevent confusion in genSetRegToIcon. - - regTracker.rsTrackRegTrash(REG_PAIR_TMP_LO); - regTracker.rsTrackRegTrash(tempReg); - - inst_RV_RV(INS_cmp, REG_PAIR_TMP_HI, op2->gtRegNum); - inst_JMP(EJ_jb, lab_no_overflow); - - inst_RV_RV(INS_mov, tempReg, REG_PAIR_TMP_LO, TYP_INT); - inst_RV_RV(INS_mov, REG_PAIR_TMP_LO, REG_PAIR_TMP_HI, TYP_INT); - genSetRegToIcon(REG_PAIR_TMP_HI, 0, TYP_INT); - inst_TT(INS_UNSIGNED_DIVIDE, op2); - inst_RV_RV(INS_mov, REG_PAIR_TMP_LO, tempReg, TYP_INT); - - // Jump point for no overflow divide - - genDefineTempLabel(lab_no_overflow); - - // Issue the divide instruction - - inst_TT(INS_UNSIGNED_DIVIDE, op2); - - /* EAX, EDX, tempReg and op2->gtRegNum are now trashed */ - - regTracker.rsTrackRegTrash(REG_PAIR_TMP_LO); - regTracker.rsTrackRegTrash(REG_PAIR_TMP_HI); - regTracker.rsTrackRegTrash(tempReg); - regTracker.rsTrackRegTrash(op2->gtRegNum); - - if (tree->gtFlags & GTF_MOD_INT_RESULT) - { - /* We don't need to normalize the result, because the caller wants - an int (in edx) */ - - regPair = REG_PAIR_TMP_REVERSE; - } - else - { - /* The result is now in EDX, we now have to normalize it, i.e. we have - to issue: - mov eax, edx; xor edx, edx (for UMOD) - */ - - inst_RV_RV(INS_mov, REG_PAIR_TMP_LO, REG_PAIR_TMP_HI, TYP_INT); - - genSetRegToIcon(REG_PAIR_TMP_HI, 0, TYP_INT); - - regPair = REG_PAIR_TMP; - } - - genReleaseRegPair(op1); - genDoneAddressable(op2, addrReg, RegSet::KEEP_REG); - - return regPair; - -#else // !_TARGET_X86_ - - NYI("codegen for LongModInt"); - - return REG_PAIR_NONE; - -#endif // !_TARGET_X86_ -} - -// Given a tree, return the number of registers that are currently -// used to hold integer enregistered local variables. -// Note that, an enregistered TYP_LONG can take 1 or 2 registers. -unsigned CodeGen::genRegCountForLiveIntEnregVars(GenTree* tree) -{ - unsigned regCount = 0; - - VarSetOps::Iter iter(compiler, compiler->compCurLife); - unsigned varNum = 0; - while (iter.NextElem(&varNum)) - { - unsigned lclNum = compiler->lvaTrackedToVarNum[varNum]; - LclVarDsc* varDsc = &compiler->lvaTable[lclNum]; - - if (varDsc->lvRegister && !varTypeIsFloating(varDsc->TypeGet())) - { - ++regCount; - - if (varTypeIsLong(varDsc->TypeGet())) - { - // For enregistered LONG/ULONG, the lower half should always be in a register. - noway_assert(varDsc->lvRegNum != REG_STK); - - // If the LONG/ULONG is NOT paritally enregistered, then the higher half should be in a register as - // well. - if (varDsc->lvOtherReg != REG_STK) - { - ++regCount; - } - } - } - } - - return regCount; -} - -/*****************************************************************************/ -/*****************************************************************************/ -#if CPU_HAS_FP_SUPPORT -/***************************************************************************** - * - * Generate code for a floating-point operation. - */ - -void CodeGen::genCodeForTreeFlt(GenTree* tree, - regMaskTP needReg, /* = RBM_ALLFLOAT */ - regMaskTP bestReg) /* = RBM_NONE */ -{ - genCodeForTreeFloat(tree, needReg, bestReg); - - if (tree->OperGet() == GT_RETURN) - { - // Make sure to get ALL THE EPILOG CODE - - // TODO: this should be done AFTER we called exit mon so that - // we are sure that we don't have to keep 'this' alive - - if (compiler->info.compCallUnmanaged && (compiler->compCurBB == compiler->genReturnBB)) - { - /* either it's an "empty" statement or the return statement - of a synchronized method - */ - - genPInvokeMethodEpilog(); - } - -#ifdef PROFILING_SUPPORTED - // The profiling hook does not trash registers, so it's safe to call after we emit the code for - // the GT_RETURN tree. - - if (compiler->compCurBB == compiler->genReturnBB) - { - genProfilingLeaveCallback(); - } -#endif - } -} - -/*****************************************************************************/ -#endif // CPU_HAS_FP_SUPPORT - -/***************************************************************************** - * - * Generate a table switch - the switch value (0-based) is in register 'reg'. - */ - -void CodeGen::genTableSwitch(regNumber reg, unsigned jumpCnt, BasicBlock** jumpTab) -{ - unsigned jmpTabBase; - - if (jumpCnt == 1) - { - // In debug code, we don't optimize away the trivial switch statements. So we can get here with a - // BBJ_SWITCH with only a default case. Therefore, don't generate the switch table. - noway_assert(compiler->opts.MinOpts() || compiler->opts.compDbgCode); - inst_JMP(EJ_jmp, jumpTab[0]); - return; - } - - noway_assert(jumpCnt >= 2); - - /* Is the number of cases right for a test and jump switch? */ - - const bool fFirstCaseFollows = (compiler->compCurBB->bbNext == jumpTab[0]); - const bool fDefaultFollows = (compiler->compCurBB->bbNext == jumpTab[jumpCnt - 1]); - const bool fHaveScratchReg = ((regSet.rsRegMaskFree() & genRegMask(reg)) != 0); - - unsigned minSwitchTabJumpCnt = 2; // table is better than just 2 cmp/jcc - - // This means really just a single cmp/jcc (aka a simple if/else) - if (fFirstCaseFollows || fDefaultFollows) - minSwitchTabJumpCnt++; - -#ifdef _TARGET_ARM_ - // On the ARM for small switch tables we will - // generate a sequence of compare and branch instructions - // because the code to load the base of the switch - // table is huge and hideous due to the relocation... :( - // - minSwitchTabJumpCnt++; - if (fHaveScratchReg) - minSwitchTabJumpCnt++; - -#endif // _TARGET_ARM_ - - bool useJumpSequence = jumpCnt < minSwitchTabJumpCnt; - -#if defined(_TARGET_UNIX_) && defined(_TARGET_ARM_) - // Force using an inlined jumping instead switch table generation. - // Switch jump table is generated with incorrect values in CoreRT case, - // so any large switch will crash after loading to PC any such value. - // I think this is due to the fact that we use absolute addressing - // instead of relative. But in CoreRT is used as a rule relative - // addressing when we generate an executable. - // See also https://github.com/dotnet/coreclr/issues/13194 - useJumpSequence = useJumpSequence || compiler->IsTargetAbi(CORINFO_CORERT_ABI); -#endif // defined(_TARGET_UNIX_) && defined(_TARGET_ARM_) - - if (useJumpSequence) - { - /* Does the first case label follow? */ - emitJumpKind jmpEqual = genJumpKindForOper(GT_EQ, CK_SIGNED); - - if (fFirstCaseFollows) - { - /* Check for the default case */ - inst_RV_IV(INS_cmp, reg, jumpCnt - 1, EA_4BYTE); - emitJumpKind jmpGEU = genJumpKindForOper(GT_GE, CK_UNSIGNED); - inst_JMP(jmpGEU, jumpTab[jumpCnt - 1]); - - /* No need to jump to the first case */ - - jumpCnt -= 2; - jumpTab += 1; - - /* Generate a series of "dec reg; jmp label" */ - - // Make sure that we can trash the register so - // that we can generate a series of compares and jumps - // - if ((jumpCnt > 0) && !fHaveScratchReg) - { - regNumber tmpReg = regSet.rsGrabReg(RBM_ALLINT); - inst_RV_RV(INS_mov, tmpReg, reg); - regTracker.rsTrackRegTrash(tmpReg); - reg = tmpReg; - } - - while (jumpCnt > 0) - { - inst_RV_IV(INS_sub, reg, 1, EA_4BYTE, INS_FLAGS_SET); - inst_JMP(jmpEqual, *jumpTab++); - jumpCnt--; - } - } - else - { - /* Check for case0 first */ - instGen_Compare_Reg_To_Zero(EA_4BYTE, reg); // set flags - inst_JMP(jmpEqual, *jumpTab); - - /* No need to jump to the first case or the default */ - - jumpCnt -= 2; - jumpTab += 1; - - /* Generate a series of "dec reg; jmp label" */ - - // Make sure that we can trash the register so - // that we can generate a series of compares and jumps - // - if ((jumpCnt > 0) && !fHaveScratchReg) - { - regNumber tmpReg = regSet.rsGrabReg(RBM_ALLINT); - inst_RV_RV(INS_mov, tmpReg, reg); - regTracker.rsTrackRegTrash(tmpReg); - reg = tmpReg; - } - - while (jumpCnt > 0) - { - inst_RV_IV(INS_sub, reg, 1, EA_4BYTE, INS_FLAGS_SET); - inst_JMP(jmpEqual, *jumpTab++); - jumpCnt--; - } - - if (!fDefaultFollows) - { - inst_JMP(EJ_jmp, *jumpTab); - } - } - - if ((fFirstCaseFollows || fDefaultFollows) && - compiler->fgInDifferentRegions(compiler->compCurBB, compiler->compCurBB->bbNext)) - { - inst_JMP(EJ_jmp, compiler->compCurBB->bbNext); - } - - return; - } - - /* First take care of the default case */ - - inst_RV_IV(INS_cmp, reg, jumpCnt - 1, EA_4BYTE); - emitJumpKind jmpGEU = genJumpKindForOper(GT_GE, CK_UNSIGNED); - inst_JMP(jmpGEU, jumpTab[jumpCnt - 1]); - - /* Generate the jump table contents */ - - jmpTabBase = getEmitter()->emitBBTableDataGenBeg(jumpCnt - 1, false); - -#ifdef DEBUG - if (compiler->opts.dspCode) - printf("\n J_M%03u_DS%02u LABEL DWORD\n", Compiler::s_compMethodsCount, jmpTabBase); -#endif - - for (unsigned index = 0; index < jumpCnt - 1; index++) - { - BasicBlock* target = jumpTab[index]; - - noway_assert(target->bbFlags & BBF_JMP_TARGET); - -#ifdef DEBUG - if (compiler->opts.dspCode) - printf(" DD L_M%03u_BB%02u\n", Compiler::s_compMethodsCount, target->bbNum); -#endif - - getEmitter()->emitDataGenData(index, target); - } - - getEmitter()->emitDataGenEnd(); - -#ifdef _TARGET_ARM_ - // We need to load the address of the table into a register. - // The data section might get placed a long distance away, so we - // can't safely do a PC-relative ADR. :( - // Pick any register except the index register. - // - regNumber regTabBase = regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(reg)); - genMov32RelocatableDataLabel(jmpTabBase, regTabBase); - regTracker.rsTrackRegTrash(regTabBase); - - // LDR PC, [regTableBase + reg * 4] (encoded as LDR PC, [regTableBase, reg, LSL 2] - getEmitter()->emitIns_R_ARX(INS_ldr, EA_PTRSIZE, REG_PC, regTabBase, reg, TARGET_POINTER_SIZE, 0); - -#else // !_TARGET_ARM_ - - getEmitter()->emitIns_IJ(EA_4BYTE_DSP_RELOC, reg, jmpTabBase); - -#endif -} - -/***************************************************************************** - * - * Generate code for a switch statement. - */ - -void CodeGen::genCodeForSwitch(GenTree* tree) -{ - unsigned jumpCnt; - BasicBlock** jumpTab; - - GenTree* oper; - regNumber reg; - - noway_assert(tree->gtOper == GT_SWITCH); - oper = tree->gtOp.gtOp1; - noway_assert(genActualTypeIsIntOrI(oper->gtType)); - - /* Get hold of the jump table */ - - noway_assert(compiler->compCurBB->bbJumpKind == BBJ_SWITCH); - - jumpCnt = compiler->compCurBB->bbJumpSwt->bbsCount; - jumpTab = compiler->compCurBB->bbJumpSwt->bbsDstTab; - - /* Compute the switch value into some register */ - - genCodeForTree(oper, 0); - - /* Get hold of the register the value is in */ - - noway_assert(oper->InReg()); - reg = oper->gtRegNum; - -#if FEATURE_STACK_FP_X87 - if (!compCurFPState.IsEmpty()) - { - return genTableSwitchStackFP(reg, jumpCnt, jumpTab); - } - else -#endif // FEATURE_STACK_FP_X87 - { - return genTableSwitch(reg, jumpCnt, jumpTab); - } -} - -/*****************************************************************************/ -/***************************************************************************** - * Emit a call to a helper function. - */ - -// inline -void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize) -{ - // Can we call the helper function directly - - void *addr = NULL, **pAddr = NULL; - -#if defined(_TARGET_ARM_) && defined(DEBUG) && defined(PROFILING_SUPPORTED) - // Don't ask VM if it hasn't requested ELT hooks - if (!compiler->compProfilerHookNeeded && compiler->opts.compJitELTHookEnabled && - (helper == CORINFO_HELP_PROF_FCN_ENTER || helper == CORINFO_HELP_PROF_FCN_LEAVE || - helper == CORINFO_HELP_PROF_FCN_TAILCALL)) - { - addr = compiler->compProfilerMethHnd; - } - else -#endif - { - addr = compiler->compGetHelperFtn((CorInfoHelpFunc)helper, (void**)&pAddr); - } - -#ifdef _TARGET_ARM_ - if (!addr || !arm_Valid_Imm_For_BL((ssize_t)addr)) - { - // Load the address into a register and call through a register - regNumber indCallReg = - regSet.rsGrabReg(RBM_ALLINT); // Grab an available register to use for the CALL indirection - if (addr) - { - instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, indCallReg, (ssize_t)addr); - } - else - { - getEmitter()->emitIns_R_AI(INS_ldr, EA_PTR_DSP_RELOC, indCallReg, (ssize_t)pAddr); - regTracker.rsTrackRegTrash(indCallReg); - } - - getEmitter()->emitIns_Call(emitter::EC_INDIR_R, compiler->eeFindHelper(helper), - INDEBUG_LDISASM_COMMA(nullptr) NULL, // addr - argSize, retSize, gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, - gcInfo.gcRegByrefSetCur, - BAD_IL_OFFSET, // ilOffset - indCallReg, // ireg - REG_NA, 0, 0, // xreg, xmul, disp - false, // isJump - emitter::emitNoGChelper(helper), - (CorInfoHelpFunc)helper == CORINFO_HELP_PROF_FCN_LEAVE); - } - else - { - getEmitter()->emitIns_Call(emitter::EC_FUNC_TOKEN, compiler->eeFindHelper(helper), - INDEBUG_LDISASM_COMMA(nullptr) addr, argSize, retSize, gcInfo.gcVarPtrSetCur, - gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, BAD_IL_OFFSET, REG_NA, REG_NA, 0, - 0, /* ilOffset, ireg, xreg, xmul, disp */ - false, /* isJump */ - emitter::emitNoGChelper(helper), - (CorInfoHelpFunc)helper == CORINFO_HELP_PROF_FCN_LEAVE); - } -#else - - { - emitter::EmitCallType callType = emitter::EC_FUNC_TOKEN; - - if (!addr) - { - callType = emitter::EC_FUNC_TOKEN_INDIR; - addr = pAddr; - } - - getEmitter()->emitIns_Call(callType, compiler->eeFindHelper(helper), INDEBUG_LDISASM_COMMA(nullptr) addr, - argSize, retSize, gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, - gcInfo.gcRegByrefSetCur, BAD_IL_OFFSET, REG_NA, REG_NA, 0, - 0, /* ilOffset, ireg, xreg, xmul, disp */ - false, /* isJump */ - emitter::emitNoGChelper(helper)); - } -#endif - - regTracker.rsTrashRegSet(RBM_CALLEE_TRASH); - regTracker.rsTrashRegsForGCInterruptability(); -} - -/***************************************************************************** - * - * Push the given argument list, right to left; returns the total amount of - * stuff pushed. - */ - -#if !FEATURE_FIXED_OUT_ARGS -#ifdef _PREFAST_ -#pragma warning(push) -#pragma warning(disable : 21000) // Suppress PREFast warning about overly large function -#endif -size_t CodeGen::genPushArgList(GenTreeCall* call) -{ - GenTreeArgList* regArgs = call->gtCallLateArgs; - size_t size = 0; - regMaskTP addrReg; - - GenTreeArgList* args; - // Create a local, artificial GenTreeArgList that includes the gtCallObjp, if that exists, as first argument, - // so we can iterate over this argument list more uniformly. - // Need to provide a temporary non-null first argument here: if we use this, we'll replace it. - GenTreeArgList firstForObjp(/*temp dummy arg*/ call, call->gtCallArgs); - if (call->gtCallObjp == NULL) - { - args = call->gtCallArgs; - } - else - { - firstForObjp.Current() = call->gtCallObjp; - args = &firstForObjp; - } - - GenTree* curr; - var_types type; - size_t opsz; - - for (; args; args = args->Rest()) - { - addrReg = DUMMY_INIT(RBM_CORRUPT); // to detect uninitialized use - - /* Get hold of the next argument value */ - curr = args->Current(); - - if (curr->IsArgPlaceHolderNode()) - { - assert(curr->gtFlags & GTF_LATE_ARG); - - addrReg = 0; - continue; - } - - // If we have a comma expression, eval the non-last, then deal with the last. - if (!(curr->gtFlags & GTF_LATE_ARG)) - curr = genCodeForCommaTree(curr); - - /* See what type of a value we're passing */ - type = curr->TypeGet(); - - opsz = genTypeSize(genActualType(type)); - - switch (type) - { - case TYP_BOOL: - case TYP_BYTE: - case TYP_SHORT: - case TYP_USHORT: - case TYP_UBYTE: - - /* Don't want to push a small value, make it a full word */ - - genCodeForTree(curr, 0); - - __fallthrough; // now the value should be in a register ... - - case TYP_INT: - case TYP_REF: - case TYP_BYREF: -#if !CPU_HAS_FP_SUPPORT - case TYP_FLOAT: -#endif - - if (curr->gtFlags & GTF_LATE_ARG) - { - assert(curr->gtOper == GT_ASG); - /* one more argument will be passed in a register */ - noway_assert(intRegState.rsCurRegArgNum < MAX_REG_ARG); - - /* arg is passed in the register, nothing on the stack */ - - opsz = 0; - } - - /* Is this value a handle? */ - - if (curr->gtOper == GT_CNS_INT && curr->IsIconHandle()) - { - /* Emit a fixup for the push instruction */ - - inst_IV_handle(INS_push, curr->gtIntCon.gtIconVal); - genSinglePush(); - - addrReg = 0; - break; - } - - /* Is the value a constant? */ - - if (curr->gtOper == GT_CNS_INT) - { - -#if REDUNDANT_LOAD - regNumber reg = regTracker.rsIconIsInReg(curr->gtIntCon.gtIconVal); - - if (reg != REG_NA) - { - inst_RV(INS_push, reg, TYP_INT); - } - else -#endif - { - inst_IV(INS_push, curr->gtIntCon.gtIconVal); - } - - /* If the type is TYP_REF, then this must be a "null". So we can - treat it as a TYP_INT as we don't need to report it as a GC ptr */ - - noway_assert(curr->TypeGet() == TYP_INT || - (varTypeIsGC(curr->TypeGet()) && curr->gtIntCon.gtIconVal == 0)); - - genSinglePush(); - - addrReg = 0; - break; - } - - if (curr->gtFlags & GTF_LATE_ARG) - { - /* This must be a register arg temp assignment */ - - noway_assert(curr->gtOper == GT_ASG); - - /* Evaluate it to the temp */ - - genCodeForTree(curr, 0); - - /* Increment the current argument register counter */ - - intRegState.rsCurRegArgNum++; - - addrReg = 0; - } - else - { - /* This is a 32-bit integer non-register argument */ - - addrReg = genMakeRvalueAddressable(curr, 0, RegSet::KEEP_REG, false); - inst_TT(INS_push, curr); - genSinglePush(); - genDoneAddressable(curr, addrReg, RegSet::KEEP_REG); - } - break; - - case TYP_LONG: -#if !CPU_HAS_FP_SUPPORT - case TYP_DOUBLE: -#endif - - /* Is the value a constant? */ - - if (curr->gtOper == GT_CNS_LNG) - { - inst_IV(INS_push, (int)(curr->gtLngCon.gtLconVal >> 32)); - genSinglePush(); - inst_IV(INS_push, (int)(curr->gtLngCon.gtLconVal)); - genSinglePush(); - - addrReg = 0; - } - else - { - addrReg = genMakeAddressable(curr, 0, RegSet::FREE_REG); - - inst_TT(INS_push, curr, sizeof(int)); - genSinglePush(); - inst_TT(INS_push, curr); - genSinglePush(); - } - break; - -#if CPU_HAS_FP_SUPPORT - case TYP_FLOAT: - case TYP_DOUBLE: -#endif -#if FEATURE_STACK_FP_X87 - addrReg = genPushArgumentStackFP(curr); -#else - NYI("FP codegen"); - addrReg = 0; -#endif - break; - - case TYP_VOID: - - /* Is this a nothing node, deferred register argument? */ - - if (curr->gtFlags & GTF_LATE_ARG) - { - GenTree* arg = curr; - if (arg->gtOper == GT_COMMA) - { - while (arg->gtOper == GT_COMMA) - { - GenTree* op1 = arg->gtOp.gtOp1; - genEvalSideEffects(op1); - genUpdateLife(op1); - arg = arg->gtOp.gtOp2; - } - if (!arg->IsNothingNode()) - { - genEvalSideEffects(arg); - genUpdateLife(arg); - } - } - - /* increment the register count and continue with the next argument */ - - intRegState.rsCurRegArgNum++; - - noway_assert(opsz == 0); - - addrReg = 0; - break; - } - - __fallthrough; - - case TYP_STRUCT: - { - GenTree* arg = curr; - while (arg->gtOper == GT_COMMA) - { - GenTree* op1 = arg->gtOp.gtOp1; - genEvalSideEffects(op1); - genUpdateLife(op1); - arg = arg->gtOp.gtOp2; - } - - noway_assert(arg->gtOper == GT_OBJ || arg->gtOper == GT_MKREFANY || arg->gtOper == GT_IND); - noway_assert((arg->gtFlags & GTF_REVERSE_OPS) == 0); - noway_assert(addrReg == DUMMY_INIT(RBM_CORRUPT)); - - if (arg->gtOper == GT_MKREFANY) - { - GenTree* op1 = arg->gtOp.gtOp1; - GenTree* op2 = arg->gtOp.gtOp2; - - addrReg = genMakeAddressable(op1, RBM_NONE, RegSet::KEEP_REG); - - /* Is this value a handle? */ - if (op2->gtOper == GT_CNS_INT && op2->IsIconHandle()) - { - /* Emit a fixup for the push instruction */ - - inst_IV_handle(INS_push, op2->gtIntCon.gtIconVal); - genSinglePush(); - } - else - { - regMaskTP addrReg2 = genMakeRvalueAddressable(op2, 0, RegSet::KEEP_REG, false); - inst_TT(INS_push, op2); - genSinglePush(); - genDoneAddressable(op2, addrReg2, RegSet::KEEP_REG); - } - addrReg = genKeepAddressable(op1, addrReg); - inst_TT(INS_push, op1); - genSinglePush(); - genDoneAddressable(op1, addrReg, RegSet::KEEP_REG); - - opsz = 2 * TARGET_POINTER_SIZE; - } - else - { - noway_assert(arg->gtOper == GT_OBJ); - - if (arg->gtObj.gtOp1->gtOper == GT_ADDR && arg->gtObj.gtOp1->gtOp.gtOp1->gtOper == GT_LCL_VAR) - { - GenTree* structLocalTree = arg->gtObj.gtOp1->gtOp.gtOp1; - unsigned structLclNum = structLocalTree->gtLclVarCommon.gtLclNum; - LclVarDsc* varDsc = &compiler->lvaTable[structLclNum]; - - // As much as we would like this to be a noway_assert, we can't because - // there are some weird casts out there, and backwards compatiblity - // dictates we do *NOT* start rejecting them now. lvaGetPromotion and - // lvPromoted in general currently do not require the local to be - // TYP_STRUCT, so this assert is really more about how we wish the world - // was then some JIT invariant. - assert((structLocalTree->TypeGet() == TYP_STRUCT) || compiler->compUnsafeCastUsed); - - Compiler::lvaPromotionType promotionType = compiler->lvaGetPromotionType(varDsc); - - if (varDsc->lvPromoted && - promotionType == - Compiler::PROMOTION_TYPE_INDEPENDENT) // Otherwise it is guaranteed to live on stack. - { - assert(!varDsc->lvAddrExposed); // Compiler::PROMOTION_TYPE_INDEPENDENT ==> not exposed. - - addrReg = 0; - - // Get the number of BYTES to copy to the stack - opsz = roundUp(compiler->info.compCompHnd->getClassSize(arg->gtObj.gtClass), - TARGET_POINTER_SIZE); - size_t bytesToBeCopied = opsz; - - // postponedFields is true if we have any postponed fields - // Any field that does not start on a 4-byte boundary is a postponed field - // Such a field is required to be a short or a byte - // - // postponedRegKind records the kind of scratch register we will - // need to process the postponed fields - // RBM_NONE means that we don't need a register - // - // expectedAlignedOffset records the aligned offset that - // has to exist for a push to cover the postponed fields. - // Since all promoted structs have the tightly packed property - // we are guaranteed that we will have such a push - // - bool postponedFields = false; - regMaskTP postponedRegKind = RBM_NONE; - size_t expectedAlignedOffset = UINT_MAX; - - VARSET_TP* deadVarBits = NULL; - compiler->GetPromotedStructDeathVars()->Lookup(structLocalTree, &deadVarBits); - - // Reverse loop, starts pushing from the end of the struct (i.e. the highest field offset) - // - for (int varNum = varDsc->lvFieldLclStart + varDsc->lvFieldCnt - 1; - varNum >= (int)varDsc->lvFieldLclStart; varNum--) - { - LclVarDsc* fieldVarDsc = compiler->lvaTable + varNum; -#ifdef DEBUG - if (fieldVarDsc->lvExactSize == 2 * sizeof(unsigned)) - { - noway_assert(fieldVarDsc->lvFldOffset % (2 * sizeof(unsigned)) == 0); - noway_assert(fieldVarDsc->lvFldOffset + (2 * sizeof(unsigned)) == bytesToBeCopied); - } -#endif - // Whenever we see a stack-aligned fieldVarDsc then we use 4-byte push instruction(s) - // For packed structs we will go back and store the unaligned bytes and shorts - // in the next loop - // - if (fieldVarDsc->lvStackAligned()) - { - if (fieldVarDsc->lvExactSize != 2 * sizeof(unsigned) && - fieldVarDsc->lvFldOffset + (unsigned)TARGET_POINTER_SIZE != bytesToBeCopied) - { - // Might need 4-bytes paddings for fields other than LONG and DOUBLE. - // Just push some junk (i.e EAX) on the stack. - inst_RV(INS_push, REG_EAX, TYP_INT); - genSinglePush(); - - bytesToBeCopied -= TARGET_POINTER_SIZE; - } - - // If we have an expectedAlignedOffset make sure that this push instruction - // is what we expect to cover the postponedFields - // - if (expectedAlignedOffset != UINT_MAX) - { - // This push must be for a small field - noway_assert(fieldVarDsc->lvExactSize < 4); - // The fldOffset for this push should be equal to the expectedAlignedOffset - noway_assert(fieldVarDsc->lvFldOffset == expectedAlignedOffset); - expectedAlignedOffset = UINT_MAX; - } - - // Push the "upper half" of LONG var first - - if (isRegPairType(fieldVarDsc->lvType)) - { - if (fieldVarDsc->lvOtherReg != REG_STK) - { - inst_RV(INS_push, fieldVarDsc->lvOtherReg, TYP_INT); - genSinglePush(); - - // Prepare the set of vars to be cleared from gcref/gcbyref set - // in case they become dead after genUpdateLife. - // genDoneAddressable() will remove dead gc vars by calling - // gcInfo.gcMarkRegSetNpt. - // Although it is not addrReg, we just borrow the name here. - addrReg |= genRegMask(fieldVarDsc->lvOtherReg); - } - else - { - getEmitter()->emitIns_S(INS_push, EA_4BYTE, varNum, TARGET_POINTER_SIZE); - genSinglePush(); - } - - bytesToBeCopied -= TARGET_POINTER_SIZE; - } - - // Push the "upper half" of DOUBLE var if it is not enregistered. - - if (fieldVarDsc->lvType == TYP_DOUBLE) - { - if (!fieldVarDsc->lvRegister) - { - getEmitter()->emitIns_S(INS_push, EA_4BYTE, varNum, TARGET_POINTER_SIZE); - genSinglePush(); - } - - bytesToBeCopied -= TARGET_POINTER_SIZE; - } - - // - // Push the field local. - // - - if (fieldVarDsc->lvRegister) - { - if (!varTypeIsFloating(genActualType(fieldVarDsc->TypeGet()))) - { - inst_RV(INS_push, fieldVarDsc->lvRegNum, - genActualType(fieldVarDsc->TypeGet())); - genSinglePush(); - - // Prepare the set of vars to be cleared from gcref/gcbyref set - // in case they become dead after genUpdateLife. - // genDoneAddressable() will remove dead gc vars by calling - // gcInfo.gcMarkRegSetNpt. - // Although it is not addrReg, we just borrow the name here. - addrReg |= genRegMask(fieldVarDsc->lvRegNum); - } - else - { - // Must be TYP_FLOAT or TYP_DOUBLE - noway_assert(fieldVarDsc->lvRegNum != REG_FPNONE); - - noway_assert(fieldVarDsc->lvExactSize == sizeof(unsigned) || - fieldVarDsc->lvExactSize == 2 * sizeof(unsigned)); - - inst_RV_IV(INS_sub, REG_SPBASE, fieldVarDsc->lvExactSize, EA_PTRSIZE); - - genSinglePush(); - if (fieldVarDsc->lvExactSize == 2 * sizeof(unsigned)) - { - genSinglePush(); - } - -#if FEATURE_STACK_FP_X87 - GenTree* fieldTree = new (compiler, GT_REG_VAR) - GenTreeLclVar(fieldVarDsc->lvType, varNum, BAD_IL_OFFSET); - fieldTree->gtOper = GT_REG_VAR; - fieldTree->gtRegNum = fieldVarDsc->lvRegNum; - fieldTree->gtRegVar.gtRegNum = fieldVarDsc->lvRegNum; - if ((arg->gtFlags & GTF_VAR_DEATH) != 0) - { - if (fieldVarDsc->lvTracked && - (deadVarBits == NULL || - VarSetOps::IsMember(compiler, *deadVarBits, - fieldVarDsc->lvVarIndex))) - { - fieldTree->gtFlags |= GTF_VAR_DEATH; - } - } - genCodeForTreeStackFP_Leaf(fieldTree); - - // Take reg to top of stack - - FlatFPX87_MoveToTOS(&compCurFPState, fieldTree->gtRegNum); - - // Pop it off to stack - compCurFPState.Pop(); - - getEmitter()->emitIns_AR_R(INS_fstp, EA_ATTR(fieldVarDsc->lvExactSize), - REG_NA, REG_SPBASE, 0); -#else - NYI_FLAT_FP_X87("FP codegen"); -#endif - } - } - else - { - getEmitter()->emitIns_S(INS_push, - (fieldVarDsc->TypeGet() == TYP_REF) ? EA_GCREF - : EA_4BYTE, - varNum, 0); - genSinglePush(); - } - - bytesToBeCopied -= TARGET_POINTER_SIZE; - } - else // not stack aligned - { - noway_assert(fieldVarDsc->lvExactSize < 4); - - // We will need to use a store byte or store word - // to set this unaligned location - postponedFields = true; - - if (expectedAlignedOffset != UINT_MAX) - { - // This should never change until it is set back to UINT_MAX by an aligned - // offset - noway_assert(expectedAlignedOffset == - roundUp(fieldVarDsc->lvFldOffset, TARGET_POINTER_SIZE) - - TARGET_POINTER_SIZE); - } - - expectedAlignedOffset = - roundUp(fieldVarDsc->lvFldOffset, TARGET_POINTER_SIZE) - TARGET_POINTER_SIZE; - - noway_assert(expectedAlignedOffset < bytesToBeCopied); - - if (fieldVarDsc->lvRegister) - { - // Do we need to use a byte-able register? - if (fieldVarDsc->lvExactSize == 1) - { - // Did we enregister fieldVarDsc2 in a non byte-able register? - if ((genRegMask(fieldVarDsc->lvRegNum) & RBM_BYTE_REGS) == 0) - { - // then we will need to grab a byte-able register - postponedRegKind = RBM_BYTE_REGS; - } - } - } - else // not enregistered - { - if (fieldVarDsc->lvExactSize == 1) - { - // We will need to grab a byte-able register - postponedRegKind = RBM_BYTE_REGS; - } - else - { - // We will need to grab any scratch register - if (postponedRegKind != RBM_BYTE_REGS) - postponedRegKind = RBM_ALLINT; - } - } - } - } - - // Now we've pushed all of the aligned fields. - // - // We should have pushed bytes equal to the entire struct - noway_assert(bytesToBeCopied == 0); - - // We should have seen a push that covers every postponed field - noway_assert(expectedAlignedOffset == UINT_MAX); - - // Did we have any postponed fields? - if (postponedFields) - { - regNumber regNum = REG_STK; // means no register - - // If we needed a scratch register then grab it here - - if (postponedRegKind != RBM_NONE) - regNum = regSet.rsGrabReg(postponedRegKind); - - // Forward loop, starts from the lowest field offset - // - for (unsigned varNum = varDsc->lvFieldLclStart; - varNum < varDsc->lvFieldLclStart + varDsc->lvFieldCnt; varNum++) - { - LclVarDsc* fieldVarDsc = compiler->lvaTable + varNum; - - // All stack aligned fields have already been pushed - if (fieldVarDsc->lvStackAligned()) - continue; - - // We have a postponed field - - // It must be a byte or a short - noway_assert(fieldVarDsc->lvExactSize < 4); - - // Is the field enregistered? - if (fieldVarDsc->lvRegister) - { - // Frequently we can just use that register - regNumber tmpRegNum = fieldVarDsc->lvRegNum; - - // Do we need to use a byte-able register? - if (fieldVarDsc->lvExactSize == 1) - { - // Did we enregister the field in a non byte-able register? - if ((genRegMask(tmpRegNum) & RBM_BYTE_REGS) == 0) - { - // then we will need to use the byte-able register 'regNum' - noway_assert((genRegMask(regNum) & RBM_BYTE_REGS) != 0); - - // Copy the register that contains fieldVarDsc into 'regNum' - getEmitter()->emitIns_R_R(INS_mov, EA_4BYTE, regNum, - fieldVarDsc->lvRegNum); - regTracker.rsTrackRegLclVar(regNum, varNum); - - // tmpRegNum is the register that we will extract the byte value from - tmpRegNum = regNum; - } - noway_assert((genRegMask(tmpRegNum) & RBM_BYTE_REGS) != 0); - } - - getEmitter()->emitIns_AR_R(ins_Store(fieldVarDsc->TypeGet()), - (emitAttr)fieldVarDsc->lvExactSize, tmpRegNum, - REG_SPBASE, fieldVarDsc->lvFldOffset); - } - else // not enregistered - { - // We will copy the non-enregister fieldVar into our scratch register 'regNum' - - noway_assert(regNum != REG_STK); - getEmitter()->emitIns_R_S(ins_Load(fieldVarDsc->TypeGet()), - (emitAttr)fieldVarDsc->lvExactSize, regNum, varNum, - 0); - - regTracker.rsTrackRegLclVar(regNum, varNum); - - // Store the value (byte or short) into the stack - - getEmitter()->emitIns_AR_R(ins_Store(fieldVarDsc->TypeGet()), - (emitAttr)fieldVarDsc->lvExactSize, regNum, - REG_SPBASE, fieldVarDsc->lvFldOffset); - } - } - } - genUpdateLife(structLocalTree); - - break; - } - } - - genCodeForTree(arg->gtObj.gtOp1, 0); - noway_assert(arg->gtObj.gtOp1->InReg()); - regNumber reg = arg->gtObj.gtOp1->gtRegNum; - // Get the number of DWORDS to copy to the stack - opsz = roundUp(compiler->info.compCompHnd->getClassSize(arg->gtObj.gtClass), sizeof(DWORD)); - unsigned slots = (unsigned)(opsz / sizeof(DWORD)); - - BYTE* gcLayout = new (compiler, CMK_Codegen) BYTE[slots]; - - compiler->info.compCompHnd->getClassGClayout(arg->gtObj.gtClass, gcLayout); - - BOOL bNoneGC = TRUE; - for (int i = slots - 1; i >= 0; --i) - { - if (gcLayout[i] != TYPE_GC_NONE) - { - bNoneGC = FALSE; - break; - } - } - - /* passing large structures using movq instead of pushes does not increase codesize very much */ - unsigned movqLenMin = 8; - unsigned movqLenMax = 64; - unsigned curBBweight = compiler->compCurBB->getBBWeight(compiler); - - if ((compiler->compCodeOpt() == Compiler::SMALL_CODE) || (curBBweight == BB_ZERO_WEIGHT)) - { - // Don't bother with this optimization in - // rarely run blocks or when optimizing for size - movqLenMax = movqLenMin = 0; - } - else if (compiler->compCodeOpt() == Compiler::FAST_CODE) - { - // Be more aggressive when optimizing for speed - movqLenMax *= 2; - } - - /* Adjust for BB weight */ - if (curBBweight >= (BB_LOOP_WEIGHT * BB_UNITY_WEIGHT) / 2) - { - // Be more aggressive when we are inside a loop - movqLenMax *= 2; - } - - if (compiler->opts.compCanUseSSE2 && bNoneGC && (opsz >= movqLenMin) && (opsz <= movqLenMax)) - { - JITLOG_THIS(compiler, (LL_INFO10000, - "Using XMM instructions to pass %3d byte valuetype while compiling %s\n", - opsz, compiler->info.compFullName)); - - int stkDisp = (int)(unsigned)opsz; - int curDisp = 0; - regNumber xmmReg = REG_XMM0; - - if (opsz & 0x4) - { - stkDisp -= TARGET_POINTER_SIZE; - getEmitter()->emitIns_AR_R(INS_push, EA_4BYTE, REG_NA, reg, stkDisp); - genSinglePush(); - } - - inst_RV_IV(INS_sub, REG_SPBASE, stkDisp, EA_PTRSIZE); - AddStackLevel(stkDisp); - - while (curDisp < stkDisp) - { - getEmitter()->emitIns_R_AR(INS_movq, EA_8BYTE, xmmReg, reg, curDisp); - getEmitter()->emitIns_AR_R(INS_movq, EA_8BYTE, xmmReg, REG_SPBASE, curDisp); - curDisp += 2 * TARGET_POINTER_SIZE; - } - noway_assert(curDisp == stkDisp); - } - else - { - for (int i = slots - 1; i >= 0; --i) - { - emitAttr fieldSize; - if (gcLayout[i] == TYPE_GC_NONE) - fieldSize = EA_4BYTE; - else if (gcLayout[i] == TYPE_GC_REF) - fieldSize = EA_GCREF; - else - { - noway_assert(gcLayout[i] == TYPE_GC_BYREF); - fieldSize = EA_BYREF; - } - getEmitter()->emitIns_AR_R(INS_push, fieldSize, REG_NA, reg, i * TARGET_POINTER_SIZE); - genSinglePush(); - } - } - gcInfo.gcMarkRegSetNpt(genRegMask(reg)); // Kill the pointer in op1 - } - - addrReg = 0; - break; - } - - default: - noway_assert(!"unhandled/unexpected arg type"); - NO_WAY("unhandled/unexpected arg type"); - } - - /* Update the current set of live variables */ - - genUpdateLife(curr); - - /* Update the current set of register pointers */ - - noway_assert(addrReg != DUMMY_INIT(RBM_CORRUPT)); - genDoneAddressable(curr, addrReg, RegSet::FREE_REG); - - /* Remember how much stuff we've pushed on the stack */ - - size += opsz; - - /* Update the current argument stack offset */ - - /* Continue with the next argument, if any more are present */ - - } // while args - - /* Move the deferred arguments to registers */ - - for (args = regArgs; args; args = args->Rest()) - { - curr = args->Current(); - - assert(!curr->IsArgPlaceHolderNode()); // No place holders nodes are in the late args - - fgArgTabEntry* curArgTabEntry = compiler->gtArgEntryByNode(call, curr); - assert(curArgTabEntry); - regNumber regNum = curArgTabEntry->regNum; - - noway_assert(isRegParamType(curr->TypeGet())); - noway_assert(curr->gtType != TYP_VOID); - - /* Evaluate the argument to a register [pair] */ - - if (genTypeSize(genActualType(curr->TypeGet())) == sizeof(int)) - { - /* Check if this is the guess area for the resolve interface call - * Pass a size of EA_OFFSET*/ - if (curr->gtOper == GT_CLS_VAR && compiler->eeGetJitDataOffs(curr->gtClsVar.gtClsVarHnd) >= 0) - { - getEmitter()->emitIns_R_C(ins_Load(TYP_INT), EA_OFFSET, regNum, curr->gtClsVar.gtClsVarHnd, 0); - regTracker.rsTrackRegTrash(regNum); - - /* The value is now in the appropriate register */ - - genMarkTreeInReg(curr, regNum); - } - else - { - genComputeReg(curr, genRegMask(regNum), RegSet::EXACT_REG, RegSet::FREE_REG, false); - } - - noway_assert(curr->gtRegNum == regNum); - - /* If the register is already marked as used, it will become - multi-used. However, since it is a callee-trashed register, - we will have to spill it before the call anyway. So do it now */ - - if (regSet.rsMaskUsed & genRegMask(regNum)) - { - noway_assert(genRegMask(regNum) & RBM_CALLEE_TRASH); - regSet.rsSpillReg(regNum); - } - - /* Mark the register as 'used' */ - - regSet.rsMarkRegUsed(curr); - } - else - { - noway_assert(!"UNDONE: Passing a TYP_STRUCT in register arguments"); - } - } - - /* If any of the previously loaded arguments were spilled - reload them */ - - for (args = regArgs; args; args = args->Rest()) - { - curr = args->Current(); - assert(curr); - - if (curr->gtFlags & GTF_SPILLED) - { - if (isRegPairType(curr->gtType)) - { - regSet.rsUnspillRegPair(curr, genRegPairMask(curr->gtRegPair), RegSet::KEEP_REG); - } - else - { - regSet.rsUnspillReg(curr, genRegMask(curr->gtRegNum), RegSet::KEEP_REG); - } - } - } - - /* Return the total size pushed */ - - return size; -} -#ifdef _PREFAST_ -#pragma warning(pop) -#endif - -#else // FEATURE_FIXED_OUT_ARGS - -// -// ARM and AMD64 uses this method to pass the stack based args -// -// returns size pushed (always zero) -size_t CodeGen::genPushArgList(GenTreeCall* call) -{ - GenTreeArgList* lateArgs = call->gtCallLateArgs; - GenTree* curr; - var_types type; - int argSize; - - GenTreeArgList* args; - // Create a local, artificial GenTreeArgList that includes the gtCallObjp, if that exists, as first argument, - // so we can iterate over this argument list more uniformly. - // Need to provide a temporary non-null first argument here: if we use this, we'll replace it. - GenTreeArgList objpArgList(/*temp dummy arg*/ call, call->gtCallArgs); - if (call->gtCallObjp == NULL) - { - args = call->gtCallArgs; - } - else - { - objpArgList.Current() = call->gtCallObjp; - args = &objpArgList; - } - - for (; args; args = args->Rest()) - { - /* Get hold of the next argument value */ - curr = args->Current(); - - fgArgTabEntry* curArgTabEntry = compiler->gtArgEntryByNode(call, curr); - assert(curArgTabEntry); - regNumber regNum = curArgTabEntry->regNum; - int argOffset = curArgTabEntry->slotNum * TARGET_POINTER_SIZE; - - /* See what type of a value we're passing */ - type = curr->TypeGet(); - - if ((type == TYP_STRUCT) && (curr->gtOper == GT_ASG)) - { - type = TYP_VOID; - } - - // This holds the set of registers corresponding to enregistered promoted struct field variables - // that go dead after this use of the variable in the argument list. - regMaskTP deadFieldVarRegs = RBM_NONE; - - argSize = TARGET_POINTER_SIZE; // The default size for an arg is one pointer-sized item - - if (curr->IsArgPlaceHolderNode()) - { - assert(curr->gtFlags & GTF_LATE_ARG); - goto DEFERRED; - } - - if (varTypeIsSmall(type)) - { - // Normalize 'type', it represents the item that we will be storing in the Outgoing Args - type = TYP_I_IMPL; - } - - switch (type) - { - - case TYP_DOUBLE: - case TYP_LONG: - -#if defined(_TARGET_ARM_) - - argSize = (TARGET_POINTER_SIZE * 2); - - /* Is the value a constant? */ - - if (curr->gtOper == GT_CNS_LNG) - { - assert((curr->gtFlags & GTF_LATE_ARG) == 0); - - int hiVal = (int)(curr->gtLngCon.gtLconVal >> 32); - int loVal = (int)(curr->gtLngCon.gtLconVal & 0xffffffff); - - instGen_Store_Imm_Into_Lcl(TYP_INT, EA_4BYTE, loVal, compiler->lvaOutgoingArgSpaceVar, argOffset); - - instGen_Store_Imm_Into_Lcl(TYP_INT, EA_4BYTE, hiVal, compiler->lvaOutgoingArgSpaceVar, - argOffset + 4); - - break; - } - else - { - genCodeForTree(curr, 0); - - if (curr->gtFlags & GTF_LATE_ARG) - { - // The arg was assigned into a temp and - // will be moved to the correct register or slot later - - argSize = 0; // nothing is passed on the stack - } - else - { - // The arg is passed in the outgoing argument area of the stack frame - // - assert(curr->gtOper != GT_ASG); // GTF_LATE_ARG should be set if this is the case - assert(curr->InReg()); // should be enregistered after genCodeForTree(curr, 0) - - if (type == TYP_LONG) - { - regNumber regLo = genRegPairLo(curr->gtRegPair); - regNumber regHi = genRegPairHi(curr->gtRegPair); - - assert(regLo != REG_STK); - inst_SA_RV(ins_Store(TYP_INT), argOffset, regLo, TYP_INT); - if (regHi == REG_STK) - { - regHi = regSet.rsPickFreeReg(); - inst_RV_TT(ins_Load(TYP_INT), regHi, curr, 4); - regTracker.rsTrackRegTrash(regHi); - } - inst_SA_RV(ins_Store(TYP_INT), argOffset + 4, regHi, TYP_INT); - } - else // (type == TYP_DOUBLE) - { - inst_SA_RV(ins_Store(type), argOffset, curr->gtRegNum, type); - } - } - } - break; - -#elif defined(_TARGET_64BIT_) - __fallthrough; -#else -#error "Unknown target for passing TYP_LONG argument using FIXED_ARGS" -#endif - - case TYP_REF: - case TYP_BYREF: - - case TYP_FLOAT: - case TYP_INT: - /* Is the value a constant? */ - - if (curr->gtOper == GT_CNS_INT) - { - assert(!(curr->gtFlags & GTF_LATE_ARG)); - -#if REDUNDANT_LOAD - regNumber reg = regTracker.rsIconIsInReg(curr->gtIntCon.gtIconVal); - - if (reg != REG_NA) - { - inst_SA_RV(ins_Store(type), argOffset, reg, type); - } - else -#endif - { - GenTreeIntConCommon* con = curr->AsIntConCommon(); - bool needReloc = con->ImmedValNeedsReloc(compiler); - emitAttr attr = needReloc ? EA_HANDLE_CNS_RELOC : emitTypeSize(type); - - instGen_Store_Imm_Into_Lcl(type, attr, curr->gtIntCon.gtIconVal, - compiler->lvaOutgoingArgSpaceVar, argOffset); - } - break; - } - - /* This is passed as a pointer-sized integer argument */ - - genCodeForTree(curr, 0); - - // The arg has been evaluated now, but will be put in a register or pushed on the stack later. - if (curr->gtFlags & GTF_LATE_ARG) - { -#ifdef _TARGET_ARM_ - argSize = 0; // nothing is passed on the stack -#endif - } - else - { - // The arg is passed in the outgoing argument area of the stack frame - - assert(curr->gtOper != GT_ASG); // GTF_LATE_ARG should be set if this is the case - assert(curr->InReg()); // should be enregistered after genCodeForTree(curr, 0) - inst_SA_RV(ins_Store(type), argOffset, curr->gtRegNum, type); - - if ((genRegMask(curr->gtRegNum) & regSet.rsMaskUsed) == 0) - gcInfo.gcMarkRegSetNpt(genRegMask(curr->gtRegNum)); - } - break; - - case TYP_VOID: - /* Is this a nothing node, deferred register argument? */ - - if (curr->gtFlags & GTF_LATE_ARG) - { - /* Handle side-effects */ - DEFERRED: - if (curr->OperIsCopyBlkOp() || curr->OperGet() == GT_COMMA) - { -#ifdef _TARGET_ARM_ - { - GenTree* curArgNode = curArgTabEntry->node; - var_types curRegArgType = curArgNode->gtType; - assert(curRegArgType != TYP_UNDEF); - - if (curRegArgType == TYP_STRUCT) - { - // If the RHS of the COPYBLK is a promoted struct local, then the use of that - // is an implicit use of all its field vars. If these are last uses, remember that, - // so we can later update the GC compiler->info. - if (curr->OperIsCopyBlkOp()) - deadFieldVarRegs |= genFindDeadFieldRegs(curr); - } - } -#endif // _TARGET_ARM_ - - genCodeForTree(curr, 0); - } - else - { - assert(curr->IsArgPlaceHolderNode() || curr->IsNothingNode()); - } - -#if defined(_TARGET_ARM_) - argSize = curArgTabEntry->numSlots * TARGET_POINTER_SIZE; -#endif - } - else - { - for (GenTree* arg = curr; arg->gtOper == GT_COMMA; arg = arg->gtOp.gtOp2) - { - GenTree* op1 = arg->gtOp.gtOp1; - - genEvalSideEffects(op1); - genUpdateLife(op1); - } - } - break; - -#ifdef _TARGET_ARM_ - - case TYP_STRUCT: - { - GenTree* arg = curr; - while (arg->gtOper == GT_COMMA) - { - GenTree* op1 = arg->gtOp.gtOp1; - genEvalSideEffects(op1); - genUpdateLife(op1); - arg = arg->gtOp.gtOp2; - } - noway_assert((arg->OperGet() == GT_OBJ) || (arg->OperGet() == GT_MKREFANY)); - - CORINFO_CLASS_HANDLE clsHnd; - unsigned argAlign; - unsigned slots; - BYTE* gcLayout = NULL; - - // If the struct being passed is a OBJ of a local struct variable that is promoted (in the - // INDEPENDENT fashion, which doesn't require writes to be written through to the variable's - // home stack loc) "promotedStructLocalVarDesc" will be set to point to the local variable - // table entry for the promoted struct local. As we fill slots with the contents of a - // promoted struct, "bytesOfNextSlotOfCurPromotedStruct" will be the number of filled bytes - // that indicate another filled slot, and "nextPromotedStructFieldVar" will be the local - // variable number of the next field variable to be copied. - LclVarDsc* promotedStructLocalVarDesc = NULL; - GenTree* structLocalTree = NULL; - unsigned bytesOfNextSlotOfCurPromotedStruct = TARGET_POINTER_SIZE; // Size of slot. - unsigned nextPromotedStructFieldVar = BAD_VAR_NUM; - unsigned promotedStructOffsetOfFirstStackSlot = 0; - unsigned argOffsetOfFirstStackSlot = UINT32_MAX; // Indicates uninitialized. - - if (arg->OperGet() == GT_OBJ) - { - clsHnd = arg->gtObj.gtClass; - unsigned originalSize = compiler->info.compCompHnd->getClassSize(clsHnd); - argAlign = - roundUp(compiler->info.compCompHnd->getClassAlignmentRequirement(clsHnd), TARGET_POINTER_SIZE); - argSize = (unsigned)(roundUp(originalSize, TARGET_POINTER_SIZE)); - - slots = (unsigned)(argSize / TARGET_POINTER_SIZE); - - gcLayout = new (compiler, CMK_Codegen) BYTE[slots]; - - compiler->info.compCompHnd->getClassGClayout(clsHnd, gcLayout); - - // Are we loading a promoted struct local var? - if (arg->gtObj.gtOp1->gtOper == GT_ADDR && arg->gtObj.gtOp1->gtOp.gtOp1->gtOper == GT_LCL_VAR) - { - structLocalTree = arg->gtObj.gtOp1->gtOp.gtOp1; - unsigned structLclNum = structLocalTree->gtLclVarCommon.gtLclNum; - LclVarDsc* varDsc = &compiler->lvaTable[structLclNum]; - - // As much as we would like this to be a noway_assert, we can't because - // there are some weird casts out there, and backwards compatiblity - // dictates we do *NOT* start rejecting them now. lvaGetPromotion and - // lvPromoted in general currently do not require the local to be - // TYP_STRUCT, so this assert is really more about how we wish the world - // was then some JIT invariant. - assert((structLocalTree->TypeGet() == TYP_STRUCT) || compiler->compUnsafeCastUsed); - - Compiler::lvaPromotionType promotionType = compiler->lvaGetPromotionType(varDsc); - - if (varDsc->lvPromoted && - promotionType == Compiler::PROMOTION_TYPE_INDEPENDENT) // Otherwise it is guaranteed to live - // on stack. - { - assert(!varDsc->lvAddrExposed); // Compiler::PROMOTION_TYPE_INDEPENDENT ==> not exposed. - promotedStructLocalVarDesc = varDsc; - nextPromotedStructFieldVar = promotedStructLocalVarDesc->lvFieldLclStart; - } - } - } - else - { - noway_assert(arg->OperGet() == GT_MKREFANY); - - clsHnd = NULL; - argAlign = TARGET_POINTER_SIZE; - argSize = 2 * TARGET_POINTER_SIZE; - slots = 2; - } - - // Any TYP_STRUCT argument that is passed in registers must be moved over to the LateArg list - noway_assert(regNum == REG_STK); - - // This code passes a TYP_STRUCT by value using the outgoing arg space var - // - if (arg->OperGet() == GT_OBJ) - { - regNumber regSrc = REG_STK; - regNumber regTmp = REG_STK; // This will get set below if the obj is not of a promoted struct local. - int cStackSlots = 0; - - if (promotedStructLocalVarDesc == NULL) - { - genComputeReg(arg->gtObj.gtOp1, 0, RegSet::ANY_REG, RegSet::KEEP_REG); - noway_assert(arg->gtObj.gtOp1->InReg()); - regSrc = arg->gtObj.gtOp1->gtRegNum; - } - - // The number of bytes to add "argOffset" to get the arg offset of the current slot. - int extraArgOffset = 0; - - for (unsigned i = 0; i < slots; i++) - { - emitAttr fieldSize; - if (gcLayout[i] == TYPE_GC_NONE) - fieldSize = EA_PTRSIZE; - else if (gcLayout[i] == TYPE_GC_REF) - fieldSize = EA_GCREF; - else - { - noway_assert(gcLayout[i] == TYPE_GC_BYREF); - fieldSize = EA_BYREF; - } - - // Pass the argument using the lvaOutgoingArgSpaceVar - - if (promotedStructLocalVarDesc != NULL) - { - if (argOffsetOfFirstStackSlot == UINT32_MAX) - argOffsetOfFirstStackSlot = argOffset; - - regNumber maxRegArg = regNumber(MAX_REG_ARG); - bool filledExtraSlot = genFillSlotFromPromotedStruct( - arg, curArgTabEntry, promotedStructLocalVarDesc, fieldSize, &nextPromotedStructFieldVar, - &bytesOfNextSlotOfCurPromotedStruct, - /*pCurRegNum*/ &maxRegArg, - /*argOffset*/ argOffset + extraArgOffset, - /*fieldOffsetOfFirstStackSlot*/ promotedStructOffsetOfFirstStackSlot, - argOffsetOfFirstStackSlot, &deadFieldVarRegs, ®Tmp); - extraArgOffset += TARGET_POINTER_SIZE; - // If we filled an extra slot with an 8-byte value, skip a slot. - if (filledExtraSlot) - { - i++; - cStackSlots++; - extraArgOffset += TARGET_POINTER_SIZE; - } - } - else - { - if (regTmp == REG_STK) - { - regTmp = regSet.rsPickFreeReg(); - } - - getEmitter()->emitIns_R_AR(ins_Load(TYP_I_IMPL), fieldSize, regTmp, regSrc, - i * TARGET_POINTER_SIZE); - - getEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), fieldSize, regTmp, - compiler->lvaOutgoingArgSpaceVar, - argOffset + cStackSlots * TARGET_POINTER_SIZE); - regTracker.rsTrackRegTrash(regTmp); - } - cStackSlots++; - } - - if (promotedStructLocalVarDesc == NULL) - { - regSet.rsMarkRegFree(genRegMask(regSrc)); - } - if (structLocalTree != NULL) - genUpdateLife(structLocalTree); - } - else - { - assert(arg->OperGet() == GT_MKREFANY); - PushMkRefAnyArg(arg, curArgTabEntry, RBM_ALLINT); - argSize = (curArgTabEntry->numSlots * TARGET_POINTER_SIZE); - } - } - break; -#endif // _TARGET_ARM_ - - default: - assert(!"unhandled/unexpected arg type"); - NO_WAY("unhandled/unexpected arg type"); - } - - /* Update the current set of live variables */ - - genUpdateLife(curr); - - // Now, if some copied field locals were enregistered, and they're now dead, update the set of - // register holding gc pointers. - if (deadFieldVarRegs != 0) - gcInfo.gcMarkRegSetNpt(deadFieldVarRegs); - - /* Update the current argument stack offset */ - - argOffset += argSize; - - /* Continue with the next argument, if any more are present */ - } // while (args) - - if (lateArgs) - { - SetupLateArgs(call); - } - - /* Return the total size pushed */ - - return 0; -} - -#ifdef _TARGET_ARM_ -bool CodeGen::genFillSlotFromPromotedStruct(GenTree* arg, - fgArgTabEntry* curArgTabEntry, - LclVarDsc* promotedStructLocalVarDesc, - emitAttr fieldSize, - unsigned* pNextPromotedStructFieldVar, - unsigned* pBytesOfNextSlotOfCurPromotedStruct, - regNumber* pCurRegNum, - int argOffset, - int fieldOffsetOfFirstStackSlot, - int argOffsetOfFirstStackSlot, - regMaskTP* deadFieldVarRegs, - regNumber* pRegTmp) -{ - unsigned nextPromotedStructFieldVar = *pNextPromotedStructFieldVar; - unsigned limitPromotedStructFieldVar = - promotedStructLocalVarDesc->lvFieldLclStart + promotedStructLocalVarDesc->lvFieldCnt; - unsigned bytesOfNextSlotOfCurPromotedStruct = *pBytesOfNextSlotOfCurPromotedStruct; - - regNumber curRegNum = *pCurRegNum; - regNumber regTmp = *pRegTmp; - bool filledExtraSlot = false; - - if (nextPromotedStructFieldVar == limitPromotedStructFieldVar) - { - // We've already finished; just return. - // We can reach this because the calling loop computes a # of slots based on the size of the struct. - // If the struct has padding at the end because of alignment (say, long/int), then we'll get a call for - // the fourth slot, even though we've copied all the fields. - return false; - } - - LclVarDsc* fieldVarDsc = &compiler->lvaTable[nextPromotedStructFieldVar]; - - // Does this field fill an entire slot, and does it go at the start of the slot? - // If so, things are easier... - - bool oneFieldFillsSlotFromStart = - (fieldVarDsc->lvFldOffset < bytesOfNextSlotOfCurPromotedStruct) // The field should start in the current slot... - && ((fieldVarDsc->lvFldOffset % 4) == 0) // at the start of the slot, and... - && (nextPromotedStructFieldVar + 1 == - limitPromotedStructFieldVar // next field, if there is one, goes in the next slot. - || compiler->lvaTable[nextPromotedStructFieldVar + 1].lvFldOffset >= bytesOfNextSlotOfCurPromotedStruct); - - // Compute the proper size. - if (fieldSize == EA_4BYTE) // Not a GC ref or byref. - { - switch (fieldVarDsc->lvExactSize) - { - case 1: - fieldSize = EA_1BYTE; - break; - case 2: - fieldSize = EA_2BYTE; - break; - case 8: - // An 8-byte field will be at an 8-byte-aligned offset unless explicit layout has been used, - // in which case we should not have promoted the struct variable. - noway_assert((fieldVarDsc->lvFldOffset % 8) == 0); - - // If the current reg number is not aligned, align it, and return to the calling loop, which will - // consider that a filled slot and move on to the next argument register. - if (curRegNum != MAX_REG_ARG && ((curRegNum % 2) != 0)) - { - // We must update the slot target, however! - bytesOfNextSlotOfCurPromotedStruct += 4; - *pBytesOfNextSlotOfCurPromotedStruct = bytesOfNextSlotOfCurPromotedStruct; - return false; - } - // Dest is an aligned pair of arg regs, if the struct type demands it. - noway_assert((curRegNum % 2) == 0); - // We leave the fieldSize as EA_4BYTE; but we must do 2 reg moves. - break; - default: - assert(fieldVarDsc->lvExactSize == 4); - break; - } - } - else - { - // If the gc layout said it's a GC ref or byref, then the field size must be 4. - noway_assert(fieldVarDsc->lvExactSize == 4); - } - - // We may need the type of the field to influence instruction selection. - // If we have a TYP_LONG we can use TYP_I_IMPL and we do two loads/stores - // If the fieldVarDsc is enregistered float we must use the field's exact type - // however if it is in memory we can use an integer type TYP_I_IMPL - // - var_types fieldTypeForInstr = var_types(fieldVarDsc->lvType); - if ((fieldVarDsc->lvType == TYP_LONG) || (!fieldVarDsc->lvRegister && varTypeIsFloating(fieldTypeForInstr))) - { - fieldTypeForInstr = TYP_I_IMPL; - } - - // If we have a HFA, then it is a much simpler deal -- HFAs are completely enregistered. - if (curArgTabEntry->isHfaRegArg) - { - assert(oneFieldFillsSlotFromStart); - - // Is the field variable promoted? - if (fieldVarDsc->lvRegister) - { - // Move the field var living in register to dst, if they are different registers. - regNumber srcReg = fieldVarDsc->lvRegNum; - regNumber dstReg = curRegNum; - if (srcReg != dstReg) - { - inst_RV_RV(ins_Copy(fieldVarDsc->TypeGet()), dstReg, srcReg, fieldVarDsc->TypeGet()); - assert(genIsValidFloatReg(dstReg)); // we don't use register tracking for FP - } - } - else - { - // Move the field var living in stack to dst. - getEmitter()->emitIns_R_S(ins_Load(fieldVarDsc->TypeGet()), - fieldVarDsc->TypeGet() == TYP_DOUBLE ? EA_8BYTE : EA_4BYTE, curRegNum, - nextPromotedStructFieldVar, 0); - assert(genIsValidFloatReg(curRegNum)); // we don't use register tracking for FP - } - - // Mark the arg as used and using reg val. - genMarkTreeInReg(arg, curRegNum); - regSet.SetUsedRegFloat(arg, true); - - // Advance for double. - if (fieldVarDsc->TypeGet() == TYP_DOUBLE) - { - bytesOfNextSlotOfCurPromotedStruct += 4; - curRegNum = REG_NEXT(curRegNum); - arg->gtRegNum = curRegNum; - regSet.SetUsedRegFloat(arg, true); - filledExtraSlot = true; - } - arg->gtRegNum = curArgTabEntry->regNum; - - // Advance. - bytesOfNextSlotOfCurPromotedStruct += 4; - nextPromotedStructFieldVar++; - } - else - { - if (oneFieldFillsSlotFromStart) - { - // If we write to the stack, offset in outgoing args at which we'll write. - int fieldArgOffset = argOffsetOfFirstStackSlot + fieldVarDsc->lvFldOffset - fieldOffsetOfFirstStackSlot; - assert(fieldArgOffset >= 0); - - // Is the source a register or memory? - if (fieldVarDsc->lvRegister) - { - if (fieldTypeForInstr == TYP_DOUBLE) - { - fieldSize = EA_8BYTE; - } - - // Are we writing to a register or to the stack? - if (curRegNum != MAX_REG_ARG) - { - // Source is register and Dest is register. - - instruction insCopy = INS_mov; - - if (varTypeIsFloating(fieldTypeForInstr)) - { - if (fieldTypeForInstr == TYP_FLOAT) - { - insCopy = INS_vmov_f2i; - } - else - { - assert(fieldTypeForInstr == TYP_DOUBLE); - insCopy = INS_vmov_d2i; - } - } - - // If the value being copied is a TYP_LONG (8 bytes), it may be in two registers. Record the second - // register (which may become a tmp register, if its held in the argument register that the first - // register to be copied will overwrite). - regNumber otherRegNum = REG_STK; - if (fieldVarDsc->lvType == TYP_LONG) - { - otherRegNum = fieldVarDsc->lvOtherReg; - // Are we about to overwrite? - if (otherRegNum == curRegNum) - { - if (regTmp == REG_STK) - { - regTmp = regSet.rsPickFreeReg(); - } - // Copy the second register to the temp reg. - getEmitter()->emitIns_R_R(INS_mov, fieldSize, regTmp, otherRegNum); - regTracker.rsTrackRegCopy(regTmp, otherRegNum); - otherRegNum = regTmp; - } - } - - if (fieldVarDsc->lvType == TYP_DOUBLE) - { - assert(curRegNum <= REG_R2); - getEmitter()->emitIns_R_R_R(insCopy, fieldSize, curRegNum, genRegArgNext(curRegNum), - fieldVarDsc->lvRegNum); - regTracker.rsTrackRegTrash(curRegNum); - regTracker.rsTrackRegTrash(genRegArgNext(curRegNum)); - } - else - { - // Now do the first register. - // It might be the case that it's already in the desired register; if so do nothing. - if (curRegNum != fieldVarDsc->lvRegNum) - { - getEmitter()->emitIns_R_R(insCopy, fieldSize, curRegNum, fieldVarDsc->lvRegNum); - regTracker.rsTrackRegCopy(curRegNum, fieldVarDsc->lvRegNum); - } - } - - // In either case, mark the arg register as used. - regSet.rsMarkArgRegUsedByPromotedFieldArg(arg, curRegNum, EA_IS_GCREF(fieldSize)); - - // Is there a second half of the value? - if (fieldVarDsc->lvExactSize == 8) - { - curRegNum = genRegArgNext(curRegNum); - // The second dest reg must also be an argument register. - noway_assert(curRegNum < MAX_REG_ARG); - - // Now, if it's an 8-byte TYP_LONG, we have to do the second 4 bytes. - if (fieldVarDsc->lvType == TYP_LONG) - { - // Copy the second register into the next argument register - - // If it's a register variable for a TYP_LONG value, then otherReg now should - // hold the second register or it might say that it's in the stack. - if (otherRegNum == REG_STK) - { - // Apparently when we partially enregister, we allocate stack space for the full - // 8 bytes, and enregister the low half. Thus the final TARGET_POINTER_SIZE offset - // parameter, to get the high half. - getEmitter()->emitIns_R_S(ins_Load(fieldTypeForInstr), fieldSize, curRegNum, - nextPromotedStructFieldVar, TARGET_POINTER_SIZE); - regTracker.rsTrackRegTrash(curRegNum); - } - else - { - // The other half is in a register. - // Again, it might be the case that it's already in the desired register; if so do - // nothing. - if (curRegNum != otherRegNum) - { - getEmitter()->emitIns_R_R(INS_mov, fieldSize, curRegNum, otherRegNum); - regTracker.rsTrackRegCopy(curRegNum, otherRegNum); - } - } - } - - // Also mark the 2nd arg register as used. - regSet.rsMarkArgRegUsedByPromotedFieldArg(arg, curRegNum, false); - // Record the fact that we filled in an extra register slot - filledExtraSlot = true; - } - } - else - { - // Source is register and Dest is memory (OutgoingArgSpace). - - // Now write the srcReg into the right location in the outgoing argument list. - getEmitter()->emitIns_S_R(ins_Store(fieldTypeForInstr), fieldSize, fieldVarDsc->lvRegNum, - compiler->lvaOutgoingArgSpaceVar, fieldArgOffset); - - if (fieldVarDsc->lvExactSize == 8) - { - // Now, if it's an 8-byte TYP_LONG, we have to do the second 4 bytes. - if (fieldVarDsc->lvType == TYP_LONG) - { - if (fieldVarDsc->lvOtherReg == REG_STK) - { - // Source is stack. - if (regTmp == REG_STK) - { - regTmp = regSet.rsPickFreeReg(); - } - // Apparently if we partially enregister, we allocate stack space for the full - // 8 bytes, and enregister the low half. Thus the final TARGET_POINTER_SIZE offset - // parameter, to get the high half. - getEmitter()->emitIns_R_S(ins_Load(fieldTypeForInstr), fieldSize, regTmp, - nextPromotedStructFieldVar, TARGET_POINTER_SIZE); - regTracker.rsTrackRegTrash(regTmp); - getEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), fieldSize, regTmp, - compiler->lvaOutgoingArgSpaceVar, - fieldArgOffset + TARGET_POINTER_SIZE); - } - else - { - getEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), fieldSize, fieldVarDsc->lvOtherReg, - compiler->lvaOutgoingArgSpaceVar, - fieldArgOffset + TARGET_POINTER_SIZE); - } - } - // Record the fact that we filled in an extra register slot - filledExtraSlot = true; - } - } - assert(fieldVarDsc->lvTracked); // Must be tracked, since it's enregistered... - // If the fieldVar becomes dead, then declare the register not to contain a pointer value. - if (arg->gtFlags & GTF_VAR_DEATH) - { - *deadFieldVarRegs |= genRegMask(fieldVarDsc->lvRegNum); - // We don't bother with the second reg of a register pair, since if it has one, - // it obviously doesn't hold a pointer. - } - } - else - { - // Source is in memory. - - if (curRegNum != MAX_REG_ARG) - { - // Dest is reg. - getEmitter()->emitIns_R_S(ins_Load(fieldTypeForInstr), fieldSize, curRegNum, - nextPromotedStructFieldVar, 0); - regTracker.rsTrackRegTrash(curRegNum); - - regSet.rsMarkArgRegUsedByPromotedFieldArg(arg, curRegNum, EA_IS_GCREF(fieldSize)); - - if (fieldVarDsc->lvExactSize == 8) - { - noway_assert(fieldSize == EA_4BYTE); - curRegNum = genRegArgNext(curRegNum); - noway_assert(curRegNum < MAX_REG_ARG); // Because of 8-byte alignment. - getEmitter()->emitIns_R_S(ins_Load(TYP_I_IMPL), fieldSize, curRegNum, - nextPromotedStructFieldVar, TARGET_POINTER_SIZE); - regTracker.rsTrackRegTrash(curRegNum); - regSet.rsMarkArgRegUsedByPromotedFieldArg(arg, curRegNum, EA_IS_GCREF(fieldSize)); - // Record the fact that we filled in an extra stack slot - filledExtraSlot = true; - } - } - else - { - // Dest is stack. - if (regTmp == REG_STK) - { - regTmp = regSet.rsPickFreeReg(); - } - getEmitter()->emitIns_R_S(ins_Load(fieldTypeForInstr), fieldSize, regTmp, - nextPromotedStructFieldVar, 0); - - // Now write regTmp into the right location in the outgoing argument list. - getEmitter()->emitIns_S_R(ins_Store(fieldTypeForInstr), fieldSize, regTmp, - compiler->lvaOutgoingArgSpaceVar, fieldArgOffset); - // We overwrote "regTmp", so erase any previous value we recorded that it contained. - regTracker.rsTrackRegTrash(regTmp); - - if (fieldVarDsc->lvExactSize == 8) - { - getEmitter()->emitIns_R_S(ins_Load(fieldTypeForInstr), fieldSize, regTmp, - nextPromotedStructFieldVar, TARGET_POINTER_SIZE); - - getEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), fieldSize, regTmp, - compiler->lvaOutgoingArgSpaceVar, - fieldArgOffset + TARGET_POINTER_SIZE); - // Record the fact that we filled in an extra stack slot - filledExtraSlot = true; - } - } - } - - // Bump up the following if we filled in an extra slot - if (filledExtraSlot) - bytesOfNextSlotOfCurPromotedStruct += 4; - - // Go to the next field. - nextPromotedStructFieldVar++; - if (nextPromotedStructFieldVar == limitPromotedStructFieldVar) - { - fieldVarDsc = NULL; - } - else - { - // The next field should have the same parent variable, and we should have put the field vars in order - // sorted by offset. - assert(fieldVarDsc->lvIsStructField && compiler->lvaTable[nextPromotedStructFieldVar].lvIsStructField && - fieldVarDsc->lvParentLcl == compiler->lvaTable[nextPromotedStructFieldVar].lvParentLcl && - fieldVarDsc->lvFldOffset < compiler->lvaTable[nextPromotedStructFieldVar].lvFldOffset); - fieldVarDsc = &compiler->lvaTable[nextPromotedStructFieldVar]; - } - bytesOfNextSlotOfCurPromotedStruct += 4; - } - else // oneFieldFillsSlotFromStart == false - { - // The current slot should contain more than one field. - // We'll construct a word in memory for the slot, then load it into a register. - // (Note that it *may* be possible for the fldOffset to be greater than the largest offset in the current - // slot, in which case we'll just skip this loop altogether.) - while (fieldVarDsc != NULL && fieldVarDsc->lvFldOffset < bytesOfNextSlotOfCurPromotedStruct) - { - // If it doesn't fill a slot, it can't overflow the slot (again, because we only promote structs - // whose fields have their natural alignment, and alignment == size on ARM). - noway_assert(fieldVarDsc->lvFldOffset + fieldVarDsc->lvExactSize <= bytesOfNextSlotOfCurPromotedStruct); - - // If the argument goes to the stack, the offset in the outgoing arg area for the argument. - int fieldArgOffset = argOffsetOfFirstStackSlot + fieldVarDsc->lvFldOffset - fieldOffsetOfFirstStackSlot; - noway_assert(argOffset == INT32_MAX || - (argOffset <= fieldArgOffset && fieldArgOffset < argOffset + TARGET_POINTER_SIZE)); - - if (fieldVarDsc->lvRegister) - { - if (curRegNum != MAX_REG_ARG) - { - noway_assert(compiler->lvaPromotedStructAssemblyScratchVar != BAD_VAR_NUM); - - getEmitter()->emitIns_S_R(ins_Store(fieldTypeForInstr), fieldSize, fieldVarDsc->lvRegNum, - compiler->lvaPromotedStructAssemblyScratchVar, - fieldVarDsc->lvFldOffset % 4); - } - else - { - // Dest is stack; write directly. - getEmitter()->emitIns_S_R(ins_Store(fieldTypeForInstr), fieldSize, fieldVarDsc->lvRegNum, - compiler->lvaOutgoingArgSpaceVar, fieldArgOffset); - } - } - else - { - // Source is in memory. - - // Make sure we have a temporary register to use... - if (regTmp == REG_STK) - { - regTmp = regSet.rsPickFreeReg(); - } - getEmitter()->emitIns_R_S(ins_Load(fieldTypeForInstr), fieldSize, regTmp, - nextPromotedStructFieldVar, 0); - regTracker.rsTrackRegTrash(regTmp); - - if (curRegNum != MAX_REG_ARG) - { - noway_assert(compiler->lvaPromotedStructAssemblyScratchVar != BAD_VAR_NUM); - - getEmitter()->emitIns_S_R(ins_Store(fieldTypeForInstr), fieldSize, regTmp, - compiler->lvaPromotedStructAssemblyScratchVar, - fieldVarDsc->lvFldOffset % 4); - } - else - { - getEmitter()->emitIns_S_R(ins_Store(fieldTypeForInstr), fieldSize, regTmp, - compiler->lvaOutgoingArgSpaceVar, fieldArgOffset); - } - } - // Go to the next field. - nextPromotedStructFieldVar++; - if (nextPromotedStructFieldVar == limitPromotedStructFieldVar) - { - fieldVarDsc = NULL; - } - else - { - // The next field should have the same parent variable, and we should have put the field vars in - // order sorted by offset. - noway_assert(fieldVarDsc->lvIsStructField && - compiler->lvaTable[nextPromotedStructFieldVar].lvIsStructField && - fieldVarDsc->lvParentLcl == - compiler->lvaTable[nextPromotedStructFieldVar].lvParentLcl && - fieldVarDsc->lvFldOffset < compiler->lvaTable[nextPromotedStructFieldVar].lvFldOffset); - fieldVarDsc = &compiler->lvaTable[nextPromotedStructFieldVar]; - } - } - // Now, if we were accumulating into the first scratch word of the outgoing argument space in order to - // write to an argument register, do so. - if (curRegNum != MAX_REG_ARG) - { - noway_assert(compiler->lvaPromotedStructAssemblyScratchVar != BAD_VAR_NUM); - - getEmitter()->emitIns_R_S(ins_Load(TYP_I_IMPL), EA_4BYTE, curRegNum, - compiler->lvaPromotedStructAssemblyScratchVar, 0); - regTracker.rsTrackRegTrash(curRegNum); - regSet.rsMarkArgRegUsedByPromotedFieldArg(arg, curRegNum, EA_IS_GCREF(fieldSize)); - } - // We've finished a slot; set the goal of the next slot. - bytesOfNextSlotOfCurPromotedStruct += 4; - } - } - - // Write back the updates. - *pNextPromotedStructFieldVar = nextPromotedStructFieldVar; - *pBytesOfNextSlotOfCurPromotedStruct = bytesOfNextSlotOfCurPromotedStruct; - *pCurRegNum = curRegNum; - *pRegTmp = regTmp; - - return filledExtraSlot; -} -#endif // _TARGET_ARM_ - -regMaskTP CodeGen::genFindDeadFieldRegs(GenTree* cpBlk) -{ - noway_assert(cpBlk->OperIsCopyBlkOp()); // Precondition. - GenTree* rhs = cpBlk->gtOp.gtOp1; - regMaskTP res = 0; - if (rhs->OperIsIndir()) - { - GenTree* addr = rhs->AsIndir()->Addr(); - if (addr->gtOper == GT_ADDR) - { - rhs = addr->gtOp.gtOp1; - } - } - if (rhs->OperGet() == GT_LCL_VAR) - { - LclVarDsc* rhsDsc = &compiler->lvaTable[rhs->gtLclVarCommon.gtLclNum]; - if (rhsDsc->lvPromoted) - { - // It is promoted; iterate over its field vars. - unsigned fieldVarNum = rhsDsc->lvFieldLclStart; - for (unsigned i = 0; i < rhsDsc->lvFieldCnt; i++, fieldVarNum++) - { - LclVarDsc* fieldVarDsc = &compiler->lvaTable[fieldVarNum]; - // Did the variable go dead, and is it enregistered? - if (fieldVarDsc->lvRegister && (rhs->gtFlags & GTF_VAR_DEATH)) - { - // Add the register number to the set of registers holding field vars that are going dead. - res |= genRegMask(fieldVarDsc->lvRegNum); - } - } - } - } - return res; -} - -void CodeGen::SetupLateArgs(GenTreeCall* call) -{ - GenTreeArgList* lateArgs; - GenTree* curr; - - /* Generate the code to move the late arguments into registers */ - - for (lateArgs = call->gtCallLateArgs; lateArgs; lateArgs = lateArgs->Rest()) - { - curr = lateArgs->Current(); - assert(curr); - - fgArgTabEntry* curArgTabEntry = compiler->gtArgEntryByNode(call, curr); - assert(curArgTabEntry); - regNumber regNum = curArgTabEntry->regNum; - unsigned argOffset = curArgTabEntry->slotNum * TARGET_POINTER_SIZE; - - assert(isRegParamType(curr->TypeGet())); - assert(curr->gtType != TYP_VOID); - - /* If the register is already marked as used, it will become - multi-used. However, since it is a callee-trashed register, - we will have to spill it before the call anyway. So do it now */ - - { - // Remember which registers hold pointers. We will spill - // them, but the code that follows will fetch reg vars from - // the registers, so we need that gc compiler->info. - // Also regSet.rsSpillReg doesn't like to spill enregistered - // variables, but if this is their last use that is *exactly* - // what we need to do, so we have to temporarily pretend - // they are no longer live. - // You might ask why are they in regSet.rsMaskUsed and regSet.rsMaskVars - // when their last use is about to occur? - // It is because this is the second operand to be evaluated - // of some parent binary op, and the first operand is - // live across this tree, and thought it could re-use the - // variables register (like a GT_REG_VAR). This probably - // is caused by RegAlloc assuming the first operand would - // evaluate into another register. - regMaskTP rsTemp = regSet.rsMaskVars & regSet.rsMaskUsed & RBM_CALLEE_TRASH; - regMaskTP gcRegSavedByref = gcInfo.gcRegByrefSetCur & rsTemp; - regMaskTP gcRegSavedGCRef = gcInfo.gcRegGCrefSetCur & rsTemp; - regSet.RemoveMaskVars(rsTemp); - - regNumber regNum2 = regNum; - for (unsigned i = 0; i < curArgTabEntry->numRegs; i++) - { - if (regSet.rsMaskUsed & genRegMask(regNum2)) - { - assert(genRegMask(regNum2) & RBM_CALLEE_TRASH); - regSet.rsSpillReg(regNum2); - } - regNum2 = genRegArgNext(regNum2); - assert(i + 1 == curArgTabEntry->numRegs || regNum2 != MAX_REG_ARG); - } - - // Restore gc tracking masks. - gcInfo.gcRegByrefSetCur |= gcRegSavedByref; - gcInfo.gcRegGCrefSetCur |= gcRegSavedGCRef; - - // Set maskvars back to normal - regSet.AddMaskVars(rsTemp); - } - - /* Evaluate the argument to a register */ - - /* Check if this is the guess area for the resolve interface call - * Pass a size of EA_OFFSET*/ - if (curr->gtOper == GT_CLS_VAR && compiler->eeGetJitDataOffs(curr->gtClsVar.gtClsVarHnd) >= 0) - { - getEmitter()->emitIns_R_C(ins_Load(TYP_INT), EA_OFFSET, regNum, curr->gtClsVar.gtClsVarHnd, 0); - regTracker.rsTrackRegTrash(regNum); - - /* The value is now in the appropriate register */ - - genMarkTreeInReg(curr, regNum); - - regSet.rsMarkRegUsed(curr); - } -#ifdef _TARGET_ARM_ - else if (curr->gtType == TYP_STRUCT) - { - GenTree* arg = curr; - while (arg->gtOper == GT_COMMA) - { - GenTree* op1 = arg->gtOp.gtOp1; - genEvalSideEffects(op1); - genUpdateLife(op1); - arg = arg->gtOp.gtOp2; - } - noway_assert((arg->OperGet() == GT_OBJ) || (arg->OperGet() == GT_LCL_VAR) || - (arg->OperGet() == GT_MKREFANY)); - - // This code passes a TYP_STRUCT by value using - // the argument registers first and - // then the lvaOutgoingArgSpaceVar area. - // - - // We prefer to choose low registers here to reduce code bloat - regMaskTP regNeedMask = RBM_LOW_REGS; - unsigned firstStackSlot = 0; - unsigned argAlign = TARGET_POINTER_SIZE; - size_t originalSize = InferStructOpSizeAlign(arg, &argAlign); - - unsigned slots = (unsigned)(roundUp(originalSize, TARGET_POINTER_SIZE) / TARGET_POINTER_SIZE); - assert(slots > 0); - - if (regNum == REG_STK) - { - firstStackSlot = 0; - } - else - { - if (argAlign == (TARGET_POINTER_SIZE * 2)) - { - assert((regNum & 1) == 0); - } - - // firstStackSlot is an index of the first slot of the struct - // that is on the stack, in the range [0,slots]. If it is 'slots', - // then the entire struct is in registers. It is also equal to - // the number of slots of the struct that are passed in registers. - - if (curArgTabEntry->isHfaRegArg) - { - // HFA arguments that have been decided to go into registers fit the reg space. - assert(regNum >= FIRST_FP_ARGREG && "HFA must go in FP register"); - assert(regNum + slots - 1 <= LAST_FP_ARGREG && - "HFA argument doesn't fit entirely in FP argument registers"); - firstStackSlot = slots; - } - else if (regNum + slots > MAX_REG_ARG) - { - firstStackSlot = MAX_REG_ARG - regNum; - assert(firstStackSlot > 0); - } - else - { - firstStackSlot = slots; - } - - if (curArgTabEntry->isHfaRegArg) - { - // Mask out the registers used by an HFA arg from the ones used to compute tree into. - for (unsigned i = regNum; i < regNum + slots; i++) - { - regNeedMask &= ~genRegMask(regNumber(i)); - } - } - } - - // This holds the set of registers corresponding to enregistered promoted struct field variables - // that go dead after this use of the variable in the argument list. - regMaskTP deadFieldVarRegs = RBM_NONE; - - // If the struct being passed is an OBJ of a local struct variable that is promoted (in the - // INDEPENDENT fashion, which doesn't require writes to be written through to the variables - // home stack loc) "promotedStructLocalVarDesc" will be set to point to the local variable - // table entry for the promoted struct local. As we fill slots with the contents of a - // promoted struct, "bytesOfNextSlotOfCurPromotedStruct" will be the number of filled bytes - // that indicate another filled slot (if we have a 12-byte struct, it has 3 four byte slots; when we're - // working on the second slot, "bytesOfNextSlotOfCurPromotedStruct" will be 8, the point at which we're - // done), and "nextPromotedStructFieldVar" will be the local variable number of the next field variable - // to be copied. - LclVarDsc* promotedStructLocalVarDesc = NULL; - unsigned bytesOfNextSlotOfCurPromotedStruct = 0; // Size of slot. - unsigned nextPromotedStructFieldVar = BAD_VAR_NUM; - GenTree* structLocalTree = NULL; - - BYTE* gcLayout = NULL; - regNumber regSrc = REG_NA; - if (arg->gtOper == GT_OBJ) - { - // Are we loading a promoted struct local var? - if (arg->gtObj.gtOp1->gtOper == GT_ADDR && arg->gtObj.gtOp1->gtOp.gtOp1->gtOper == GT_LCL_VAR) - { - structLocalTree = arg->gtObj.gtOp1->gtOp.gtOp1; - unsigned structLclNum = structLocalTree->gtLclVarCommon.gtLclNum; - LclVarDsc* varDsc = &compiler->lvaTable[structLclNum]; - - Compiler::lvaPromotionType promotionType = compiler->lvaGetPromotionType(varDsc); - - if (varDsc->lvPromoted && promotionType == Compiler::PROMOTION_TYPE_INDEPENDENT) // Otherwise it is - // guaranteed to - // live on stack. - { - // Fix 388395 ARM JitStress WP7 - noway_assert(structLocalTree->TypeGet() == TYP_STRUCT); - - assert(!varDsc->lvAddrExposed); // Compiler::PROMOTION_TYPE_INDEPENDENT ==> not exposed. - promotedStructLocalVarDesc = varDsc; - nextPromotedStructFieldVar = promotedStructLocalVarDesc->lvFieldLclStart; - } - } - - if (promotedStructLocalVarDesc == NULL) - { - // If it's not a promoted struct variable, set "regSrc" to the address - // of the struct local. - genComputeReg(arg->gtObj.gtOp1, regNeedMask, RegSet::EXACT_REG, RegSet::KEEP_REG); - noway_assert(arg->gtObj.gtOp1->InReg()); - regSrc = arg->gtObj.gtOp1->gtRegNum; - // Remove this register from the set of registers that we pick from, unless slots equals 1 - if (slots > 1) - regNeedMask &= ~genRegMask(regSrc); - } - - gcLayout = new (compiler, CMK_Codegen) BYTE[slots]; - compiler->info.compCompHnd->getClassGClayout(arg->gtObj.gtClass, gcLayout); - } - else if (arg->gtOper == GT_LCL_VAR) - { - // Move the address of the LCL_VAR in arg into reg - - unsigned varNum = arg->gtLclVarCommon.gtLclNum; - - // Are we loading a promoted struct local var? - structLocalTree = arg; - unsigned structLclNum = structLocalTree->gtLclVarCommon.gtLclNum; - LclVarDsc* varDsc = &compiler->lvaTable[structLclNum]; - - noway_assert(structLocalTree->TypeGet() == TYP_STRUCT); - - Compiler::lvaPromotionType promotionType = compiler->lvaGetPromotionType(varDsc); - - if (varDsc->lvPromoted && promotionType == Compiler::PROMOTION_TYPE_INDEPENDENT) // Otherwise it is - // guaranteed to live - // on stack. - { - assert(!varDsc->lvAddrExposed); // Compiler::PROMOTION_TYPE_INDEPENDENT ==> not exposed. - promotedStructLocalVarDesc = varDsc; - nextPromotedStructFieldVar = promotedStructLocalVarDesc->lvFieldLclStart; - } - - if (promotedStructLocalVarDesc == NULL) - { - regSrc = regSet.rsPickFreeReg(regNeedMask); - // Remove this register from the set of registers that we pick from, unless slots equals 1 - if (slots > 1) - regNeedMask &= ~genRegMask(regSrc); - - getEmitter()->emitIns_R_S(INS_lea, EA_PTRSIZE, regSrc, varNum, 0); - regTracker.rsTrackRegTrash(regSrc); - - if (varDsc->lvExactSize >= TARGET_POINTER_SIZE) - { - gcLayout = compiler->lvaGetGcLayout(varNum); - } - else - { - gcLayout = new (compiler, CMK_Codegen) BYTE[1]; - gcLayout[0] = TYPE_GC_NONE; - } - } - } - else if (arg->gtOper == GT_MKREFANY) - { - assert(slots == 2); - assert((firstStackSlot == 1) || (firstStackSlot == 2)); - assert(argOffset == 0); // ??? - PushMkRefAnyArg(arg, curArgTabEntry, regNeedMask); - - // Adjust argOffset if part of this guy was pushed onto the stack - if (firstStackSlot < slots) - { - argOffset += TARGET_POINTER_SIZE; - } - - // Skip the copy loop below because we have already placed the argument in the right place - slots = 0; - gcLayout = NULL; - } - else - { - assert(!"Unsupported TYP_STRUCT arg kind"); - gcLayout = new (compiler, CMK_Codegen) BYTE[slots]; - } - - if (promotedStructLocalVarDesc != NULL) - { - // We must do do the stack parts first, since those might need values - // from argument registers that will be overwritten in the portion of the - // loop that writes into the argument registers. - bytesOfNextSlotOfCurPromotedStruct = (firstStackSlot + 1) * TARGET_POINTER_SIZE; - // Now find the var number of the first that starts in the first stack slot. - unsigned fieldVarLim = - promotedStructLocalVarDesc->lvFieldLclStart + promotedStructLocalVarDesc->lvFieldCnt; - while (compiler->lvaTable[nextPromotedStructFieldVar].lvFldOffset < - (firstStackSlot * TARGET_POINTER_SIZE) && - nextPromotedStructFieldVar < fieldVarLim) - { - nextPromotedStructFieldVar++; - } - // If we reach the limit, meaning there is no field that goes even partly in the stack, only if the - // first stack slot is after the last slot. - assert(nextPromotedStructFieldVar < fieldVarLim || firstStackSlot >= slots); - } - - if (slots > 0) // the mkref case may have set "slots" to zero. - { - // First pass the stack portion of the struct (if any) - // - int argOffsetOfFirstStackSlot = argOffset; - for (unsigned i = firstStackSlot; i < slots; i++) - { - emitAttr fieldSize; - if (gcLayout[i] == TYPE_GC_NONE) - fieldSize = EA_PTRSIZE; - else if (gcLayout[i] == TYPE_GC_REF) - fieldSize = EA_GCREF; - else - { - noway_assert(gcLayout[i] == TYPE_GC_BYREF); - fieldSize = EA_BYREF; - } - - regNumber maxRegArg = regNumber(MAX_REG_ARG); - if (promotedStructLocalVarDesc != NULL) - { - regNumber regTmp = REG_STK; - - bool filledExtraSlot = - genFillSlotFromPromotedStruct(arg, curArgTabEntry, promotedStructLocalVarDesc, fieldSize, - &nextPromotedStructFieldVar, - &bytesOfNextSlotOfCurPromotedStruct, - /*pCurRegNum*/ &maxRegArg, argOffset, - /*fieldOffsetOfFirstStackSlot*/ firstStackSlot * - TARGET_POINTER_SIZE, - argOffsetOfFirstStackSlot, &deadFieldVarRegs, ®Tmp); - if (filledExtraSlot) - { - i++; - argOffset += TARGET_POINTER_SIZE; - } - } - else // (promotedStructLocalVarDesc == NULL) - { - // when slots > 1, we perform multiple load/stores thus regTmp cannot be equal to regSrc - // and although regSrc has been excluded from regNeedMask, regNeedMask is only a *hint* - // to regSet.rsPickFreeReg, so we need to be a little more forceful. - // Otherwise, just re-use the same register. - // - regNumber regTmp = regSrc; - if (slots != 1) - { - regMaskTP regSrcUsed; - regSet.rsLockReg(genRegMask(regSrc), ®SrcUsed); - - regTmp = regSet.rsPickFreeReg(regNeedMask); - - noway_assert(regTmp != regSrc); - - regSet.rsUnlockReg(genRegMask(regSrc), regSrcUsed); - } - - getEmitter()->emitIns_R_AR(ins_Load(TYP_I_IMPL), fieldSize, regTmp, regSrc, - i * TARGET_POINTER_SIZE); - - getEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), fieldSize, regTmp, - compiler->lvaOutgoingArgSpaceVar, argOffset); - regTracker.rsTrackRegTrash(regTmp); - } - argOffset += TARGET_POINTER_SIZE; - } - - // Now pass the register portion of the struct - // - - bytesOfNextSlotOfCurPromotedStruct = TARGET_POINTER_SIZE; - if (promotedStructLocalVarDesc != NULL) - nextPromotedStructFieldVar = promotedStructLocalVarDesc->lvFieldLclStart; - - // Create a nested loop here so that the first time thru the loop - // we setup all of the regArg registers except for possibly - // the one that would overwrite regSrc. Then in the final loop - // (if necessary) we just setup regArg/regSrc with the overwrite - // - bool overwriteRegSrc = false; - bool needOverwriteRegSrc = false; - do - { - if (needOverwriteRegSrc) - overwriteRegSrc = true; - - for (unsigned i = 0; i < firstStackSlot; i++) - { - regNumber regArg = (regNumber)(regNum + i); - - if (overwriteRegSrc == false) - { - if (regArg == regSrc) - { - needOverwriteRegSrc = true; - continue; - } - } - else - { - if (regArg != regSrc) - continue; - } - - emitAttr fieldSize; - if (gcLayout[i] == TYPE_GC_NONE) - fieldSize = EA_PTRSIZE; - else if (gcLayout[i] == TYPE_GC_REF) - fieldSize = EA_GCREF; - else - { - noway_assert(gcLayout[i] == TYPE_GC_BYREF); - fieldSize = EA_BYREF; - } - - regNumber regTmp = REG_STK; - if (promotedStructLocalVarDesc != NULL) - { - bool filledExtraSlot = - genFillSlotFromPromotedStruct(arg, curArgTabEntry, promotedStructLocalVarDesc, - fieldSize, &nextPromotedStructFieldVar, - &bytesOfNextSlotOfCurPromotedStruct, - /*pCurRegNum*/ ®Arg, - /*argOffset*/ INT32_MAX, - /*fieldOffsetOfFirstStackSlot*/ INT32_MAX, - /*argOffsetOfFirstStackSlot*/ INT32_MAX, - &deadFieldVarRegs, ®Tmp); - if (filledExtraSlot) - i++; - } - else - { - getEmitter()->emitIns_R_AR(ins_Load(curArgTabEntry->isHfaRegArg ? TYP_FLOAT : TYP_I_IMPL), - fieldSize, regArg, regSrc, i * TARGET_POINTER_SIZE); - } - regTracker.rsTrackRegTrash(regArg); - } - } while (needOverwriteRegSrc != overwriteRegSrc); - } - - if ((arg->gtOper == GT_OBJ) && (promotedStructLocalVarDesc == NULL)) - { - regSet.rsMarkRegFree(genRegMask(regSrc)); - } - - if (regNum != REG_STK && promotedStructLocalVarDesc == NULL) // If promoted, we already declared the regs - // used. - { - arg->SetInReg(); - for (unsigned i = 1; i < firstStackSlot; i++) - { - arg->gtRegNum = (regNumber)(regNum + i); - curArgTabEntry->isHfaRegArg ? regSet.SetUsedRegFloat(arg, true) : regSet.rsMarkRegUsed(arg); - } - arg->gtRegNum = regNum; - curArgTabEntry->isHfaRegArg ? regSet.SetUsedRegFloat(arg, true) : regSet.rsMarkRegUsed(arg); - } - - // If we're doing struct promotion, the liveness of the promoted field vars may change after this use, - // so update liveness. - genUpdateLife(arg); - - // Now, if some copied field locals were enregistered, and they're now dead, update the set of - // register holding gc pointers. - if (deadFieldVarRegs != RBM_NONE) - gcInfo.gcMarkRegSetNpt(deadFieldVarRegs); - } - else if (curr->gtType == TYP_LONG || curr->gtType == TYP_ULONG) - { - if (curArgTabEntry->regNum == REG_STK) - { - // The arg is passed in the outgoing argument area of the stack frame - genCompIntoFreeRegPair(curr, RBM_NONE, RegSet::FREE_REG); - assert(curr->InReg()); // should be enregistered after genCompIntoFreeRegPair(curr, 0) - - inst_SA_RV(ins_Store(TYP_INT), argOffset + 0, genRegPairLo(curr->gtRegPair), TYP_INT); - inst_SA_RV(ins_Store(TYP_INT), argOffset + 4, genRegPairHi(curr->gtRegPair), TYP_INT); - } - else - { - assert(regNum < REG_ARG_LAST); - regPairNo regPair = gen2regs2pair(regNum, REG_NEXT(regNum)); - genComputeRegPair(curr, regPair, RBM_NONE, RegSet::FREE_REG, false); - assert(curr->gtRegPair == regPair); - regSet.rsMarkRegPairUsed(curr); - } - } -#endif // _TARGET_ARM_ - else if (curArgTabEntry->regNum == REG_STK) - { - // The arg is passed in the outgoing argument area of the stack frame - // - genCodeForTree(curr, 0); - assert(curr->InReg()); // should be enregistered after genCodeForTree(curr, 0) - - inst_SA_RV(ins_Store(curr->gtType), argOffset, curr->gtRegNum, curr->gtType); - - if ((genRegMask(curr->gtRegNum) & regSet.rsMaskUsed) == 0) - gcInfo.gcMarkRegSetNpt(genRegMask(curr->gtRegNum)); - } - else - { - if (!varTypeIsFloating(curr->gtType)) - { - genComputeReg(curr, genRegMask(regNum), RegSet::EXACT_REG, RegSet::FREE_REG, false); - assert(curr->gtRegNum == regNum); - regSet.rsMarkRegUsed(curr); - } - else // varTypeIsFloating(curr->gtType) - { - if (genIsValidFloatReg(regNum)) - { - genComputeReg(curr, genRegMaskFloat(regNum, curr->gtType), RegSet::EXACT_REG, RegSet::FREE_REG, - false); - assert(curr->gtRegNum == regNum); - regSet.rsMarkRegUsed(curr); - } - else - { - genCodeForTree(curr, 0); - // If we are loading a floating point type into integer registers - // then it must be for varargs. - // genCodeForTree will load it into a floating point register, - // now copy it into the correct integer register(s) - if (curr->TypeGet() == TYP_FLOAT) - { - assert(genRegMask(regNum) & RBM_CALLEE_TRASH); - regSet.rsSpillRegIfUsed(regNum); -#ifdef _TARGET_ARM_ - getEmitter()->emitIns_R_R(INS_vmov_f2i, EA_4BYTE, regNum, curr->gtRegNum); -#else -#error "Unsupported target" -#endif - regTracker.rsTrackRegTrash(regNum); - - curr->gtType = TYP_INT; // Change this to TYP_INT in case we need to spill this register - curr->gtRegNum = regNum; - regSet.rsMarkRegUsed(curr); - } - else - { - assert(curr->TypeGet() == TYP_DOUBLE); - regNumber intRegNumLo = regNum; - curr->gtType = TYP_LONG; // Change this to TYP_LONG in case we spill this -#ifdef _TARGET_ARM_ - regNumber intRegNumHi = regNumber(intRegNumLo + 1); - assert(genRegMask(intRegNumHi) & RBM_CALLEE_TRASH); - assert(genRegMask(intRegNumLo) & RBM_CALLEE_TRASH); - regSet.rsSpillRegIfUsed(intRegNumHi); - regSet.rsSpillRegIfUsed(intRegNumLo); - - getEmitter()->emitIns_R_R_R(INS_vmov_d2i, EA_8BYTE, intRegNumLo, intRegNumHi, curr->gtRegNum); - regTracker.rsTrackRegTrash(intRegNumLo); - regTracker.rsTrackRegTrash(intRegNumHi); - curr->gtRegPair = gen2regs2pair(intRegNumLo, intRegNumHi); - regSet.rsMarkRegPairUsed(curr); -#else -#error "Unsupported target" -#endif - } - } - } - } - } - - /* If any of the previously loaded arguments were spilled - reload them */ - - for (lateArgs = call->gtCallLateArgs; lateArgs; lateArgs = lateArgs->Rest()) - { - curr = lateArgs->Current(); - assert(curr); - - if (curr->gtFlags & GTF_SPILLED) - { - if (isRegPairType(curr->gtType)) - { - regSet.rsUnspillRegPair(curr, genRegPairMask(curr->gtRegPair), RegSet::KEEP_REG); - } - else - { - regSet.rsUnspillReg(curr, genRegMask(curr->gtRegNum), RegSet::KEEP_REG); - } - } - } -} - -#ifdef _TARGET_ARM_ - -// 'Push' a single GT_MKREFANY argument onto a call's argument list -// The argument is passed as described by the fgArgTabEntry -// If any part of the struct is to be passed in a register the -// regNum value will be equal to the the registers used to pass the -// the first part of the struct. -// If any part is to go onto the stack, we first generate the -// value into a register specified by 'regNeedMask' and -// then store it to the out going argument area. -// When this method returns, both parts of the TypeReference have -// been pushed onto the stack, but *no* registers have been marked -// as 'in-use', that is the responsibility of the caller. -// -void CodeGen::PushMkRefAnyArg(GenTree* mkRefAnyTree, fgArgTabEntry* curArgTabEntry, regMaskTP regNeedMask) -{ - regNumber regNum = curArgTabEntry->regNum; - regNumber regNum2; - assert(mkRefAnyTree->gtOper == GT_MKREFANY); - regMaskTP arg1RegMask = 0; - int argOffset = curArgTabEntry->slotNum * TARGET_POINTER_SIZE; - - // Construct the TypedReference directly into the argument list of the call by - // 'pushing' the first field of the typed reference: the pointer. - // Do this by directly generating it into the argument register or outgoing arg area of the stack. - // Mark it as used so we don't trash it while generating the second field. - // - if (regNum == REG_STK) - { - genComputeReg(mkRefAnyTree->gtOp.gtOp1, regNeedMask, RegSet::EXACT_REG, RegSet::FREE_REG); - noway_assert(mkRefAnyTree->gtOp.gtOp1->InReg()); - regNumber tmpReg1 = mkRefAnyTree->gtOp.gtOp1->gtRegNum; - inst_SA_RV(ins_Store(TYP_I_IMPL), argOffset, tmpReg1, TYP_I_IMPL); - regTracker.rsTrackRegTrash(tmpReg1); - argOffset += TARGET_POINTER_SIZE; - regNum2 = REG_STK; - } - else - { - assert(regNum <= REG_ARG_LAST); - arg1RegMask = genRegMask(regNum); - genComputeReg(mkRefAnyTree->gtOp.gtOp1, arg1RegMask, RegSet::EXACT_REG, RegSet::KEEP_REG); - regNum2 = (regNum == REG_ARG_LAST) ? REG_STK : genRegArgNext(regNum); - } - - // Now 'push' the second field of the typed reference: the method table. - if (regNum2 == REG_STK) - { - genComputeReg(mkRefAnyTree->gtOp.gtOp2, regNeedMask, RegSet::EXACT_REG, RegSet::FREE_REG); - noway_assert(mkRefAnyTree->gtOp.gtOp2->InReg()); - regNumber tmpReg2 = mkRefAnyTree->gtOp.gtOp2->gtRegNum; - inst_SA_RV(ins_Store(TYP_I_IMPL), argOffset, tmpReg2, TYP_I_IMPL); - regTracker.rsTrackRegTrash(tmpReg2); - } - else - { - assert(regNum2 <= REG_ARG_LAST); - // We don't have to mark this register as being in use here because it will - // be done by the caller, and we don't want to double-count it. - genComputeReg(mkRefAnyTree->gtOp.gtOp2, genRegMask(regNum2), RegSet::EXACT_REG, RegSet::FREE_REG); - } - - // Now that we are done generating the second part of the TypeReference, we can mark - // the first register as free. - // The caller in the shared path we will re-mark all registers used by this argument - // as being used, so we don't want to double-count this one. - if (arg1RegMask != 0) - { - GenTree* op1 = mkRefAnyTree->gtOp.gtOp1; - if (op1->gtFlags & GTF_SPILLED) - { - /* The register that we loaded arg1 into has been spilled -- reload it back into the correct arg register */ - - regSet.rsUnspillReg(op1, arg1RegMask, RegSet::FREE_REG); - } - else - { - regSet.rsMarkRegFree(arg1RegMask); - } - } -} -#endif // _TARGET_ARM_ - -#endif // FEATURE_FIXED_OUT_ARGS - -regMaskTP CodeGen::genLoadIndirectCallTarget(GenTreeCall* call) -{ - assert((gtCallTypes)call->gtCallType == CT_INDIRECT); - - regMaskTP fptrRegs; - - /* Loading the indirect call target might cause one or more of the previously - loaded argument registers to be spilled. So, we save information about all - the argument registers, and unspill any of them that get spilled, after - the call target is loaded. - */ - struct - { - GenTree* node; - union { - regNumber regNum; - regPairNo regPair; - }; - } regArgTab[MAX_REG_ARG]; - - /* Record the previously loaded arguments, if any */ - - unsigned regIndex; - regMaskTP prefRegs = regSet.rsRegMaskFree(); - regMaskTP argRegs = RBM_NONE; - for (regIndex = 0; regIndex < MAX_REG_ARG; regIndex++) - { - regMaskTP mask; - regNumber regNum = genMapRegArgNumToRegNum(regIndex, TYP_INT); - GenTree* argTree = regSet.rsUsedTree[regNum]; - regArgTab[regIndex].node = argTree; - if ((argTree != NULL) && (argTree->gtType != TYP_STRUCT)) // We won't spill the struct - { - assert(argTree->InReg()); - if (isRegPairType(argTree->gtType)) - { - regPairNo regPair = argTree->gtRegPair; - assert(regNum == genRegPairHi(regPair) || regNum == genRegPairLo(regPair)); - regArgTab[regIndex].regPair = regPair; - mask = genRegPairMask(regPair); - } - else - { - assert(regNum == argTree->gtRegNum); - regArgTab[regIndex].regNum = regNum; - mask = genRegMask(regNum); - } - assert(!(prefRegs & mask)); - argRegs |= mask; - } - } - - /* Record the register(s) used for the indirect call func ptr */ - fptrRegs = genMakeRvalueAddressable(call->gtCallAddr, prefRegs, RegSet::KEEP_REG, false); - - /* If any of the previously loaded arguments were spilled, reload them */ - - for (regIndex = 0; regIndex < MAX_REG_ARG; regIndex++) - { - GenTree* argTree = regArgTab[regIndex].node; - if ((argTree != NULL) && (argTree->gtFlags & GTF_SPILLED)) - { - assert(argTree->gtType != TYP_STRUCT); // We currently don't support spilling structs in argument registers - if (isRegPairType(argTree->gtType)) - { - regSet.rsUnspillRegPair(argTree, genRegPairMask(regArgTab[regIndex].regPair), RegSet::KEEP_REG); - } - else - { - regSet.rsUnspillReg(argTree, genRegMask(regArgTab[regIndex].regNum), RegSet::KEEP_REG); - } - } - } - - /* Make sure the target is still addressable while avoiding the argument registers */ - - fptrRegs = genKeepAddressable(call->gtCallAddr, fptrRegs, argRegs); - - return fptrRegs; -} - -/***************************************************************************** - * - * Generate code for a call. If the call returns a value in register(s), the - * register mask that describes where the result will be found is returned; - * otherwise, RBM_NONE is returned. - */ - -#ifdef _PREFAST_ -#pragma warning(push) -#pragma warning(disable : 21000) // Suppress PREFast warning about overly large function -#endif -regMaskTP CodeGen::genCodeForCall(GenTreeCall* call, bool valUsed) -{ - emitAttr retSize; - size_t argSize; - size_t args; - regMaskTP retVal; - emitter::EmitCallType emitCallType; - - unsigned saveStackLvl; - - BasicBlock* returnLabel = DUMMY_INIT(NULL); - LclVarDsc* frameListRoot = NULL; - - unsigned savCurIntArgReg; - unsigned savCurFloatArgReg; - - unsigned areg; - - regMaskTP fptrRegs = RBM_NONE; - regMaskTP vptrMask = RBM_NONE; - -#ifdef DEBUG - unsigned stackLvl = getEmitter()->emitCurStackLvl; - - if (compiler->verbose) - { - printf("\t\t\t\t\t\t\tBeg call "); - Compiler::printTreeID(call); - printf(" stack %02u [E=%02u]\n", genStackLevel, stackLvl); - } -#endif - -#ifdef _TARGET_ARM_ - if (compiler->opts.ShouldUsePInvokeHelpers() && (call->gtFlags & GTF_CALL_UNMANAGED) && !call->IsVirtual()) - { - (void)genPInvokeCallProlog(nullptr, 0, (CORINFO_METHOD_HANDLE) nullptr, nullptr); - } -#endif - - gtCallTypes callType = (gtCallTypes)call->gtCallType; - IL_OFFSETX ilOffset = BAD_IL_OFFSET; - - CORINFO_SIG_INFO* sigInfo = nullptr; - - if (compiler->opts.compDbgInfo && compiler->genCallSite2ILOffsetMap != NULL) - { - (void)compiler->genCallSite2ILOffsetMap->Lookup(call, &ilOffset); - } - - /* Make some sanity checks on the call node */ - - // "this" only makes sense for user functions - noway_assert(call->gtCallObjp == 0 || callType == CT_USER_FUNC || callType == CT_INDIRECT); - // tailcalls won't be done for helpers, caller-pop args, and check that - // the global flag is set - noway_assert(!call->IsTailCall() || - (callType != CT_HELPER && !(call->gtFlags & GTF_CALL_POP_ARGS) && compiler->compTailCallUsed)); - -#ifdef DEBUG - // Pass the call signature information down into the emitter so the emitter can associate - // native call sites with the signatures they were generated from. - if (callType != CT_HELPER) - { - sigInfo = call->callSig; - } -#endif // DEBUG - - unsigned pseudoStackLvl = 0; - - if (!isFramePointerUsed() && (genStackLevel != 0) && compiler->fgIsThrowHlpBlk(compiler->compCurBB)) - { - noway_assert(compiler->compCurBB->bbTreeList->gtStmt.gtStmtExpr == call); - - pseudoStackLvl = genStackLevel; - - noway_assert(!"Blocks with non-empty stack on entry are NYI in the emitter " - "so fgAddCodeRef() should have set isFramePointerRequired()"); - } - - /* Mark the current stack level and list of pointer arguments */ - - saveStackLvl = genStackLevel; - - /*------------------------------------------------------------------------- - * Set up the registers and arguments - */ - - /* We'll keep track of how much we've pushed on the stack */ - - argSize = 0; - - /* We need to get a label for the return address with the proper stack depth. */ - /* For the callee pops case (the default) that is before the args are pushed. */ - - if ((call->gtFlags & GTF_CALL_UNMANAGED) && !(call->gtFlags & GTF_CALL_POP_ARGS)) - { - returnLabel = genCreateTempLabel(); - } - - /* - Make sure to save the current argument register status - in case we have nested calls. - */ - - noway_assert(intRegState.rsCurRegArgNum <= MAX_REG_ARG); - savCurIntArgReg = intRegState.rsCurRegArgNum; - savCurFloatArgReg = floatRegState.rsCurRegArgNum; - intRegState.rsCurRegArgNum = 0; - floatRegState.rsCurRegArgNum = 0; - - /* Pass the arguments */ - - if ((call->gtCallObjp != NULL) || (call->gtCallArgs != NULL)) - { - argSize += genPushArgList(call); - } - - /* We need to get a label for the return address with the proper stack depth. */ - /* For the caller pops case (cdecl) that is after the args are pushed. */ - - if (call->gtFlags & GTF_CALL_UNMANAGED) - { - if (call->gtFlags & GTF_CALL_POP_ARGS) - returnLabel = genCreateTempLabel(); - - /* Make sure that we now have a label */ - noway_assert(returnLabel != DUMMY_INIT(NULL)); - } - - if (callType == CT_INDIRECT) - { - fptrRegs = genLoadIndirectCallTarget(call); - } - - /* Make sure any callee-trashed registers are saved */ - - regMaskTP calleeTrashedRegs = RBM_NONE; - -#if GTF_CALL_REG_SAVE - if (call->gtFlags & GTF_CALL_REG_SAVE) - { - /* The return value reg(s) will definitely be trashed */ - - switch (call->gtType) - { - case TYP_INT: - case TYP_REF: - case TYP_BYREF: -#if !CPU_HAS_FP_SUPPORT - case TYP_FLOAT: -#endif - calleeTrashedRegs = RBM_INTRET; - break; - - case TYP_LONG: -#if !CPU_HAS_FP_SUPPORT - case TYP_DOUBLE: -#endif - calleeTrashedRegs = RBM_LNGRET; - break; - - case TYP_VOID: -#if CPU_HAS_FP_SUPPORT - case TYP_FLOAT: - case TYP_DOUBLE: -#endif - calleeTrashedRegs = 0; - break; - - default: - noway_assert(!"unhandled/unexpected type"); - } - } - else -#endif - { - calleeTrashedRegs = RBM_CALLEE_TRASH; - } - - /* Spill any callee-saved registers which are being used */ - - regMaskTP spillRegs = calleeTrashedRegs & regSet.rsMaskUsed; - - /* We need to save all GC registers to the InlinedCallFrame. - Instead, just spill them to temps. */ - - if (call->gtFlags & GTF_CALL_UNMANAGED) - spillRegs |= (gcInfo.gcRegGCrefSetCur | gcInfo.gcRegByrefSetCur) & regSet.rsMaskUsed; - - // Ignore fptrRegs as it is needed only to perform the indirect call - - spillRegs &= ~fptrRegs; - - /* Do not spill the argument registers. - Multi-use of RBM_ARG_REGS should be prevented by genPushArgList() */ - - noway_assert((regSet.rsMaskMult & call->gtCallRegUsedMask) == 0); - spillRegs &= ~call->gtCallRegUsedMask; - - if (spillRegs) - { - regSet.rsSpillRegs(spillRegs); - } - -#if FEATURE_STACK_FP_X87 - // Spill fp stack - SpillForCallStackFP(); - - if (call->gtType == TYP_FLOAT || call->gtType == TYP_DOUBLE) - { - // Pick up a reg - regNumber regReturn = regSet.PickRegFloat(); - - // Assign reg to tree - genMarkTreeInReg(call, regReturn); - - // Mark as used - regSet.SetUsedRegFloat(call, true); - - // Update fp state - compCurFPState.Push(regReturn); - } -#else - SpillForCallRegisterFP(call->gtCallRegUsedMask); -#endif - - /* If the method returns a GC ref, set size to EA_GCREF or EA_BYREF */ - - retSize = EA_PTRSIZE; - - if (valUsed) - { - if (call->gtType == TYP_REF) - { - retSize = EA_GCREF; - } - else if (call->gtType == TYP_BYREF) - { - retSize = EA_BYREF; - } - } - - /*------------------------------------------------------------------------- - * For caller-pop calls, the GC info will report the arguments as pending - arguments as the caller explicitly pops them. Also should be - reported as non-GC arguments as they effectively go dead at the - call site (callee owns them) - */ - - args = (call->gtFlags & GTF_CALL_POP_ARGS) ? -int(argSize) : argSize; - -#ifdef PROFILING_SUPPORTED - - /*------------------------------------------------------------------------- - * Generate the profiling hooks for the call - */ - - /* Treat special cases first */ - - /* fire the event at the call site */ - /* alas, right now I can only handle calls via a method handle */ - if (compiler->compIsProfilerHookNeeded() && (callType == CT_USER_FUNC) && call->IsTailCall()) - { - unsigned saveStackLvl2 = genStackLevel; - - // - // Push the profilerHandle - // - CLANG_FORMAT_COMMENT_ANCHOR; - -#ifdef _TARGET_X86_ - regMaskTP byrefPushedRegs; - regMaskTP norefPushedRegs; - regMaskTP pushedArgRegs = genPushRegs(call->gtCallRegUsedMask, &byrefPushedRegs, &norefPushedRegs); - - if (compiler->compProfilerMethHndIndirected) - { - getEmitter()->emitIns_AR_R(INS_push, EA_PTR_DSP_RELOC, REG_NA, REG_NA, - (ssize_t)compiler->compProfilerMethHnd); - } - else - { - inst_IV(INS_push, (size_t)compiler->compProfilerMethHnd); - } - genSinglePush(); - - genEmitHelperCall(CORINFO_HELP_PROF_FCN_TAILCALL, - sizeof(int) * 1, // argSize - EA_UNKNOWN); // retSize - - // - // Adjust the number of stack slots used by this managed method if necessary. - // - if (compiler->fgPtrArgCntMax < 1) - { - JITDUMP("Upping fgPtrArgCntMax from %d to 1\n", compiler->fgPtrArgCntMax); - compiler->fgPtrArgCntMax = 1; - } - - genPopRegs(pushedArgRegs, byrefPushedRegs, norefPushedRegs); -#elif _TARGET_ARM_ - // We need r0 (to pass profiler handle) and another register (call target) to emit a tailcall callback. - // To make r0 available, we add REG_PROFILER_TAIL_SCRATCH as an additional interference for tail prefixed calls. - // Here we grab a register to temporarily store r0 and revert it back after we have emitted callback. - // - // By the time we reach this point argument registers are setup (by genPushArgList()), therefore we don't want - // to disturb them and hence argument registers are locked here. - regMaskTP usedMask = RBM_NONE; - regSet.rsLockReg(RBM_ARG_REGS, &usedMask); - - regNumber scratchReg = regSet.rsGrabReg(RBM_CALLEE_SAVED); - regSet.rsLockReg(genRegMask(scratchReg)); - - emitAttr attr = EA_UNKNOWN; - if (RBM_R0 & gcInfo.gcRegGCrefSetCur) - { - attr = EA_GCREF; - gcInfo.gcMarkRegSetGCref(scratchReg); - } - else if (RBM_R0 & gcInfo.gcRegByrefSetCur) - { - attr = EA_BYREF; - gcInfo.gcMarkRegSetByref(scratchReg); - } - else - { - attr = EA_4BYTE; - } - - getEmitter()->emitIns_R_R(INS_mov, attr, scratchReg, REG_R0); - regTracker.rsTrackRegTrash(scratchReg); - - if (compiler->compProfilerMethHndIndirected) - { - getEmitter()->emitIns_R_AI(INS_ldr, EA_PTR_DSP_RELOC, REG_R0, (ssize_t)compiler->compProfilerMethHnd); - regTracker.rsTrackRegTrash(REG_R0); - } - else - { - instGen_Set_Reg_To_Imm(EA_4BYTE, REG_R0, (ssize_t)compiler->compProfilerMethHnd); - } - - genEmitHelperCall(CORINFO_HELP_PROF_FCN_TAILCALL, - 0, // argSize - EA_UNKNOWN); // retSize - - // Restore back to the state that existed before profiler callback - gcInfo.gcMarkRegSetNpt(scratchReg); - getEmitter()->emitIns_R_R(INS_mov, attr, REG_R0, scratchReg); - regTracker.rsTrackRegTrash(REG_R0); - regSet.rsUnlockReg(genRegMask(scratchReg)); - regSet.rsUnlockReg(RBM_ARG_REGS, usedMask); -#else - NYI("Pushing the profilerHandle & caller's sp for the profiler callout and locking any registers"); -#endif //_TARGET_X86_ - - /* Restore the stack level */ - SetStackLevel(saveStackLvl2); - } - -#endif // PROFILING_SUPPORTED - -#ifdef DEBUG - /*------------------------------------------------------------------------- - * Generate an ESP check for the call - */ - - if (compiler->opts.compStackCheckOnCall -#if defined(USE_TRANSITION_THUNKS) || defined(USE_DYNAMIC_STACK_ALIGN) - // check the stacks as frequently as possible - && !call->IsHelperCall() -#else - && call->gtCallType == CT_USER_FUNC -#endif - ) - { - noway_assert(compiler->lvaCallEspCheck != 0xCCCCCCCC && - compiler->lvaTable[compiler->lvaCallEspCheck].lvDoNotEnregister && - compiler->lvaTable[compiler->lvaCallEspCheck].lvOnFrame); - getEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, REG_SPBASE, compiler->lvaCallEspCheck, 0); - } -#endif - - /*------------------------------------------------------------------------- - * Generate the call - */ - - bool fPossibleSyncHelperCall = false; - CorInfoHelpFunc helperNum = CORINFO_HELP_UNDEF; /* only initialized to avoid compiler C4701 warning */ - - bool fTailCallTargetIsVSD = false; - - bool fTailCall = (call->gtCallMoreFlags & GTF_CALL_M_TAILCALL) != 0; - - /* Check for Delegate.Invoke. If so, we inline it. We get the - target-object and target-function from the delegate-object, and do - an indirect call. - */ - - if ((call->gtCallMoreFlags & GTF_CALL_M_DELEGATE_INV) && !fTailCall) - { - noway_assert(call->gtCallType == CT_USER_FUNC); - - assert((compiler->info.compCompHnd->getMethodAttribs(call->gtCallMethHnd) & - (CORINFO_FLG_DELEGATE_INVOKE | CORINFO_FLG_FINAL)) == - (CORINFO_FLG_DELEGATE_INVOKE | CORINFO_FLG_FINAL)); - - /* Find the offsets of the 'this' pointer and new target */ - - CORINFO_EE_INFO* pInfo; - unsigned instOffs; // offset of new 'this' pointer - unsigned firstTgtOffs; // offset of first target to invoke - const regNumber regThis = genGetThisArgReg(call); - - pInfo = compiler->eeGetEEInfo(); - instOffs = pInfo->offsetOfDelegateInstance; - firstTgtOffs = pInfo->offsetOfDelegateFirstTarget; - -#ifdef _TARGET_ARM_ - // Ensure that we don't trash any of these registers if we have to load - // the helper call target into a register to invoke it. - regMaskTP regsUsed = 0; - - if ((call->gtCallMoreFlags & GTF_CALL_M_SECURE_DELEGATE_INV)) - { - getEmitter()->emitIns_R_R_I(INS_add, EA_BYREF, compiler->virtualStubParamInfo->GetReg(), regThis, - pInfo->offsetOfSecureDelegateIndirectCell); - regTracker.rsTrackRegTrash(compiler->virtualStubParamInfo->GetReg()); - - // Ensure that the virtual stub param info register doesn't get reused before the call is taken - regSet.rsLockReg(compiler->virtualStubParamInfo->GetRegMask(), ®sUsed); - } - -#endif // _TARGET_ARM_ - - // Grab an available register to use for the CALL indirection - regNumber indCallReg = regSet.rsGrabReg(RBM_ALLINT); - - // Save the invoke-target-function in indCallReg - // 'mov indCallReg, dword ptr [regThis + firstTgtOffs]' - getEmitter()->emitIns_R_AR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, indCallReg, regThis, firstTgtOffs); - regTracker.rsTrackRegTrash(indCallReg); - - /* Set new 'this' in REG_CALL_THIS - 'mov REG_CALL_THIS, dword ptr [regThis + instOffs]' */ - - getEmitter()->emitIns_R_AR(ins_Load(TYP_I_IMPL), EA_GCREF, regThis, regThis, instOffs); - regTracker.rsTrackRegTrash(regThis); - noway_assert(instOffs < 127); - - /* Call through indCallReg */ - - getEmitter()->emitIns_Call(emitter::EC_INDIR_R, - NULL, // methHnd - INDEBUG_LDISASM_COMMA(sigInfo) NULL, // addr - args, retSize, gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, - gcInfo.gcRegByrefSetCur, ilOffset, indCallReg); - -#ifdef _TARGET_ARM_ - if ((call->gtCallMoreFlags & GTF_CALL_M_SECURE_DELEGATE_INV)) - { - regSet.rsUnlockReg(compiler->virtualStubParamInfo->GetRegMask(), regsUsed); - } -#endif // _TARGET_ARM_ - } - else - - /*------------------------------------------------------------------------- - * Virtual and interface calls - */ - - switch (call->gtFlags & GTF_CALL_VIRT_KIND_MASK) - { - case GTF_CALL_VIRT_STUB: - { - regSet.rsSetRegsModified(compiler->virtualStubParamInfo->GetRegMask()); - - // An x86 JIT which uses full stub dispatch must generate only - // the following stub dispatch calls: - // - // (1) isCallRelativeIndirect: - // call dword ptr [rel32] ; FF 15 ---rel32---- - // (2) isCallRelative: - // call abc ; E8 ---rel32---- - // (3) isCallRegisterIndirect: - // 3-byte nop ; - // call dword ptr [eax] ; FF 10 - // - // THIS IS VERY TIGHTLY TIED TO THE PREDICATES IN - // vm\i386\cGenCpu.h, esp. isCallRegisterIndirect. - - // - // Please do not insert any Random NOPs while constructing this VSD call - // - getEmitter()->emitDisableRandomNops(); - - if (!fTailCall) - { - // This is code to set up an indirect call to a stub address computed - // via dictionary lookup. However the dispatch stub receivers aren't set up - // to accept such calls at the moment. - if (callType == CT_INDIRECT) - { - regNumber indReg; - - // ------------------------------------------------------------------------- - // The importer decided we needed a stub call via a computed - // stub dispatch address, i.e. an address which came from a dictionary lookup. - // - The dictionary lookup produces an indirected address, suitable for call - // via "call [virtualStubParamInfo.reg]" - // - // This combination will only be generated for shared generic code and when - // stub dispatch is active. - - // No need to null check the this pointer - the dispatch code will deal with this. - - noway_assert(genStillAddressable(call->gtCallAddr)); - - // Now put the address in virtualStubParamInfo.reg. - // This is typically a nop when the register used for - // the gtCallAddr is virtualStubParamInfo.reg - // - inst_RV_TT(INS_mov, compiler->virtualStubParamInfo->GetReg(), call->gtCallAddr); - regTracker.rsTrackRegTrash(compiler->virtualStubParamInfo->GetReg()); - -#if defined(_TARGET_X86_) - // Emit enough bytes of nops so that this sequence can be distinguished - // from other virtual stub dispatch calls. - // - // NOTE: THIS IS VERY TIGHTLY TIED TO THE PREDICATES IN - // vm\i386\cGenCpu.h, esp. isCallRegisterIndirect. - // - getEmitter()->emitIns_Nop(3); - - // Make the virtual stub call: - // call [virtualStubParamInfo.reg] - // - emitCallType = emitter::EC_INDIR_ARD; - - indReg = compiler->virtualStubParamInfo->GetReg(); - genDoneAddressable(call->gtCallAddr, fptrRegs, RegSet::KEEP_REG); - -#elif CPU_LOAD_STORE_ARCH // ARM doesn't allow us to use an indirection for the call - - genDoneAddressable(call->gtCallAddr, fptrRegs, RegSet::KEEP_REG); - - // Make the virtual stub call: - // ldr indReg, [virtualStubParamInfo.reg] - // call indReg - // - emitCallType = emitter::EC_INDIR_R; - - // Now dereference [virtualStubParamInfo.reg] and put it in a new temp register 'indReg' - // - indReg = regSet.rsGrabReg(RBM_ALLINT & ~compiler->virtualStubParamInfo->GetRegMask()); - assert(call->gtCallAddr->InReg()); - getEmitter()->emitIns_R_R_I(INS_ldr, EA_PTRSIZE, indReg, - compiler->virtualStubParamInfo->GetReg(), 0); - regTracker.rsTrackRegTrash(indReg); - -#else -#error "Unknown target for VSD call" -#endif - - getEmitter()->emitIns_Call(emitCallType, - NULL, // methHnd - INDEBUG_LDISASM_COMMA(sigInfo) NULL, // addr - args, retSize, gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, - gcInfo.gcRegByrefSetCur, ilOffset, indReg); - } - else - { - // ------------------------------------------------------------------------- - // Check for a direct stub call. - // - - // Get stub addr. This will return NULL if virtual call stubs are not active - void* stubAddr = NULL; - - stubAddr = (void*)call->gtStubCallStubAddr; - - noway_assert(stubAddr != NULL); - - // ------------------------------------------------------------------------- - // Direct stub calls, though the stubAddr itself may still need to be - // accesed via an indirection. - // - - // No need to null check - the dispatch code will deal with null this. - - emitter::EmitCallType callTypeStubAddr = emitter::EC_FUNC_ADDR; - void* addr = stubAddr; - int disp = 0; - regNumber callReg = REG_NA; - - if (call->gtCallMoreFlags & GTF_CALL_M_VIRTSTUB_REL_INDIRECT) - { -#if CPU_LOAD_STORE_ARCH - callReg = regSet.rsGrabReg(compiler->virtualStubParamInfo->GetRegMask()); - noway_assert(callReg == compiler->virtualStubParamInfo->GetReg()); - - instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, compiler->virtualStubParamInfo->GetReg(), - (ssize_t)stubAddr); - // The stub will write-back to this register, so don't track it - regTracker.rsTrackRegTrash(compiler->virtualStubParamInfo->GetReg()); - regNumber indReg; - if (compiler->IsTargetAbi(CORINFO_CORERT_ABI)) - { - indReg = regSet.rsGrabReg(RBM_ALLINT & ~compiler->virtualStubParamInfo->GetRegMask()); - } - else - { - indReg = REG_JUMP_THUNK_PARAM; - } - getEmitter()->emitIns_R_R_I(INS_ldr, EA_PTRSIZE, indReg, - compiler->virtualStubParamInfo->GetReg(), 0); - regTracker.rsTrackRegTrash(indReg); - callTypeStubAddr = emitter::EC_INDIR_R; - getEmitter()->emitIns_Call(emitter::EC_INDIR_R, - NULL, // methHnd - INDEBUG_LDISASM_COMMA(sigInfo) NULL, // addr - args, retSize, gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, - gcInfo.gcRegByrefSetCur, ilOffset, indReg); - -#else - // emit an indirect call - callTypeStubAddr = emitter::EC_INDIR_C; - addr = 0; - disp = (ssize_t)stubAddr; -#endif - } -#if CPU_LOAD_STORE_ARCH - if (callTypeStubAddr != emitter::EC_INDIR_R) -#endif - { - getEmitter()->emitIns_Call(callTypeStubAddr, call->gtCallMethHnd, - INDEBUG_LDISASM_COMMA(sigInfo) addr, args, retSize, - gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, - gcInfo.gcRegByrefSetCur, ilOffset, callReg, REG_NA, 0, disp); - } - } - } - else // tailCall is true - { - -// Non-X86 tail calls materialize the null-check in fgMorphTailCall, when it -// moves the this pointer out of it's usual place and into the argument list. -#ifdef _TARGET_X86_ - - // Generate "cmp ECX, [ECX]" to trap null pointers - const regNumber regThis = genGetThisArgReg(call); - getEmitter()->emitIns_AR_R(INS_cmp, EA_4BYTE, regThis, regThis, 0); - -#endif // _TARGET_X86_ - - if (callType == CT_INDIRECT) - { - noway_assert(genStillAddressable(call->gtCallAddr)); - - // Now put the address in EAX. - inst_RV_TT(INS_mov, REG_TAILCALL_ADDR, call->gtCallAddr); - regTracker.rsTrackRegTrash(REG_TAILCALL_ADDR); - - genDoneAddressable(call->gtCallAddr, fptrRegs, RegSet::KEEP_REG); - } - else - { - // importer/EE should guarantee the indirection - noway_assert(call->gtCallMoreFlags & GTF_CALL_M_VIRTSTUB_REL_INDIRECT); - - instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, REG_TAILCALL_ADDR, - ssize_t(call->gtStubCallStubAddr)); - } - - fTailCallTargetIsVSD = true; - } - - // - // OK to start inserting random NOPs again - // - getEmitter()->emitEnableRandomNops(); - } - break; - - case GTF_CALL_VIRT_VTABLE: - // stub dispatching is off or this is not a virtual call (could be a tailcall) - { - regNumber vptrReg; - regNumber vptrReg1 = REG_NA; - regMaskTP vptrMask1 = RBM_NONE; - unsigned vtabOffsOfIndirection; - unsigned vtabOffsAfterIndirection; - bool isRelative; - - noway_assert(callType == CT_USER_FUNC); - - /* Get hold of the vtable offset (note: this might be expensive) */ - - compiler->info.compCompHnd->getMethodVTableOffset(call->gtCallMethHnd, &vtabOffsOfIndirection, - &vtabOffsAfterIndirection, &isRelative); - - vptrReg = - regSet.rsGrabReg(RBM_ALLINT); // Grab an available register to use for the CALL indirection - vptrMask = genRegMask(vptrReg); - - if (isRelative) - { - vptrReg1 = regSet.rsGrabReg(RBM_ALLINT & ~vptrMask); - vptrMask1 = genRegMask(vptrReg1); - } - - /* The register no longer holds a live pointer value */ - gcInfo.gcMarkRegSetNpt(vptrMask); - - if (isRelative) - { - gcInfo.gcMarkRegSetNpt(vptrMask1); - } - - // MOV vptrReg, [REG_CALL_THIS + offs] - getEmitter()->emitIns_R_AR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, vptrReg, genGetThisArgReg(call), - VPTR_OFFS); - regTracker.rsTrackRegTrash(vptrReg); - - if (isRelative) - { - regTracker.rsTrackRegTrash(vptrReg1); - } - - noway_assert(vptrMask & ~call->gtCallRegUsedMask); - - /* The register no longer holds a live pointer value */ - gcInfo.gcMarkRegSetNpt(vptrMask); - - /* Get the appropriate vtable chunk */ - - if (vtabOffsOfIndirection != CORINFO_VIRTUALCALL_NO_CHUNK) - { - if (isRelative) - { -#if defined(_TARGET_ARM_) - unsigned offset = vtabOffsOfIndirection + vtabOffsAfterIndirection; - - // ADD vptrReg1, REG_CALL_IND_SCRATCH, vtabOffsOfIndirection + vtabOffsAfterIndirection - getEmitter()->emitIns_R_R_I(INS_add, EA_PTRSIZE, vptrReg1, vptrReg, offset); -#else - unreached(); -#endif - } - - // MOV vptrReg, [REG_CALL_IND_SCRATCH + vtabOffsOfIndirection] - getEmitter()->emitIns_R_AR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, vptrReg, vptrReg, - vtabOffsOfIndirection); - } - else - { - assert(!isRelative); - } - - /* Call through the appropriate vtable slot */ - - if (fTailCall) - { - if (isRelative) - { -#if defined(_TARGET_ARM_) - /* Load the function address: "[vptrReg1 + vptrReg] -> reg_intret" */ - getEmitter()->emitIns_R_ARR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, REG_TAILCALL_ADDR, vptrReg1, - vptrReg, 0); -#else - unreached(); -#endif - } - else - { - /* Load the function address: "[vptrReg+vtabOffs] -> reg_intret" */ - getEmitter()->emitIns_R_AR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, REG_TAILCALL_ADDR, vptrReg, - vtabOffsAfterIndirection); - } - } - else - { -#if CPU_LOAD_STORE_ARCH - if (isRelative) - { - getEmitter()->emitIns_R_ARR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, vptrReg, vptrReg1, vptrReg, - 0); - } - else - { - getEmitter()->emitIns_R_AR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, vptrReg, vptrReg, - vtabOffsAfterIndirection); - } - - getEmitter()->emitIns_Call(emitter::EC_INDIR_R, call->gtCallMethHnd, - INDEBUG_LDISASM_COMMA(sigInfo) NULL, // addr - args, retSize, gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, - gcInfo.gcRegByrefSetCur, ilOffset, - vptrReg); // ireg -#else - assert(!isRelative); - getEmitter()->emitIns_Call(emitter::EC_FUNC_VIRTUAL, call->gtCallMethHnd, - INDEBUG_LDISASM_COMMA(sigInfo) NULL, // addr - args, retSize, gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, - gcInfo.gcRegByrefSetCur, ilOffset, - vptrReg, // ireg - REG_NA, // xreg - 0, // xmul - vtabOffsAfterIndirection); // disp -#endif // CPU_LOAD_STORE_ARCH - } - } - break; - - case GTF_CALL_NONVIRT: - { - //------------------------ Non-virtual/Indirect calls ------------------------- - // Lots of cases follow - // - Direct P/Invoke calls - // - Indirect calls to P/Invoke functions via the P/Invoke stub - // - Direct Helper calls - // - Indirect Helper calls - // - Direct calls to known addresses - // - Direct calls where address is accessed by one or two indirections - // - Indirect calls to computed addresses - // - Tailcall versions of all of the above - - CORINFO_METHOD_HANDLE methHnd = call->gtCallMethHnd; - - //------------------------------------------------------ - // Non-virtual/Indirect calls: Insert a null check on the "this" pointer if needed - // - // For (final and private) functions which were called with - // invokevirtual, but which we call directly, we need to - // dereference the object pointer to make sure it's not NULL. - // - - if (call->gtFlags & GTF_CALL_NULLCHECK) - { - /* Generate "cmp ECX, [ECX]" to trap null pointers */ - const regNumber regThis = genGetThisArgReg(call); -#if CPU_LOAD_STORE_ARCH - regNumber indReg = - regSet.rsGrabReg(RBM_ALLINT); // Grab an available register to use for the indirection - getEmitter()->emitIns_R_R_I(INS_ldr, EA_4BYTE, indReg, regThis, 0); - regTracker.rsTrackRegTrash(indReg); -#else - getEmitter()->emitIns_AR_R(INS_cmp, EA_4BYTE, regThis, regThis, 0); -#endif - } - - if (call->gtFlags & GTF_CALL_UNMANAGED) - { - //------------------------------------------------------ - // Non-virtual/Indirect calls: PInvoke calls. - - noway_assert(compiler->info.compCallUnmanaged != 0); - - /* args shouldn't be greater than 64K */ - - noway_assert((argSize & 0xffff0000) == 0); - - /* Remember the varDsc for the callsite-epilog */ - - frameListRoot = &compiler->lvaTable[compiler->info.compLvFrameListRoot]; - - // exact codegen is required - getEmitter()->emitDisableRandomNops(); - - int nArgSize = 0; - - regNumber indCallReg = REG_NA; - - if (callType == CT_INDIRECT) - { - noway_assert(genStillAddressable(call->gtCallAddr)); - - if (call->gtCallAddr->InReg()) - indCallReg = call->gtCallAddr->gtRegNum; - - nArgSize = (call->gtFlags & GTF_CALL_POP_ARGS) ? 0 : (int)argSize; - methHnd = 0; - } - else - { - noway_assert(callType == CT_USER_FUNC); - } - - regNumber tcbReg = REG_NA; - - if (!compiler->opts.ShouldUsePInvokeHelpers()) - { - tcbReg = genPInvokeCallProlog(frameListRoot, nArgSize, methHnd, returnLabel); - } - - void* addr = NULL; - - if (callType == CT_INDIRECT) - { - /* Double check that the callee didn't use/trash the - registers holding the call target. - */ - noway_assert(tcbReg != indCallReg); - - if (indCallReg == REG_NA) - { - indCallReg = regSet.rsGrabReg(RBM_ALLINT); // Grab an available register to use for the CALL - // indirection - - /* Please note that this even works with tcbReg == REG_EAX. - tcbReg contains an interesting value only if frameListRoot is - an enregistered local that stays alive across the call - (certainly not EAX). If frameListRoot has been moved into - EAX, we can trash it since it won't survive across the call - anyways. - */ - - inst_RV_TT(INS_mov, indCallReg, call->gtCallAddr); - regTracker.rsTrackRegTrash(indCallReg); - } - - emitCallType = emitter::EC_INDIR_R; - } - else - { - noway_assert(callType == CT_USER_FUNC); - - CORINFO_CONST_LOOKUP lookup; - compiler->info.compCompHnd->getAddressOfPInvokeTarget(methHnd, &lookup); - - addr = lookup.addr; - - assert(addr != NULL); - -#if defined(_TARGET_ARM_) - // Legacy backend does not handle the `IAT_VALUE` case that does not - // fit. It is not reachable currently from any front end so just check - // for it via assert. - assert(lookup.accessType != IAT_VALUE || arm_Valid_Imm_For_BL((ssize_t)addr)); -#endif - if (lookup.accessType == IAT_VALUE || lookup.accessType == IAT_PVALUE) - { -#if CPU_LOAD_STORE_ARCH - // Load the address into a register, indirect it and call through a register - indCallReg = regSet.rsGrabReg(RBM_ALLINT); // Grab an available register to use for the CALL - // indirection - instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, indCallReg, (ssize_t)addr); - - if (lookup.accessType == IAT_PVALUE) - { - getEmitter()->emitIns_R_R_I(INS_ldr, EA_PTRSIZE, indCallReg, indCallReg, 0); - } - - regTracker.rsTrackRegTrash(indCallReg); - // Now make the call "call indCallReg" - - getEmitter()->emitIns_Call(emitter::EC_INDIR_R, - methHnd, // methHnd - INDEBUG_LDISASM_COMMA(sigInfo) // sigInfo - NULL, // addr - args, - retSize, gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, - gcInfo.gcRegByrefSetCur, ilOffset, indCallReg); - - emitCallType = emitter::EC_INDIR_R; - break; -#else - emitCallType = emitter::EC_FUNC_TOKEN_INDIR; - indCallReg = REG_NA; -#endif - } - else - { - assert(lookup.accessType == IAT_PPVALUE); - // Double-indirection. Load the address into a register - // and call indirectly through a register - indCallReg = regSet.rsGrabReg(RBM_ALLINT); // Grab an available register to use for the CALL - // indirection - -#if CPU_LOAD_STORE_ARCH - instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, indCallReg, (ssize_t)addr); - getEmitter()->emitIns_R_R_I(INS_ldr, EA_PTRSIZE, indCallReg, indCallReg, 0); - getEmitter()->emitIns_R_R_I(INS_ldr, EA_PTRSIZE, indCallReg, indCallReg, 0); - regTracker.rsTrackRegTrash(indCallReg); - - emitCallType = emitter::EC_INDIR_R; - -#else - getEmitter()->emitIns_R_AI(INS_mov, EA_PTR_DSP_RELOC, indCallReg, (ssize_t)addr); - regTracker.rsTrackRegTrash(indCallReg); - emitCallType = emitter::EC_INDIR_ARD; - -#endif // CPU_LOAD_STORE_ARCH - - // For a indirect calls, we don't want to pass the address (used below), - // so set it to nullptr. (We've already used the address to load up the target register.) - addr = nullptr; - } - } - - getEmitter()->emitIns_Call(emitCallType, compiler->eeMarkNativeTarget(methHnd), - INDEBUG_LDISASM_COMMA(sigInfo) addr, args, retSize, - gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, - ilOffset, indCallReg); - - if (callType == CT_INDIRECT) - genDoneAddressable(call->gtCallAddr, fptrRegs, RegSet::KEEP_REG); - - getEmitter()->emitEnableRandomNops(); - - // Done with PInvoke calls - break; - } - - if (callType == CT_INDIRECT) - { - noway_assert(genStillAddressable(call->gtCallAddr)); - - if (call->gtCallCookie) - { - //------------------------------------------------------ - // Non-virtual indirect calls via the P/Invoke stub - - GenTree* cookie = call->gtCallCookie; - GenTree* target = call->gtCallAddr; - - noway_assert((call->gtFlags & GTF_CALL_POP_ARGS) == 0); - - noway_assert(cookie->gtOper == GT_CNS_INT || - cookie->gtOper == GT_IND && cookie->gtOp.gtOp1->gtOper == GT_CNS_INT); - - noway_assert(args == argSize); - -#if defined(_TARGET_X86_) - /* load eax with the real target */ - - inst_RV_TT(INS_mov, REG_EAX, target); - regTracker.rsTrackRegTrash(REG_EAX); - - if (cookie->gtOper == GT_CNS_INT) - inst_IV_handle(INS_push, cookie->gtIntCon.gtIconVal); - else - inst_TT(INS_push, cookie); - - /* Keep track of ESP for EBP-less frames */ - genSinglePush(); - - argSize += REGSIZE_BYTES; - -#elif defined(_TARGET_ARM_) - - // Ensure that we spill these registers (if caller saved) in the prolog - regSet.rsSetRegsModified(RBM_PINVOKE_COOKIE_PARAM | RBM_PINVOKE_TARGET_PARAM); - - // ARM: load r12 with the real target - // X64: load r10 with the real target - inst_RV_TT(INS_mov, REG_PINVOKE_TARGET_PARAM, target); - regTracker.rsTrackRegTrash(REG_PINVOKE_TARGET_PARAM); - - // ARM: load r4 with the pinvoke VASigCookie - // X64: load r11 with the pinvoke VASigCookie - if (cookie->gtOper == GT_CNS_INT) - inst_RV_IV(INS_mov, REG_PINVOKE_COOKIE_PARAM, cookie->gtIntCon.gtIconVal, - EA_HANDLE_CNS_RELOC); - else - inst_RV_TT(INS_mov, REG_PINVOKE_COOKIE_PARAM, cookie); - regTracker.rsTrackRegTrash(REG_PINVOKE_COOKIE_PARAM); - - noway_assert(args == argSize); - - // Ensure that we don't trash any of these registers if we have to load - // the helper call target into a register to invoke it. - regMaskTP regsUsed; - regSet.rsLockReg(call->gtCallRegUsedMask | RBM_PINVOKE_TARGET_PARAM | RBM_PINVOKE_COOKIE_PARAM, - ®sUsed); -#else - NYI("Non-virtual indirect calls via the P/Invoke stub"); -#endif - - args = argSize; - noway_assert((size_t)(int)args == args); - - genEmitHelperCall(CORINFO_HELP_PINVOKE_CALLI, (int)args, retSize); - -#if defined(_TARGET_ARM_) - regSet.rsUnlockReg(call->gtCallRegUsedMask | RBM_PINVOKE_TARGET_PARAM | - RBM_PINVOKE_COOKIE_PARAM, - regsUsed); -#endif - -#ifdef _TARGET_ARM_ - // genEmitHelperCall doesn't record all registers a helper call would trash. - regTracker.rsTrackRegTrash(REG_PINVOKE_COOKIE_PARAM); -#endif - } - else - { - //------------------------------------------------------ - // Non-virtual indirect calls - - if (fTailCall) - { - inst_RV_TT(INS_mov, REG_TAILCALL_ADDR, call->gtCallAddr); - regTracker.rsTrackRegTrash(REG_TAILCALL_ADDR); - } - else - instEmit_indCall(call, args, retSize); - } - - genDoneAddressable(call->gtCallAddr, fptrRegs, RegSet::KEEP_REG); - - // Done with indirect calls - break; - } - - //------------------------------------------------------ - // Non-virtual direct/indirect calls: Work out if the address of the - // call is known at JIT time (if not it is either an indirect call - // or the address must be accessed via an single/double indirection) - - noway_assert(callType == CT_USER_FUNC || callType == CT_HELPER); - - void* addr; - InfoAccessType accessType; - - helperNum = compiler->eeGetHelperNum(methHnd); - - if (callType == CT_HELPER) - { - noway_assert(helperNum != CORINFO_HELP_UNDEF); - -#ifdef FEATURE_READYTORUN_COMPILER - if (call->gtEntryPoint.addr != NULL) - { - accessType = call->gtEntryPoint.accessType; - addr = call->gtEntryPoint.addr; - } - else -#endif // FEATURE_READYTORUN_COMPILER - { - void* pAddr; - - accessType = IAT_VALUE; - addr = compiler->compGetHelperFtn(helperNum, (void**)&pAddr); - - if (!addr) - { - accessType = IAT_PVALUE; - addr = pAddr; - } - } - } - else - { - noway_assert(helperNum == CORINFO_HELP_UNDEF); - - CORINFO_ACCESS_FLAGS aflags = CORINFO_ACCESS_ANY; - - if (call->gtCallMoreFlags & GTF_CALL_M_NONVIRT_SAME_THIS) - aflags = (CORINFO_ACCESS_FLAGS)(aflags | CORINFO_ACCESS_THIS); - - if ((call->gtFlags & GTF_CALL_NULLCHECK) == 0) - aflags = (CORINFO_ACCESS_FLAGS)(aflags | CORINFO_ACCESS_NONNULL); - -#ifdef FEATURE_READYTORUN_COMPILER - if (call->gtEntryPoint.addr != NULL) - { - accessType = call->gtEntryPoint.accessType; - addr = call->gtEntryPoint.addr; - } - else -#endif // FEATURE_READYTORUN_COMPILER - { - CORINFO_CONST_LOOKUP addrInfo; - compiler->info.compCompHnd->getFunctionEntryPoint(methHnd, &addrInfo, aflags); - - accessType = addrInfo.accessType; - addr = addrInfo.addr; - } - } - - if (fTailCall) - { - noway_assert(callType == CT_USER_FUNC); - - switch (accessType) - { - case IAT_VALUE: - //------------------------------------------------------ - // Non-virtual direct calls to known addressess - // - instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, REG_TAILCALL_ADDR, (ssize_t)addr); - break; - - case IAT_PVALUE: - //------------------------------------------------------ - // Non-virtual direct calls to addresses accessed by - // a single indirection. - // - // For tailcalls we place the target address in REG_TAILCALL_ADDR - CLANG_FORMAT_COMMENT_ANCHOR; - -#if CPU_LOAD_STORE_ARCH - { - regNumber indReg = REG_TAILCALL_ADDR; - instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, indReg, (ssize_t)addr); - getEmitter()->emitIns_R_R_I(INS_ldr, EA_4BYTE, indReg, indReg, 0); - regTracker.rsTrackRegTrash(indReg); - } -#else - getEmitter()->emitIns_R_AI(INS_mov, EA_PTR_DSP_RELOC, REG_TAILCALL_ADDR, (ssize_t)addr); - regTracker.rsTrackRegTrash(REG_TAILCALL_ADDR); -#endif - break; - - case IAT_PPVALUE: - //------------------------------------------------------ - // Non-virtual direct calls to addresses accessed by - // a double indirection. - // - // For tailcalls we place the target address in REG_TAILCALL_ADDR - CLANG_FORMAT_COMMENT_ANCHOR; - -#if CPU_LOAD_STORE_ARCH - { - regNumber indReg = REG_TAILCALL_ADDR; - instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, indReg, (ssize_t)addr); - getEmitter()->emitIns_R_R_I(INS_ldr, EA_4BYTE, indReg, indReg, 0); - getEmitter()->emitIns_R_R_I(INS_ldr, EA_4BYTE, indReg, indReg, 0); - regTracker.rsTrackRegTrash(indReg); - } -#else - getEmitter()->emitIns_R_AI(INS_mov, EA_PTR_DSP_RELOC, REG_TAILCALL_ADDR, (ssize_t)addr); - getEmitter()->emitIns_R_AR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, REG_TAILCALL_ADDR, - REG_TAILCALL_ADDR, 0); - regTracker.rsTrackRegTrash(REG_TAILCALL_ADDR); -#endif - break; - - default: - noway_assert(!"Bad accessType"); - break; - } - } - else - { - switch (accessType) - { - regNumber indCallReg; - - case IAT_VALUE: - { - //------------------------------------------------------ - // Non-virtual direct calls to known addressess - // - // The vast majority of calls end up here.... Wouldn't - // it be nice if they all did! - CLANG_FORMAT_COMMENT_ANCHOR; -#ifdef _TARGET_ARM_ - // We may use direct call for some of recursive calls - // as we can safely estimate the distance from the call site to the top of the method - const int codeOffset = MAX_PROLOG_SIZE_BYTES + // prolog size - getEmitter()->emitCurCodeOffset + // offset of the current IG - getEmitter()->emitCurIGsize + // size of the current IG - 4; // size of the jump instruction - // that we are now emitting - if (compiler->gtIsRecursiveCall(call) && codeOffset <= -CALL_DIST_MAX_NEG) - { - getEmitter()->emitIns_Call(emitter::EC_FUNC_TOKEN, methHnd, - INDEBUG_LDISASM_COMMA(sigInfo) NULL, // addr - args, retSize, gcInfo.gcVarPtrSetCur, - gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, ilOffset, - REG_NA, REG_NA, 0, 0, // ireg, xreg, xmul, disp - false, // isJump - emitter::emitNoGChelper(helperNum)); - } - else if (!arm_Valid_Imm_For_BL((ssize_t)addr)) - { - // Load the address into a register and call through a register - indCallReg = regSet.rsGrabReg(RBM_ALLINT); // Grab an available register to use for the - // CALL indirection - instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, indCallReg, (ssize_t)addr); - - getEmitter()->emitIns_Call(emitter::EC_INDIR_R, methHnd, - INDEBUG_LDISASM_COMMA(sigInfo) NULL, // addr - args, retSize, gcInfo.gcVarPtrSetCur, - gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, ilOffset, - indCallReg, // ireg - REG_NA, 0, 0, // xreg, xmul, disp - false, // isJump - emitter::emitNoGChelper(helperNum)); - } - else -#endif - { - getEmitter()->emitIns_Call(emitter::EC_FUNC_TOKEN, methHnd, - INDEBUG_LDISASM_COMMA(sigInfo) addr, args, retSize, - gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, - gcInfo.gcRegByrefSetCur, ilOffset, REG_NA, REG_NA, 0, - 0, /* ireg, xreg, xmul, disp */ - false, /* isJump */ - emitter::emitNoGChelper(helperNum)); - } - } - break; - - case IAT_PVALUE: - { - //------------------------------------------------------ - // Non-virtual direct calls to addresses accessed by - // a single indirection. - // - - // Load the address into a register, load indirect and call through a register - CLANG_FORMAT_COMMENT_ANCHOR; -#if CPU_LOAD_STORE_ARCH - regMaskTP indCallMask = RBM_ALLINT; - -#ifdef FEATURE_READYTORUN_COMPILER - if (call->IsR2RRelativeIndir()) - { - indCallMask &= ~RBM_R2R_INDIRECT_PARAM; - } -#endif // FEATURE_READYTORUN_COMPILER - - // Grab an available register to use for the CALL indirection - indCallReg = regSet.rsGrabReg(indCallMask); - - instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, indCallReg, (ssize_t)addr); - -#ifdef FEATURE_READYTORUN_COMPILER - if (call->IsR2RRelativeIndir()) - { - noway_assert(regSet.rsRegMaskCanGrab() & RBM_R2R_INDIRECT_PARAM); - getEmitter()->emitIns_R_R(INS_mov, EA_PTRSIZE, REG_R2R_INDIRECT_PARAM, indCallReg); - regTracker.rsTrackRegTrash(REG_R2R_INDIRECT_PARAM); - } -#endif // FEATURE_READYTORUN_COMPILER - - getEmitter()->emitIns_R_R_I(INS_ldr, EA_PTRSIZE, indCallReg, indCallReg, 0); - regTracker.rsTrackRegTrash(indCallReg); - - emitCallType = emitter::EC_INDIR_R; - addr = NULL; - -#else - emitCallType = emitter::EC_FUNC_TOKEN_INDIR; - indCallReg = REG_NA; - -#endif // CPU_LOAD_STORE_ARCH - - getEmitter()->emitIns_Call(emitCallType, methHnd, INDEBUG_LDISASM_COMMA(sigInfo) addr, args, - retSize, gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, - gcInfo.gcRegByrefSetCur, ilOffset, - indCallReg, // ireg - REG_NA, 0, 0, // xreg, xmul, disp - false, /* isJump */ - emitter::emitNoGChelper(helperNum)); - } - break; - - case IAT_PPVALUE: - { - //------------------------------------------------------ - // Non-virtual direct calls to addresses accessed by - // a double indirection. - // - // Double-indirection. Load the address into a register - // and call indirectly through the register - - noway_assert(helperNum == CORINFO_HELP_UNDEF); - - // Grab an available register to use for the CALL indirection - indCallReg = regSet.rsGrabReg(RBM_ALLINT); - -#if CPU_LOAD_STORE_ARCH - instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, indCallReg, (ssize_t)addr); - getEmitter()->emitIns_R_R_I(INS_ldr, EA_PTRSIZE, indCallReg, indCallReg, 0); - getEmitter()->emitIns_R_R_I(INS_ldr, EA_PTRSIZE, indCallReg, indCallReg, 0); - regTracker.rsTrackRegTrash(indCallReg); - - emitCallType = emitter::EC_INDIR_R; - -#else - - getEmitter()->emitIns_R_AI(INS_mov, EA_PTR_DSP_RELOC, indCallReg, (ssize_t)addr); - regTracker.rsTrackRegTrash(indCallReg); - - emitCallType = emitter::EC_INDIR_ARD; - -#endif // CPU_LOAD_STORE_ARCH - - getEmitter()->emitIns_Call(emitCallType, methHnd, - INDEBUG_LDISASM_COMMA(sigInfo) NULL, // addr - args, retSize, gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, - gcInfo.gcRegByrefSetCur, ilOffset, - indCallReg, // ireg - REG_NA, 0, 0, // xreg, xmul, disp - false, // isJump - emitter::emitNoGChelper(helperNum)); - } - break; - - default: - noway_assert(!"Bad accessType"); - break; - } - - // tracking of region protected by the monitor in synchronized methods - if ((helperNum != CORINFO_HELP_UNDEF) && (compiler->info.compFlags & CORINFO_FLG_SYNCH)) - { - fPossibleSyncHelperCall = true; - } - } - } - break; - - default: - noway_assert(!"strange call type"); - break; - } - - /*------------------------------------------------------------------------- - * For tailcalls, REG_INTRET contains the address of the target function, - * enregistered args are in the correct registers, and the stack arguments - * have been pushed on the stack. Now call the stub-sliding helper - */ - - if (fTailCall) - { - - if (compiler->info.compCallUnmanaged) - genPInvokeMethodEpilog(); - -#ifdef _TARGET_X86_ - noway_assert(0 <= (ssize_t)args); // caller-pop args not supported for tailcall - - // Push the count of the incoming stack arguments - - unsigned nOldStkArgs = - (unsigned)((compiler->compArgSize - (intRegState.rsCalleeRegArgCount * REGSIZE_BYTES)) / REGSIZE_BYTES); - getEmitter()->emitIns_I(INS_push, EA_4BYTE, nOldStkArgs); - genSinglePush(); // Keep track of ESP for EBP-less frames - args += REGSIZE_BYTES; - - // Push the count of the outgoing stack arguments - - getEmitter()->emitIns_I(INS_push, EA_4BYTE, argSize / REGSIZE_BYTES); - genSinglePush(); // Keep track of ESP for EBP-less frames - args += REGSIZE_BYTES; - - // Push info about the callee-saved registers to be restored - // For now, we always spill all registers if compiler->compTailCallUsed - - DWORD calleeSavedRegInfo = 1 | // always restore EDI,ESI,EBX - (fTailCallTargetIsVSD ? 0x2 : 0x0); // Stub dispatch flag - getEmitter()->emitIns_I(INS_push, EA_4BYTE, calleeSavedRegInfo); - genSinglePush(); // Keep track of ESP for EBP-less frames - args += REGSIZE_BYTES; - - // Push the address of the target function - - getEmitter()->emitIns_R(INS_push, EA_4BYTE, REG_TAILCALL_ADDR); - genSinglePush(); // Keep track of ESP for EBP-less frames - args += REGSIZE_BYTES; - -#else // _TARGET_X86_ - - args = 0; - retSize = EA_UNKNOWN; - -#endif // _TARGET_X86_ - - if (compiler->getNeedsGSSecurityCookie()) - { - genEmitGSCookieCheck(true); - } - - // TailCall helper does not poll for GC. An explicit GC poll - // Should have been placed in when we morphed this into a tail call. - noway_assert(compiler->compCurBB->bbFlags & BBF_GC_SAFE_POINT); - - // Now call the helper - - genEmitHelperCall(CORINFO_HELP_TAILCALL, (int)args, retSize); - } - - /*------------------------------------------------------------------------- - * Done with call. - * Trash registers, pop arguments if needed, etc - */ - - /* Mark the argument registers as free */ - - noway_assert(intRegState.rsCurRegArgNum <= MAX_REG_ARG); - - for (areg = 0; areg < MAX_REG_ARG; areg++) - { - regMaskTP curArgMask = genMapArgNumToRegMask(areg, TYP_INT); - - // Is this one of the used argument registers? - if ((curArgMask & call->gtCallRegUsedMask) == 0) - continue; - -#ifdef _TARGET_ARM_ - if (regSet.rsUsedTree[areg] == NULL) - { - noway_assert(areg % 2 == 1 && - (((areg + 1) >= MAX_REG_ARG) || (regSet.rsUsedTree[areg + 1]->TypeGet() == TYP_STRUCT) || - (genTypeStSz(regSet.rsUsedTree[areg + 1]->TypeGet()) == 2))); - continue; - } -#endif - - regSet.rsMarkRegFree(curArgMask); - - // We keep regSet.rsMaskVars current during codegen, so we have to remove any - // that have been copied into arg regs. - - regSet.RemoveMaskVars(curArgMask); - gcInfo.gcRegGCrefSetCur &= ~(curArgMask); - gcInfo.gcRegByrefSetCur &= ~(curArgMask); - } - -#if !FEATURE_STACK_FP_X87 - //------------------------------------------------------------------------- - // free up the FP args - - for (areg = 0; areg < MAX_FLOAT_REG_ARG; areg++) - { - regNumber argRegNum = genMapRegArgNumToRegNum(areg, TYP_FLOAT); - regMaskTP curArgMask = genMapArgNumToRegMask(areg, TYP_FLOAT); - - // Is this one of the used argument registers? - if ((curArgMask & call->gtCallRegUsedMask) == 0) - continue; - - regSet.rsMaskUsed &= ~curArgMask; - regSet.rsUsedTree[argRegNum] = NULL; - } -#endif // !FEATURE_STACK_FP_X87 - - /* restore the old argument register status */ - - intRegState.rsCurRegArgNum = savCurIntArgReg; - floatRegState.rsCurRegArgNum = savCurFloatArgReg; - - noway_assert(intRegState.rsCurRegArgNum <= MAX_REG_ARG); - - /* Mark all trashed registers as such */ - - if (calleeTrashedRegs) - regTracker.rsTrashRegSet(calleeTrashedRegs); - - regTracker.rsTrashRegsForGCInterruptability(); - -#ifdef DEBUG - - if (!(call->gtFlags & GTF_CALL_POP_ARGS)) - { - if (compiler->verbose) - { - printf("\t\t\t\t\t\t\tEnd call "); - Compiler::printTreeID(call); - printf(" stack %02u [E=%02u] argSize=%u\n", saveStackLvl, getEmitter()->emitCurStackLvl, argSize); - } - noway_assert(stackLvl == getEmitter()->emitCurStackLvl); - } - -#endif - -#if FEATURE_STACK_FP_X87 - /* All float temps must be spilled around function calls */ - if (call->gtType == TYP_FLOAT || call->gtType == TYP_DOUBLE) - { - noway_assert(compCurFPState.m_uStackSize == 1); - } - else - { - noway_assert(compCurFPState.m_uStackSize == 0); - } -#else - if (call->gtType == TYP_FLOAT || call->gtType == TYP_DOUBLE) - { -#ifdef _TARGET_ARM_ - if (call->IsVarargs() || compiler->opts.compUseSoftFP) - { - // Result return for vararg methods is in r0, r1, but our callers would - // expect the return in s0, s1 because of floating type. Do the move now. - if (call->gtType == TYP_FLOAT) - { - inst_RV_RV(INS_vmov_i2f, REG_FLOATRET, REG_INTRET, TYP_FLOAT, EA_4BYTE); - } - else - { - inst_RV_RV_RV(INS_vmov_i2d, REG_FLOATRET, REG_INTRET, REG_NEXT(REG_INTRET), EA_8BYTE); - } - } -#endif - genMarkTreeInReg(call, REG_FLOATRET); - } -#endif - - /* The function will pop all arguments before returning */ - - SetStackLevel(saveStackLvl); - - /* No trashed registers may possibly hold a pointer at this point */ - CLANG_FORMAT_COMMENT_ANCHOR; - -#ifdef DEBUG - - regMaskTP ptrRegs = (gcInfo.gcRegGCrefSetCur | gcInfo.gcRegByrefSetCur) & (calleeTrashedRegs & RBM_ALLINT) & - ~regSet.rsMaskVars & ~vptrMask; - if (ptrRegs) - { - // A reg may be dead already. The assertion is too strong. - LclVarDsc* varDsc; - unsigned varNum; - - // use compiler->compCurLife - for (varNum = 0, varDsc = compiler->lvaTable; varNum < compiler->lvaCount && ptrRegs != 0; varNum++, varDsc++) - { - /* Ignore the variable if it's not tracked, not in a register, or a floating-point type */ - - if (!varDsc->lvTracked) - continue; - if (!varDsc->lvRegister) - continue; - if (varDsc->IsFloatRegType()) - continue; - - /* Get hold of the index and the bitmask for the variable */ - - unsigned varIndex = varDsc->lvVarIndex; - - /* Is this variable live currently? */ - - if (!VarSetOps::IsMember(compiler, compiler->compCurLife, varIndex)) - { - regNumber regNum = varDsc->lvRegNum; - regMaskTP regMask = genRegMask(regNum); - - if (varDsc->lvType == TYP_REF || varDsc->lvType == TYP_BYREF) - ptrRegs &= ~regMask; - } - } - if (ptrRegs) - { - printf("Bad call handling for "); - Compiler::printTreeID(call); - printf("\n"); - noway_assert(!"A callee trashed reg is holding a GC pointer"); - } - } -#endif - -#if defined(_TARGET_X86_) - //------------------------------------------------------------------------- - // Create a label for tracking of region protected by the monitor in synchronized methods. - // This needs to be here, rather than above where fPossibleSyncHelperCall is set, - // so the GC state vars have been updated before creating the label. - - if (fPossibleSyncHelperCall) - { - switch (helperNum) - { - case CORINFO_HELP_MON_ENTER: - case CORINFO_HELP_MON_ENTER_STATIC: - noway_assert(compiler->syncStartEmitCookie == NULL); - compiler->syncStartEmitCookie = - getEmitter()->emitAddLabel(gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur); - noway_assert(compiler->syncStartEmitCookie != NULL); - break; - case CORINFO_HELP_MON_EXIT: - case CORINFO_HELP_MON_EXIT_STATIC: - noway_assert(compiler->syncEndEmitCookie == NULL); - compiler->syncEndEmitCookie = - getEmitter()->emitAddLabel(gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur); - noway_assert(compiler->syncEndEmitCookie != NULL); - break; - default: - break; - } - } -#endif // _TARGET_X86_ - - if (call->gtFlags & GTF_CALL_UNMANAGED) - { - genDefineTempLabel(returnLabel); - -#ifdef _TARGET_X86_ - if (getInlinePInvokeCheckEnabled()) - { - noway_assert(compiler->lvaInlinedPInvokeFrameVar != BAD_VAR_NUM); - BasicBlock* esp_check; - - CORINFO_EE_INFO* pInfo = compiler->eeGetEEInfo(); - /* mov ecx, dword ptr [frame.callSiteTracker] */ - - getEmitter()->emitIns_R_S(INS_mov, EA_4BYTE, REG_ARG_0, compiler->lvaInlinedPInvokeFrameVar, - pInfo->inlinedCallFrameInfo.offsetOfCallSiteSP); - regTracker.rsTrackRegTrash(REG_ARG_0); - - /* Generate the conditional jump */ - - if (!(call->gtFlags & GTF_CALL_POP_ARGS)) - { - if (argSize) - { - getEmitter()->emitIns_R_I(INS_add, EA_PTRSIZE, REG_ARG_0, argSize); - } - } - /* cmp ecx, esp */ - - getEmitter()->emitIns_R_R(INS_cmp, EA_PTRSIZE, REG_ARG_0, REG_SPBASE); - - esp_check = genCreateTempLabel(); - - emitJumpKind jmpEqual = genJumpKindForOper(GT_EQ, CK_SIGNED); - inst_JMP(jmpEqual, esp_check); - - getEmitter()->emitIns(INS_BREAKPOINT); - - /* genCondJump() closes the current emitter block */ - - genDefineTempLabel(esp_check); - } -#endif - } - - /* Are we supposed to pop the arguments? */ - CLANG_FORMAT_COMMENT_ANCHOR; - -#if defined(_TARGET_X86_) - if (call->gtFlags & GTF_CALL_UNMANAGED) - { - if (compiler->opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PINVOKE_RESTORE_ESP) || - compiler->compStressCompile(Compiler::STRESS_PINVOKE_RESTORE_ESP, 50)) - { - // P/Invoke signature mismatch resilience - restore ESP to pre-call value. We would ideally - // take care of the cdecl argument popping here as well but the stack depth tracking logic - // makes this very hard, i.e. it needs to "see" the actual pop. - - CORINFO_EE_INFO* pInfo = compiler->eeGetEEInfo(); - - if (argSize == 0 || (call->gtFlags & GTF_CALL_POP_ARGS)) - { - /* mov esp, dword ptr [frame.callSiteTracker] */ - getEmitter()->emitIns_R_S(ins_Load(TYP_I_IMPL), EA_PTRSIZE, REG_SPBASE, - compiler->lvaInlinedPInvokeFrameVar, - pInfo->inlinedCallFrameInfo.offsetOfCallSiteSP); - } - else - { - /* mov ecx, dword ptr [frame.callSiteTracker] */ - getEmitter()->emitIns_R_S(ins_Load(TYP_I_IMPL), EA_PTRSIZE, REG_ARG_0, - compiler->lvaInlinedPInvokeFrameVar, - pInfo->inlinedCallFrameInfo.offsetOfCallSiteSP); - regTracker.rsTrackRegTrash(REG_ARG_0); - - /* lea esp, [ecx + argSize] */ - getEmitter()->emitIns_R_AR(INS_lea, EA_PTRSIZE, REG_SPBASE, REG_ARG_0, (int)argSize); - } - } - } -#endif // _TARGET_X86_ - - if (call->gtFlags & GTF_CALL_POP_ARGS) - { - noway_assert(args == (size_t) - (int)argSize); - - if (argSize) - { - genAdjustSP(argSize); - } - } - - if (pseudoStackLvl) - { - noway_assert(call->gtType == TYP_VOID); - - /* Generate NOP */ - - instGen(INS_nop); - } - - /* What does the function return? */ - - retVal = RBM_NONE; - - switch (call->gtType) - { - case TYP_REF: - case TYP_BYREF: - gcInfo.gcMarkRegPtrVal(REG_INTRET, call->TypeGet()); - - __fallthrough; - - case TYP_INT: -#if !CPU_HAS_FP_SUPPORT - case TYP_FLOAT: -#endif - retVal = RBM_INTRET; - break; - -#ifdef _TARGET_ARM_ - case TYP_STRUCT: - { - assert(call->gtRetClsHnd != NULL); - assert(compiler->IsHfa(call->gtRetClsHnd)); - int retSlots = compiler->GetHfaCount(call->gtRetClsHnd); - assert(retSlots > 0 && retSlots <= MAX_HFA_RET_SLOTS); - assert(MAX_HFA_RET_SLOTS < sizeof(int) * 8); - retVal = ((1 << retSlots) - 1) << REG_FLOATRET; - } - break; -#endif - - case TYP_LONG: -#if !CPU_HAS_FP_SUPPORT - case TYP_DOUBLE: -#endif - retVal = RBM_LNGRET; - break; - -#if CPU_HAS_FP_SUPPORT - case TYP_FLOAT: - case TYP_DOUBLE: - - break; -#endif - - case TYP_VOID: - break; - - default: - noway_assert(!"unexpected/unhandled fn return type"); - } - - // We now have to generate the "call epilog" (if it was a call to unmanaged code). - /* if it is a call to unmanaged code, frameListRoot must be set */ - - noway_assert((call->gtFlags & GTF_CALL_UNMANAGED) == 0 || frameListRoot); - - if (frameListRoot) - genPInvokeCallEpilog(frameListRoot, retVal); - - if (frameListRoot && (call->gtCallMoreFlags & GTF_CALL_M_FRAME_VAR_DEATH)) - { - if (frameListRoot->lvRegister) - { - bool isBorn = false; - bool isDying = true; - genUpdateRegLife(frameListRoot, isBorn, isDying DEBUGARG(call)); - } - } - -#ifdef DEBUG - if (compiler->opts.compStackCheckOnCall -#if defined(USE_TRANSITION_THUNKS) || defined(USE_DYNAMIC_STACK_ALIGN) - // check the stack as frequently as possible - && !call->IsHelperCall() -#else - && call->gtCallType == CT_USER_FUNC -#endif - ) - { - noway_assert(compiler->lvaCallEspCheck != 0xCCCCCCCC && - compiler->lvaTable[compiler->lvaCallEspCheck].lvDoNotEnregister && - compiler->lvaTable[compiler->lvaCallEspCheck].lvOnFrame); - if (argSize > 0) - { - getEmitter()->emitIns_R_R(INS_mov, EA_4BYTE, REG_ARG_0, REG_SPBASE); - getEmitter()->emitIns_R_I(INS_sub, EA_4BYTE, REG_ARG_0, argSize); - getEmitter()->emitIns_S_R(INS_cmp, EA_4BYTE, REG_ARG_0, compiler->lvaCallEspCheck, 0); - regTracker.rsTrackRegTrash(REG_ARG_0); - } - else - getEmitter()->emitIns_S_R(INS_cmp, EA_4BYTE, REG_SPBASE, compiler->lvaCallEspCheck, 0); - - BasicBlock* esp_check = genCreateTempLabel(); - emitJumpKind jmpEqual = genJumpKindForOper(GT_EQ, CK_SIGNED); - inst_JMP(jmpEqual, esp_check); - getEmitter()->emitIns(INS_BREAKPOINT); - genDefineTempLabel(esp_check); - } -#endif // DEBUG - -#if FEATURE_STACK_FP_X87 - UnspillRegVarsStackFp(); -#endif // FEATURE_STACK_FP_X87 - - if (call->gtType == TYP_FLOAT || call->gtType == TYP_DOUBLE) - { - // Restore return node if necessary - if (call->gtFlags & GTF_SPILLED) - { - UnspillFloat(call); - } - -#if FEATURE_STACK_FP_X87 - // Mark as free - regSet.SetUsedRegFloat(call, false); -#endif - } - -#if FEATURE_STACK_FP_X87 -#ifdef DEBUG - if (compiler->verbose) - { - JitDumpFPState(); - } -#endif -#endif - - return retVal; -} -#ifdef _PREFAST_ -#pragma warning(pop) -#endif - -/***************************************************************************** - * - * Create and record GC Info for the function. - */ -#ifdef JIT32_GCENCODER -void* -#else -void -#endif -CodeGen::genCreateAndStoreGCInfo(unsigned codeSize, unsigned prologSize, unsigned epilogSize DEBUGARG(void* codePtr)) -{ -#ifdef JIT32_GCENCODER - return genCreateAndStoreGCInfoJIT32(codeSize, prologSize, epilogSize DEBUGARG(codePtr)); -#else - genCreateAndStoreGCInfoX64(codeSize, prologSize DEBUGARG(codePtr)); -#endif -} - -#ifdef JIT32_GCENCODER -void* CodeGen::genCreateAndStoreGCInfoJIT32(unsigned codeSize, - unsigned prologSize, - unsigned epilogSize DEBUGARG(void* codePtr)) -{ - BYTE headerBuf[64]; - InfoHdr header; - - int s_cached; -#ifdef DEBUG - size_t headerSize = -#endif - compiler->compInfoBlkSize = - gcInfo.gcInfoBlockHdrSave(headerBuf, 0, codeSize, prologSize, epilogSize, &header, &s_cached); - - size_t argTabOffset = 0; - size_t ptrMapSize = gcInfo.gcPtrTableSize(header, codeSize, &argTabOffset); - -#if DISPLAY_SIZES - - if (genInterruptible) - { - gcHeaderISize += compiler->compInfoBlkSize; - gcPtrMapISize += ptrMapSize; - } - else - { - gcHeaderNSize += compiler->compInfoBlkSize; - gcPtrMapNSize += ptrMapSize; - } - -#endif // DISPLAY_SIZES - - compiler->compInfoBlkSize += ptrMapSize; - - /* Allocate the info block for the method */ - - compiler->compInfoBlkAddr = (BYTE*)compiler->info.compCompHnd->allocGCInfo(compiler->compInfoBlkSize); - -#if 0 // VERBOSE_SIZES - // TODO-Review: 'dataSize', below, is not defined - -// if (compiler->compInfoBlkSize > codeSize && compiler->compInfoBlkSize > 100) - { - printf("[%7u VM, %7u+%7u/%7u x86 %03u/%03u%%] %s.%s\n", - compiler->info.compILCodeSize, - compiler->compInfoBlkSize, - codeSize + dataSize, - codeSize + dataSize - prologSize - epilogSize, - 100 * (codeSize + dataSize) / compiler->info.compILCodeSize, - 100 * (codeSize + dataSize + compiler->compInfoBlkSize) / compiler->info.compILCodeSize, - compiler->info.compClassName, - compiler->info.compMethodName); - } - -#endif - - /* Fill in the info block and return it to the caller */ - - void* infoPtr = compiler->compInfoBlkAddr; - - /* Create the method info block: header followed by GC tracking tables */ - - compiler->compInfoBlkAddr += - gcInfo.gcInfoBlockHdrSave(compiler->compInfoBlkAddr, -1, codeSize, prologSize, epilogSize, &header, &s_cached); - - assert(compiler->compInfoBlkAddr == (BYTE*)infoPtr + headerSize); - compiler->compInfoBlkAddr = gcInfo.gcPtrTableSave(compiler->compInfoBlkAddr, header, codeSize, &argTabOffset); - assert(compiler->compInfoBlkAddr == (BYTE*)infoPtr + headerSize + ptrMapSize); - -#ifdef DEBUG - - if (0) - { - BYTE* temp = (BYTE*)infoPtr; - unsigned size = compiler->compInfoBlkAddr - temp; - BYTE* ptab = temp + headerSize; - - noway_assert(size == headerSize + ptrMapSize); - - printf("Method info block - header [%u bytes]:", headerSize); - - for (unsigned i = 0; i < size; i++) - { - if (temp == ptab) - { - printf("\nMethod info block - ptrtab [%u bytes]:", ptrMapSize); - printf("\n %04X: %*c", i & ~0xF, 3 * (i & 0xF), ' '); - } - else - { - if (!(i % 16)) - printf("\n %04X: ", i); - } - - printf("%02X ", *temp++); - } - - printf("\n"); - } - -#endif // DEBUG - -#if DUMP_GC_TABLES - - if (compiler->opts.dspGCtbls) - { - const BYTE* base = (BYTE*)infoPtr; - unsigned size; - unsigned methodSize; - InfoHdr dumpHeader; - - printf("GC Info for method %s\n", compiler->info.compFullName); - printf("GC info size = %3u\n", compiler->compInfoBlkSize); - - size = gcInfo.gcInfoBlockHdrDump(base, &dumpHeader, &methodSize); - // printf("size of header encoding is %3u\n", size); - printf("\n"); - - if (compiler->opts.dspGCtbls) - { - base += size; - size = gcInfo.gcDumpPtrTable(base, dumpHeader, methodSize); - // printf("size of pointer table is %3u\n", size); - printf("\n"); - noway_assert(compiler->compInfoBlkAddr == (base + size)); - } - } - -#ifdef DEBUG - if (jitOpts.testMask & 128) - { - for (unsigned offs = 0; offs < codeSize; offs++) - { - gcInfo.gcFindPtrsInFrame(infoPtr, codePtr, offs); - } - } -#endif // DEBUG -#endif // DUMP_GC_TABLES - - /* Make sure we ended up generating the expected number of bytes */ - - noway_assert(compiler->compInfoBlkAddr == (BYTE*)infoPtr + compiler->compInfoBlkSize); - - return infoPtr; -} - -#else // JIT32_GCENCODER - -void CodeGen::genCreateAndStoreGCInfoX64(unsigned codeSize, unsigned prologSize DEBUGARG(void* codePtr)) -{ - IAllocator* allowZeroAlloc = new (compiler, CMK_GC) CompIAllocator(compiler->getAllocatorGC()); - GcInfoEncoder* gcInfoEncoder = new (compiler, CMK_GC) - GcInfoEncoder(compiler->info.compCompHnd, compiler->info.compMethodInfo, allowZeroAlloc, NOMEM); - assert(gcInfoEncoder); - - // Follow the code pattern of the x86 gc info encoder (genCreateAndStoreGCInfoJIT32). - gcInfo.gcInfoBlockHdrSave(gcInfoEncoder, codeSize, prologSize); - - // We keep the call count for the second call to gcMakeRegPtrTable() below. - unsigned callCnt = 0; - // First we figure out the encoder ID's for the stack slots and registers. - gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_ASSIGN_SLOTS, &callCnt); - // Now we've requested all the slots we'll need; "finalize" these (make more compact data structures for them). - gcInfoEncoder->FinalizeSlotIds(); - // Now we can actually use those slot ID's to declare live ranges. - gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_DO_WORK, &callCnt); - - gcInfoEncoder->Build(); - - // GC Encoder automatically puts the GC info in the right spot using ICorJitInfo::allocGCInfo(size_t) - // let's save the values anyway for debugging purposes - compiler->compInfoBlkAddr = gcInfoEncoder->Emit(); - compiler->compInfoBlkSize = 0; // not exposed by the GCEncoder interface -} -#endif - -/***************************************************************************** - * For CEE_LOCALLOC - */ - -regNumber CodeGen::genLclHeap(GenTree* size) -{ - noway_assert((genActualType(size->gtType) == TYP_INT) || (genActualType(size->gtType) == TYP_I_IMPL)); - - // regCnt is a register used to hold both - // the amount to stack alloc (either in bytes or pointer sized words) - // and the final stack alloc address to return as the result - // - regNumber regCnt = DUMMY_INIT(REG_CORRUPT); - var_types type = genActualType(size->gtType); - emitAttr easz = emitTypeSize(type); - -#ifdef DEBUG - // Verify ESP - if (compiler->opts.compStackCheckOnRet) - { - noway_assert(compiler->lvaReturnEspCheck != 0xCCCCCCCC && - compiler->lvaTable[compiler->lvaReturnEspCheck].lvDoNotEnregister && - compiler->lvaTable[compiler->lvaReturnEspCheck].lvOnFrame); - getEmitter()->emitIns_S_R(INS_cmp, EA_PTRSIZE, REG_SPBASE, compiler->lvaReturnEspCheck, 0); - - BasicBlock* esp_check = genCreateTempLabel(); - emitJumpKind jmpEqual = genJumpKindForOper(GT_EQ, CK_SIGNED); - inst_JMP(jmpEqual, esp_check); - getEmitter()->emitIns(INS_BREAKPOINT); - genDefineTempLabel(esp_check); - } -#endif - - noway_assert(isFramePointerUsed()); - noway_assert(genStackLevel == 0); // Can't have anything on the stack - - BasicBlock* endLabel = NULL; -#if FEATURE_FIXED_OUT_ARGS - bool stackAdjusted = false; -#endif - - if (size->IsCnsIntOrI()) - { -#if FEATURE_FIXED_OUT_ARGS - // If we have an outgoing arg area then we must adjust the SP - // essentially popping off the outgoing arg area, - // We will restore it right before we return from this method - // - if (compiler->lvaOutgoingArgSpaceSize > 0) - { - assert((compiler->lvaOutgoingArgSpaceSize % STACK_ALIGN) == - 0); // This must be true for the stack to remain aligned - inst_RV_IV(INS_add, REG_SPBASE, compiler->lvaOutgoingArgSpaceSize, EA_PTRSIZE); - stackAdjusted = true; - } -#endif - size_t amount = size->gtIntCon.gtIconVal; - - // Convert amount to be properly STACK_ALIGN and count of DWORD_PTRs - amount += (STACK_ALIGN - 1); - amount &= ~(STACK_ALIGN - 1); - amount >>= STACK_ALIGN_SHIFT; // amount is number of pointer-sized words to locAlloc - size->gtIntCon.gtIconVal = amount; // update the GT_CNS value in the node - - /* If amount is zero then return null in RegCnt */ - if (amount == 0) - { - regCnt = regSet.rsGrabReg(RBM_ALLINT); - instGen_Set_Reg_To_Zero(EA_PTRSIZE, regCnt); - goto DONE; - } - - /* For small allocations we will generate up to six push 0 inline */ - if (amount <= 6) - { - regCnt = regSet.rsGrabReg(RBM_ALLINT); -#if CPU_LOAD_STORE_ARCH - regNumber regZero = regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(regCnt)); - // Set 'regZero' to zero - instGen_Set_Reg_To_Zero(EA_PTRSIZE, regZero); -#endif - - while (amount != 0) - { -#if CPU_LOAD_STORE_ARCH - inst_IV(INS_push, (unsigned)genRegMask(regZero)); -#else - inst_IV(INS_push_hide, 0); // push_hide means don't track the stack -#endif - amount--; - } - - regTracker.rsTrackRegTrash(regCnt); - // --- move regCnt, ESP - inst_RV_RV(INS_mov, regCnt, REG_SPBASE, TYP_I_IMPL); - goto DONE; - } - else - { - if (!compiler->info.compInitMem) - { - // Re-bias amount to be number of bytes to adjust the SP - amount <<= STACK_ALIGN_SHIFT; - size->gtIntCon.gtIconVal = amount; // update the GT_CNS value in the node - if (amount < compiler->eeGetPageSize()) // must be < not <= - { - // Since the size is a page or less, simply adjust ESP - - // ESP might already be in the guard page, must touch it BEFORE - // the alloc, not after. - regCnt = regSet.rsGrabReg(RBM_ALLINT); - inst_RV_RV(INS_mov, regCnt, REG_SPBASE, TYP_I_IMPL); -#if CPU_LOAD_STORE_ARCH - regNumber regTmp = regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(regCnt)); - getEmitter()->emitIns_R_R_I(INS_ldr, EA_PTRSIZE, regTmp, REG_SPBASE, 0); - regTracker.rsTrackRegTrash(regTmp); -#else - getEmitter()->emitIns_AR_R(INS_TEST, EA_4BYTE, REG_SPBASE, REG_SPBASE, 0); -#endif - inst_RV_IV(INS_sub, regCnt, amount, EA_PTRSIZE); - inst_RV_RV(INS_mov, REG_SPBASE, regCnt, TYP_I_IMPL); - regTracker.rsTrackRegTrash(regCnt); - goto DONE; - } - } - } - } - - // Compute the size of the block to allocate - genCompIntoFreeReg(size, 0, RegSet::KEEP_REG); - noway_assert(size->InReg()); - regCnt = size->gtRegNum; - -#if FEATURE_FIXED_OUT_ARGS - // If we have an outgoing arg area then we must adjust the SP - // essentially popping off the outgoing arg area, - // We will restore it right before we return from this method - // - if ((compiler->lvaOutgoingArgSpaceSize > 0) && !stackAdjusted) - { - assert((compiler->lvaOutgoingArgSpaceSize % STACK_ALIGN) == - 0); // This must be true for the stack to remain aligned - inst_RV_IV(INS_add, REG_SPBASE, compiler->lvaOutgoingArgSpaceSize, EA_PTRSIZE); - stackAdjusted = true; - } -#endif - - // Perform alignment if we don't have a GT_CNS size - // - if (!size->IsCnsIntOrI()) - { - endLabel = genCreateTempLabel(); - - // If 0 we bail out - instGen_Compare_Reg_To_Zero(easz, regCnt); // set flags - emitJumpKind jmpEqual = genJumpKindForOper(GT_EQ, CK_SIGNED); - inst_JMP(jmpEqual, endLabel); - - // Align to STACK_ALIGN - inst_RV_IV(INS_add, regCnt, (STACK_ALIGN - 1), emitActualTypeSize(type)); - - if (compiler->info.compInitMem) - { -#if ((STACK_ALIGN >> STACK_ALIGN_SHIFT) > 1) - // regCnt will be the number of pointer-sized words to locAlloc - // If the shift right won't do the 'and' do it here - inst_RV_IV(INS_AND, regCnt, ~(STACK_ALIGN - 1), emitActualTypeSize(type)); -#endif - // --- shr regCnt, 2 --- - inst_RV_SH(INS_SHIFT_RIGHT_LOGICAL, EA_PTRSIZE, regCnt, STACK_ALIGN_SHIFT); - } - else - { - // regCnt will be the total number of bytes to locAlloc - - inst_RV_IV(INS_AND, regCnt, ~(STACK_ALIGN - 1), emitActualTypeSize(type)); - } - } - - BasicBlock* loop; - loop = genCreateTempLabel(); - - if (compiler->info.compInitMem) - { - // At this point 'regCnt' is set to the number of pointer-sized words to locAlloc - - /* Since we have to zero out the allocated memory AND ensure that - ESP is always valid by tickling the pages, we will just push 0's - on the stack */ - CLANG_FORMAT_COMMENT_ANCHOR; - -#if defined(_TARGET_ARM_) - regNumber regZero1 = regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(regCnt)); - regNumber regZero2 = regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(regCnt) & ~genRegMask(regZero1)); - // Set 'regZero1' and 'regZero2' to zero - instGen_Set_Reg_To_Zero(EA_PTRSIZE, regZero1); - instGen_Set_Reg_To_Zero(EA_PTRSIZE, regZero2); -#endif - - // Loop: - genDefineTempLabel(loop); - -#if defined(_TARGET_X86_) - - inst_IV(INS_push_hide, 0); // --- push 0 - // Are we done? - inst_RV(INS_dec, regCnt, type); - -#elif defined(_TARGET_ARM_) - - inst_IV(INS_push, (unsigned)(genRegMask(regZero1) | genRegMask(regZero2))); - // Are we done? - inst_RV_IV(INS_sub, regCnt, 2, emitActualTypeSize(type), INS_FLAGS_SET); - -#else - assert(!"Codegen missing"); -#endif // TARGETS - - emitJumpKind jmpNotEqual = genJumpKindForOper(GT_NE, CK_SIGNED); - inst_JMP(jmpNotEqual, loop); - - // Move the final value of ESP into regCnt - inst_RV_RV(INS_mov, regCnt, REG_SPBASE); - regTracker.rsTrackRegTrash(regCnt); - } - else - { - // At this point 'regCnt' is set to the total number of bytes to locAlloc - - /* We don't need to zero out the allocated memory. However, we do have - to tickle the pages to ensure that ESP is always valid and is - in sync with the "stack guard page". Note that in the worst - case ESP is on the last byte of the guard page. Thus you must - touch ESP+0 first not ESP+x01000. - - Another subtlety is that you don't want ESP to be exactly on the - boundary of the guard page because PUSH is predecrement, thus - call setup would not touch the guard page but just beyond it */ - - /* Note that we go through a few hoops so that ESP never points to - illegal pages at any time during the ticking process - - neg REG - add REG, ESP // reg now holds ultimate ESP - jb loop // result is smaller than orignial ESP (no wrap around) - xor REG, REG, // Overflow, pick lowest possible number - loop: - test ESP, [ESP+0] // X86 - tickle the page - ldr REGH,[ESP+0] // ARM - tickle the page - mov REGH, ESP - sub REGH, GetOsPageSize() - mov ESP, REGH - cmp ESP, REG - jae loop - - mov ESP, REG - end: - */ - CLANG_FORMAT_COMMENT_ANCHOR; - -#ifdef _TARGET_ARM_ - - inst_RV_RV_RV(INS_sub, regCnt, REG_SPBASE, regCnt, EA_4BYTE, INS_FLAGS_SET); - inst_JMP(EJ_hs, loop); -#else - inst_RV(INS_NEG, regCnt, TYP_I_IMPL); - inst_RV_RV(INS_add, regCnt, REG_SPBASE, TYP_I_IMPL); - inst_JMP(EJ_jb, loop); -#endif - regTracker.rsTrackRegTrash(regCnt); - - instGen_Set_Reg_To_Zero(EA_PTRSIZE, regCnt); - - genDefineTempLabel(loop); - - // This is a workaround to avoid the emitter trying to track the - // decrement of the ESP - we do the subtraction in another reg - // instead of adjusting ESP directly. - - regNumber regTemp = regSet.rsPickReg(); - - // Tickle the decremented value, and move back to ESP, - // note that it has to be done BEFORE the update of ESP since - // ESP might already be on the guard page. It is OK to leave - // the final value of ESP on the guard page - CLANG_FORMAT_COMMENT_ANCHOR; - -#if CPU_LOAD_STORE_ARCH - getEmitter()->emitIns_R_R_I(INS_ldr, EA_4BYTE, regTemp, REG_SPBASE, 0); -#else - getEmitter()->emitIns_AR_R(INS_TEST, EA_4BYTE, REG_SPBASE, REG_SPBASE, 0); -#endif - - inst_RV_RV(INS_mov, regTemp, REG_SPBASE, TYP_I_IMPL); - regTracker.rsTrackRegTrash(regTemp); - - inst_RV_IV(INS_sub, regTemp, compiler->eeGetPageSize(), EA_PTRSIZE); - inst_RV_RV(INS_mov, REG_SPBASE, regTemp, TYP_I_IMPL); - - genRecoverReg(size, RBM_ALLINT, - RegSet::KEEP_REG); // not purely the 'size' tree anymore; though it is derived from 'size' - noway_assert(size->InReg()); - regCnt = size->gtRegNum; - inst_RV_RV(INS_cmp, REG_SPBASE, regCnt, TYP_I_IMPL); - emitJumpKind jmpGEU = genJumpKindForOper(GT_GE, CK_UNSIGNED); - inst_JMP(jmpGEU, loop); - - // Move the final value to ESP - inst_RV_RV(INS_mov, REG_SPBASE, regCnt); - } - regSet.rsMarkRegFree(genRegMask(regCnt)); - -DONE: - - noway_assert(regCnt != DUMMY_INIT(REG_CORRUPT)); - - if (endLabel != NULL) - genDefineTempLabel(endLabel); - -#if FEATURE_FIXED_OUT_ARGS - // If we have an outgoing arg area then we must readjust the SP - // - if (stackAdjusted) - { - assert(compiler->lvaOutgoingArgSpaceSize > 0); - assert((compiler->lvaOutgoingArgSpaceSize % STACK_ALIGN) == - 0); // This must be true for the stack to remain aligned - inst_RV_IV(INS_sub, REG_SPBASE, compiler->lvaOutgoingArgSpaceSize, EA_PTRSIZE); - } -#endif - - /* Write the lvaLocAllocSPvar stack frame slot */ - if (compiler->lvaLocAllocSPvar != BAD_VAR_NUM) - { - getEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, REG_SPBASE, compiler->lvaLocAllocSPvar, 0); - } - -#if STACK_PROBES - // Don't think it is worth it the codegen complexity to embed this - // when it's possible in each of the customized allocas. - if (compiler->opts.compNeedStackProbes) - { - genGenerateStackProbe(); - } -#endif - -#ifdef DEBUG - // Update new ESP - if (compiler->opts.compStackCheckOnRet) - { - noway_assert(compiler->lvaReturnEspCheck != 0xCCCCCCCC && - compiler->lvaTable[compiler->lvaReturnEspCheck].lvDoNotEnregister && - compiler->lvaTable[compiler->lvaReturnEspCheck].lvOnFrame); - getEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, REG_SPBASE, compiler->lvaReturnEspCheck, 0); - } -#endif - - return regCnt; -} - -/***************************************************************************** - * - * Return non-zero if the given register is free after the given tree is - * evaluated (i.e. the register is either not used at all, or it holds a - * register variable which is not live after the given node). - * This is only called by genCreateAddrMode, when tree is a GT_ADD, with one - * constant operand, and one that's in a register. Thus, the only thing we - * need to determine is whether the register holding op1 is dead. - */ -bool CodeGen::genRegTrashable(regNumber reg, GenTree* tree) -{ - regMaskTP vars; - regMaskTP mask = genRegMask(reg); - - if (regSet.rsMaskUsed & mask) - return false; - - assert(tree->gtOper == GT_ADD); - GenTree* regValTree = tree->gtOp.gtOp1; - if (!tree->gtOp.gtOp2->IsCnsIntOrI()) - { - regValTree = tree->gtOp.gtOp2; - assert(tree->gtOp.gtOp1->IsCnsIntOrI()); - } - assert(regValTree->InReg()); - - /* At this point, the only way that the register will remain live - * is if it is itself a register variable that isn't dying. - */ - assert(regValTree->gtRegNum == reg); - if (regValTree->IsRegVar() && !regValTree->IsRegVarDeath()) - return false; - else - return true; -} - -/*****************************************************************************/ -// -// This method calculates the USE and DEF values for a statement. -// It also calls fgSetRngChkTarget for the statement. -// -// We refactor out this code from fgPerBlockLocalVarLiveness -// and add QMARK logics to it. -// -// NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE -// -// The usage of this method is very limited. -// We should only call it for the first node in the statement or -// for the node after the GTF_RELOP_QMARK node. -// -// NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE - -/* - Since a GT_QMARK tree can take two paths (i.e. the thenTree Path or the elseTree path), - when we calculate its fgCurDefSet and fgCurUseSet, we need to combine the results - from both trees. - - Note that the GT_QMARK trees are threaded as shown below with nodes 1 to 11 - linked by gtNext. - - The algorithm we use is: - (1) We walk these nodes according the the evaluation order (i.e. from node 1 to node 11). - (2) When we see the GTF_RELOP_QMARK node, we know we are about to split the path. - We cache copies of current fgCurDefSet and fgCurUseSet. - (The fact that it is recursively calling itself is for nested QMARK case, - where we need to remember multiple copies of fgCurDefSet and fgCurUseSet.) - (3) We walk the thenTree. - (4) When we see GT_COLON node, we know that we just finished the thenTree. - We then make a copy of the current fgCurDefSet and fgCurUseSet, - restore them to the ones before the thenTree, and then continue walking - the elseTree. - (5) When we see the GT_QMARK node, we know we just finished the elseTree. - So we combine the results from the thenTree and elseTree and then return. - - - +--------------------+ - | GT_QMARK 11| - +----------+---------+ - | - * - / \ - / \ - / \ - +---------------------+ +--------------------+ - | GT_ 3 | | GT_COLON 7 | - | w/ GTF_RELOP_QMARK | | w/ GTF_COLON_COND | - +----------+----------+ +---------+----------+ - | | - * * - / \ / \ - / \ / \ - / \ / \ - 2 1 thenTree 6 elseTree 10 - x | | - / * * - +----------------+ / / \ / \ - |prevExpr->gtNext+------/ / \ / \ - +----------------+ / \ / \ - 5 4 9 8 - - -*/ - -GenTree* Compiler::fgLegacyPerStatementLocalVarLiveness(GenTree* startNode, // The node to start walking with. - GenTree* relopNode) // The node before the startNode. - // (It should either be NULL or - // a GTF_RELOP_QMARK node.) -{ - GenTree* tree; - - VARSET_TP defSet_BeforeSplit(VarSetOps::MakeCopy(this, fgCurDefSet)); // Store the current fgCurDefSet and - // fgCurUseSet so - VARSET_TP useSet_BeforeSplit(VarSetOps::MakeCopy(this, fgCurUseSet)); // we can restore then before entering the - // elseTree. - - MemoryKindSet memoryUse_BeforeSplit = fgCurMemoryUse; - MemoryKindSet memoryDef_BeforeSplit = fgCurMemoryDef; - MemoryKindSet memoryHavoc_BeforeSplit = fgCurMemoryHavoc; - - VARSET_TP defSet_AfterThenTree(VarSetOps::MakeEmpty(this)); // These two variables will store - // the USE and DEF sets after - VARSET_TP useSet_AfterThenTree(VarSetOps::MakeEmpty(this)); // evaluating the thenTree. - - MemoryKindSet memoryUse_AfterThenTree = fgCurMemoryUse; - MemoryKindSet memoryDef_AfterThenTree = fgCurMemoryDef; - MemoryKindSet memoryHavoc_AfterThenTree = fgCurMemoryHavoc; - - // relopNode is either NULL or a GTF_RELOP_QMARK node. - assert(!relopNode || (relopNode->OperKind() & GTK_RELOP) && (relopNode->gtFlags & GTF_RELOP_QMARK)); - - // If relopNode is NULL, then the startNode must be the 1st node of the statement. - // If relopNode is non-NULL, then the startNode must be the node right after the GTF_RELOP_QMARK node. - assert((!relopNode && startNode == compCurStmt->gtStmt.gtStmtList) || - (relopNode && startNode == relopNode->gtNext)); - - for (tree = startNode; tree; tree = tree->gtNext) - { - switch (tree->gtOper) - { - - case GT_QMARK: - - // This must be a GT_QMARK node whose GTF_RELOP_QMARK node is recursively calling us. - noway_assert(relopNode && tree->gtOp.gtOp1 == relopNode); - - // By the time we see a GT_QMARK, we must have finished processing the elseTree. - // So it's the time to combine the results - // from the the thenTree and the elseTree, and then return. - - VarSetOps::IntersectionD(this, fgCurDefSet, defSet_AfterThenTree); - VarSetOps::UnionD(this, fgCurUseSet, useSet_AfterThenTree); - - fgCurMemoryDef = fgCurMemoryDef & memoryDef_AfterThenTree; - fgCurMemoryHavoc = fgCurMemoryHavoc & memoryHavoc_AfterThenTree; - fgCurMemoryUse = fgCurMemoryUse | memoryUse_AfterThenTree; - - // Return the GT_QMARK node itself so the caller can continue from there. - // NOTE: the caller will get to the next node by doing the "tree = tree->gtNext" - // in the "for" statement. - goto _return; - - case GT_COLON: - // By the time we see GT_COLON, we must have just walked the thenTree. - // So we need to do two things here. - // (1) Save the current fgCurDefSet and fgCurUseSet so that later we can combine them - // with the result from the elseTree. - // (2) Restore fgCurDefSet and fgCurUseSet to the points before the thenTree is walked. - // and then continue walking the elseTree. - VarSetOps::Assign(this, defSet_AfterThenTree, fgCurDefSet); - VarSetOps::Assign(this, useSet_AfterThenTree, fgCurUseSet); - - memoryDef_AfterThenTree = fgCurMemoryDef; - memoryHavoc_AfterThenTree = fgCurMemoryHavoc; - memoryUse_AfterThenTree = fgCurMemoryUse; - - VarSetOps::Assign(this, fgCurDefSet, defSet_BeforeSplit); - VarSetOps::Assign(this, fgCurUseSet, useSet_BeforeSplit); - - fgCurMemoryDef = memoryDef_BeforeSplit; - fgCurMemoryHavoc = memoryHavoc_BeforeSplit; - fgCurMemoryUse = memoryUse_BeforeSplit; - - break; - - case GT_LCL_VAR: - case GT_LCL_FLD: - case GT_LCL_VAR_ADDR: - case GT_LCL_FLD_ADDR: - case GT_STORE_LCL_VAR: - case GT_STORE_LCL_FLD: - fgMarkUseDef(tree->AsLclVarCommon()); - break; - - case GT_CLS_VAR: - // For Volatile indirection, first mutate GcHeap/ByrefExposed - // see comments in ValueNum.cpp (under case GT_CLS_VAR) - // This models Volatile reads as def-then-use of the heap. - // and allows for a CSE of a subsequent non-volatile read - if ((tree->gtFlags & GTF_FLD_VOLATILE) != 0) - { - // For any Volatile indirection, we must handle it as a - // definition of GcHeap/ByrefExposed - fgCurMemoryDef |= memoryKindSet(GcHeap, ByrefExposed); - } - // If the GT_CLS_VAR is the lhs of an assignment, we'll handle it as a heap def, when we get to - // assignment. - // Otherwise, we treat it as a use here. - if ((tree->gtFlags & GTF_CLS_VAR_ASG_LHS) == 0) - { - fgCurMemoryUse |= memoryKindSet(GcHeap, ByrefExposed); - } - break; - - case GT_IND: - // For Volatile indirection, first mutate GcHeap/ByrefExposed - // see comments in ValueNum.cpp (under case GT_CLS_VAR) - // This models Volatile reads as def-then-use of the heap. - // and allows for a CSE of a subsequent non-volatile read - if ((tree->gtFlags & GTF_IND_VOLATILE) != 0) - { - // For any Volatile indirection, we must handle it as a - // definition of GcHeap/ByrefExposed - fgCurMemoryDef |= memoryKindSet(GcHeap, ByrefExposed); - } - - // If the GT_IND is the lhs of an assignment, we'll handle it - // as a heap/byref def, when we get to assignment. - // Otherwise, we treat it as a use here. - if ((tree->gtFlags & GTF_IND_ASG_LHS) == 0) - { - GenTreeLclVarCommon* dummyLclVarTree = NULL; - bool dummyIsEntire = false; - GenTree* addrArg = tree->gtOp.gtOp1->gtEffectiveVal(/*commaOnly*/ true); - if (!addrArg->DefinesLocalAddr(this, /*width doesn't matter*/ 0, &dummyLclVarTree, &dummyIsEntire)) - { - fgCurMemoryUse |= memoryKindSet(GcHeap, ByrefExposed); - } - else - { - // Defines a local addr - assert(dummyLclVarTree != nullptr); - fgMarkUseDef(dummyLclVarTree->AsLclVarCommon()); - } - } - break; - - // These should have been morphed away to become GT_INDs: - case GT_FIELD: - case GT_INDEX: - unreached(); - break; - - // We'll assume these are use-then-defs of GcHeap/ByrefExposed. - case GT_LOCKADD: - case GT_XADD: - case GT_XCHG: - case GT_CMPXCHG: - fgCurMemoryUse |= memoryKindSet(GcHeap, ByrefExposed); - fgCurMemoryDef |= memoryKindSet(GcHeap, ByrefExposed); - fgCurMemoryHavoc |= memoryKindSet(GcHeap, ByrefExposed); - break; - - case GT_MEMORYBARRIER: - // Simliar to any Volatile indirection, we must handle this as a definition of GcHeap/ByrefExposed - fgCurMemoryDef |= memoryKindSet(GcHeap, ByrefExposed); - break; - - // For now, all calls read/write GcHeap/ByrefExposed, writes in their entirety. Might tighten this case - // later. - case GT_CALL: - { - GenTreeCall* call = tree->AsCall(); - bool modHeap = true; - if (call->gtCallType == CT_HELPER) - { - CorInfoHelpFunc helpFunc = eeGetHelperNum(call->gtCallMethHnd); - - if (!s_helperCallProperties.MutatesHeap(helpFunc) && !s_helperCallProperties.MayRunCctor(helpFunc)) - { - modHeap = false; - } - } - if (modHeap) - { - fgCurMemoryUse |= memoryKindSet(GcHeap, ByrefExposed); - fgCurMemoryDef |= memoryKindSet(GcHeap, ByrefExposed); - fgCurMemoryHavoc |= memoryKindSet(GcHeap, ByrefExposed); - } - } - - // If this is a p/invoke unmanaged call or if this is a tail-call - // and we have an unmanaged p/invoke call in the method, - // then we're going to run the p/invoke epilog. - // So we mark the FrameRoot as used by this instruction. - // This ensures that the block->bbVarUse will contain - // the FrameRoot local var if is it a tracked variable. - - if (!opts.ShouldUsePInvokeHelpers()) - { - if (tree->gtCall.IsUnmanaged() || (tree->gtCall.IsTailCall() && info.compCallUnmanaged)) - { - /* Get the TCB local and mark it as used */ - - noway_assert(info.compLvFrameListRoot < lvaCount); - - LclVarDsc* varDsc = &lvaTable[info.compLvFrameListRoot]; - - if (varDsc->lvTracked) - { - if (!VarSetOps::IsMember(this, fgCurDefSet, varDsc->lvVarIndex)) - { - VarSetOps::AddElemD(this, fgCurUseSet, varDsc->lvVarIndex); - } - } - } - } - - break; - - default: - - // Determine what memory kinds it defines. - if (tree->OperIsAssignment() || tree->OperIsBlkOp()) - { - GenTreeLclVarCommon* dummyLclVarTree = NULL; - if (tree->DefinesLocal(this, &dummyLclVarTree)) - { - if (lvaVarAddrExposed(dummyLclVarTree->gtLclNum)) - { - fgCurMemoryDef |= memoryKindSet(ByrefExposed); - - // We've found a store that modifies ByrefExposed - // memory but not GcHeap memory, so track their - // states separately. - byrefStatesMatchGcHeapStates = false; - } - } - else - { - // If it doesn't define a local, then it might update GcHeap/ByrefExposed. - fgCurMemoryDef |= memoryKindSet(GcHeap, ByrefExposed); - } - } - - // Are we seeing a GT_ for a GT_QMARK node? - if ((tree->OperKind() & GTK_RELOP) && (tree->gtFlags & GTF_RELOP_QMARK)) - { - // We are about to enter the parallel paths (i.e. the thenTree and the elseTree). - // Recursively call fgLegacyPerStatementLocalVarLiveness. - // At the very beginning of fgLegacyPerStatementLocalVarLiveness, we will cache the values of the - // current - // fgCurDefSet and fgCurUseSet into local variables defSet_BeforeSplit and useSet_BeforeSplit. - // The cached values will be used to restore fgCurDefSet and fgCurUseSet once we see the GT_COLON - // node. - tree = fgLegacyPerStatementLocalVarLiveness(tree->gtNext, tree); - - // We must have been returned here after seeing a GT_QMARK node. - noway_assert(tree->gtOper == GT_QMARK); - } - - break; - } - } - -_return: - return tree; -} - -/*****************************************************************************/ - -/***************************************************************************** - * Initialize the TCB local and the NDirect stub, afterwards "push" - * the hoisted NDirect stub. - * - * 'initRegs' is the set of registers which will be zeroed out by the prolog - * typically initRegs is zero - * - * The layout of the NDirect Inlined Call Frame is as follows: - * (see VM/frames.h and VM/JITInterface.cpp for more information) - * - * offset field name when set - * -------------------------------------------------------------- - * +00h vptr for class InlinedCallFrame method prolog - * +04h m_Next method prolog - * +08h m_Datum call site - * +0ch m_pCallSiteTracker (callsite ESP) call site and zeroed in method prolog - * +10h m_pCallerReturnAddress call site - * +14h m_pCalleeSavedRegisters not set by JIT - * +18h JIT retval spill area (int) before call_gc - * +1ch JIT retval spill area (long) before call_gc - * +20h Saved value of EBP method prolog - */ - -regMaskTP CodeGen::genPInvokeMethodProlog(regMaskTP initRegs) -{ - assert(compiler->compGeneratingProlog); - noway_assert(!compiler->opts.ShouldUsePInvokeHelpers()); - noway_assert(compiler->info.compCallUnmanaged); - - CORINFO_EE_INFO* pInfo = compiler->eeGetEEInfo(); - noway_assert(compiler->lvaInlinedPInvokeFrameVar != BAD_VAR_NUM); - - /* let's find out if compLvFrameListRoot is enregistered */ - - LclVarDsc* varDsc = &compiler->lvaTable[compiler->info.compLvFrameListRoot]; - - noway_assert(!varDsc->lvIsParam); - noway_assert(varDsc->lvType == TYP_I_IMPL); - - DWORD threadTlsIndex, *pThreadTlsIndex; - - threadTlsIndex = compiler->info.compCompHnd->getThreadTLSIndex((void**)&pThreadTlsIndex); -#if defined(_TARGET_X86_) - if (threadTlsIndex == (DWORD)-1 || pInfo->osType != CORINFO_WINNT) -#else - if (true) -#endif - { - // Instead of calling GetThread(), and getting GS cookie and - // InlinedCallFrame vptr through indirections, we'll call only one helper. - // The helper takes frame address in REG_PINVOKE_FRAME, returns TCB in REG_PINVOKE_TCB - // and uses REG_PINVOKE_SCRATCH as scratch register. - getEmitter()->emitIns_R_S(INS_lea, EA_PTRSIZE, REG_PINVOKE_FRAME, compiler->lvaInlinedPInvokeFrameVar, - pInfo->inlinedCallFrameInfo.offsetOfFrameVptr); - regTracker.rsTrackRegTrash(REG_PINVOKE_FRAME); - - // We're about to trask REG_PINVOKE_TCB, it better not be in use! - assert((regSet.rsMaskUsed & RBM_PINVOKE_TCB) == 0); - - // Don't use the argument registers (including the special argument in - // REG_PINVOKE_FRAME) for computing the target address. - regSet.rsLockReg(RBM_ARG_REGS | RBM_PINVOKE_FRAME); - - genEmitHelperCall(CORINFO_HELP_INIT_PINVOKE_FRAME, 0, EA_UNKNOWN); - - regSet.rsUnlockReg(RBM_ARG_REGS | RBM_PINVOKE_FRAME); - - if (varDsc->lvRegister) - { - regNumber regTgt = varDsc->lvRegNum; - - // we are about to initialize it. So turn the bit off in initRegs to prevent - // the prolog reinitializing it. - initRegs &= ~genRegMask(regTgt); - - if (regTgt != REG_PINVOKE_TCB) - { - // move TCB to the its register if necessary - getEmitter()->emitIns_R_R(INS_mov, EA_PTRSIZE, regTgt, REG_PINVOKE_TCB); - regTracker.rsTrackRegTrash(regTgt); - } - } - else - { - // move TCB to its stack location - getEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, REG_PINVOKE_TCB, - compiler->info.compLvFrameListRoot, 0); - } - - // We are done, the rest of this function deals with the inlined case. - return initRegs; - } - - regNumber regTCB; - - if (varDsc->lvRegister) - { - regTCB = varDsc->lvRegNum; - - // we are about to initialize it. So turn the bit off in initRegs to prevent - // the prolog reinitializing it. - initRegs &= ~genRegMask(regTCB); - } - else // varDsc is allocated on the Stack - { - regTCB = REG_PINVOKE_TCB; - } - -#if !defined(_TARGET_ARM_) -#define WIN_NT_TLS_OFFSET (0xE10) -#define WIN_NT5_TLS_HIGHOFFSET (0xf94) - - /* get TCB, mov reg, FS:[compiler->info.compEEInfo.threadTlsIndex] */ - - // TODO-ARM-CQ: should we inline TlsGetValue here? - - if (threadTlsIndex < 64) - { - // mov reg, FS:[0xE10+threadTlsIndex*4] - getEmitter()->emitIns_R_C(ins_Load(TYP_I_IMPL), EA_PTRSIZE, regTCB, FLD_GLOBAL_FS, - WIN_NT_TLS_OFFSET + threadTlsIndex * sizeof(int)); - regTracker.rsTrackRegTrash(regTCB); - } - else - { - DWORD basePtr = WIN_NT5_TLS_HIGHOFFSET; - threadTlsIndex -= 64; - - // mov reg, FS:[0x2c] or mov reg, fs:[0xf94] - // mov reg, [reg+threadTlsIndex*4] - - getEmitter()->emitIns_R_C(ins_Load(TYP_I_IMPL), EA_PTRSIZE, regTCB, FLD_GLOBAL_FS, basePtr); - getEmitter()->emitIns_R_AR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, regTCB, regTCB, threadTlsIndex * sizeof(int)); - regTracker.rsTrackRegTrash(regTCB); - } -#endif - - /* save TCB in local var if not enregistered */ - - if (!varDsc->lvRegister) - { - getEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, regTCB, compiler->info.compLvFrameListRoot, 0); - } - - /* set frame's vptr */ - - const void *inlinedCallFrameVptr, **pInlinedCallFrameVptr; - inlinedCallFrameVptr = compiler->info.compCompHnd->getInlinedCallFrameVptr((void**)&pInlinedCallFrameVptr); - noway_assert(inlinedCallFrameVptr != NULL); // if we have the TLS index, vptr must also be known - - instGen_Store_Imm_Into_Lcl(TYP_I_IMPL, EA_HANDLE_CNS_RELOC, (ssize_t)inlinedCallFrameVptr, - compiler->lvaInlinedPInvokeFrameVar, pInfo->inlinedCallFrameInfo.offsetOfFrameVptr, - REG_PINVOKE_SCRATCH); - - // Set the GSCookie - GSCookie gsCookie, *pGSCookie; - compiler->info.compCompHnd->getGSCookie(&gsCookie, &pGSCookie); - noway_assert(gsCookie != 0); // if we have the TLS index, GS cookie must also be known - - instGen_Store_Imm_Into_Lcl(TYP_I_IMPL, EA_PTRSIZE, (ssize_t)gsCookie, compiler->lvaInlinedPInvokeFrameVar, - pInfo->inlinedCallFrameInfo.offsetOfGSCookie, REG_PINVOKE_SCRATCH); - - /* Get current frame root (mov reg2, [reg+offsetOfThreadFrame]) and - set next field in frame */ - - getEmitter()->emitIns_R_AR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, REG_PINVOKE_SCRATCH, regTCB, - pInfo->offsetOfThreadFrame); - regTracker.rsTrackRegTrash(REG_PINVOKE_SCRATCH); - - getEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, REG_PINVOKE_SCRATCH, - compiler->lvaInlinedPInvokeFrameVar, pInfo->inlinedCallFrameInfo.offsetOfFrameLink); - - noway_assert(isFramePointerUsed()); // Setup of Pinvoke frame currently requires an EBP style frame - - /* set EBP value in frame */ - getEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, genFramePointerReg(), - compiler->lvaInlinedPInvokeFrameVar, pInfo->inlinedCallFrameInfo.offsetOfCalleeSavedFP); - - /* reset track field in frame */ - instGen_Store_Imm_Into_Lcl(TYP_I_IMPL, EA_PTRSIZE, 0, compiler->lvaInlinedPInvokeFrameVar, - pInfo->inlinedCallFrameInfo.offsetOfReturnAddress, REG_PINVOKE_SCRATCH); - - /* get address of our frame */ - - getEmitter()->emitIns_R_S(INS_lea, EA_PTRSIZE, REG_PINVOKE_SCRATCH, compiler->lvaInlinedPInvokeFrameVar, - pInfo->inlinedCallFrameInfo.offsetOfFrameVptr); - regTracker.rsTrackRegTrash(REG_PINVOKE_SCRATCH); - - /* now "push" our N/direct frame */ - - getEmitter()->emitIns_AR_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, REG_PINVOKE_SCRATCH, regTCB, - pInfo->offsetOfThreadFrame); - - return initRegs; -} - -/***************************************************************************** - * Unchain the InlinedCallFrame. - * Technically, this is not part of the epilog; it is called when we are generating code for a GT_RETURN node - * or tail call. - */ -void CodeGen::genPInvokeMethodEpilog() -{ - if (compiler->opts.ShouldUsePInvokeHelpers()) - return; - - noway_assert(compiler->info.compCallUnmanaged); - noway_assert(!compiler->opts.ShouldUsePInvokeHelpers()); - noway_assert(compiler->compCurBB == compiler->genReturnBB || - (compiler->compTailCallUsed && (compiler->compCurBB->bbJumpKind == BBJ_THROW)) || - (compiler->compJmpOpUsed && (compiler->compCurBB->bbFlags & BBF_HAS_JMP))); - - CORINFO_EE_INFO* pInfo = compiler->eeGetEEInfo(); - noway_assert(compiler->lvaInlinedPInvokeFrameVar != BAD_VAR_NUM); - - getEmitter()->emitDisableRandomNops(); - // debug check to make sure that we're not using ESI and/or EDI across this call, except for - // compLvFrameListRoot. - unsigned regTrashCheck = 0; - - /* XXX Tue 5/29/2007 - * We explicitly add interference for these in CodeGen::rgPredictRegUse. If you change the code - * sequence or registers used, make sure to update the interference for compiler->genReturnLocal. - */ - LclVarDsc* varDsc = &compiler->lvaTable[compiler->info.compLvFrameListRoot]; - regNumber reg; - regNumber reg2 = REG_PINVOKE_FRAME; - - // - // Two cases for epilog invocation: - // - // 1. Return - // We can trash the ESI/EDI registers. - // - // 2. Tail call - // When tail called, we'd like to preserve enregistered args, - // in ESI/EDI so we can pass it to the callee. - // - // For ARM, don't modify SP for storing and restoring the TCB/frame registers. - // Instead use the reserved local variable slot. - // - if (compiler->compCurBB->bbFlags & BBF_HAS_JMP) - { - if (compiler->rpMaskPInvokeEpilogIntf & RBM_PINVOKE_TCB) - { -#if FEATURE_FIXED_OUT_ARGS - // Save the register in the reserved local var slot. - getEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, REG_PINVOKE_TCB, - compiler->lvaPInvokeFrameRegSaveVar, 0); -#else - inst_RV(INS_push, REG_PINVOKE_TCB, TYP_I_IMPL); -#endif - } - if (compiler->rpMaskPInvokeEpilogIntf & RBM_PINVOKE_FRAME) - { -#if FEATURE_FIXED_OUT_ARGS - // Save the register in the reserved local var slot. - getEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, REG_PINVOKE_FRAME, - compiler->lvaPInvokeFrameRegSaveVar, REGSIZE_BYTES); -#else - inst_RV(INS_push, REG_PINVOKE_FRAME, TYP_I_IMPL); -#endif - } - } - - if (varDsc->lvRegister) - { - reg = varDsc->lvRegNum; - if (reg == reg2) - reg2 = REG_PINVOKE_TCB; - - regTrashCheck |= genRegMask(reg2); - } - else - { - /* mov esi, [tcb address] */ - - getEmitter()->emitIns_R_S(ins_Load(TYP_I_IMPL), EA_PTRSIZE, REG_PINVOKE_TCB, compiler->info.compLvFrameListRoot, - 0); - regTracker.rsTrackRegTrash(REG_PINVOKE_TCB); - reg = REG_PINVOKE_TCB; - - regTrashCheck = RBM_PINVOKE_TCB | RBM_PINVOKE_FRAME; - } - - /* mov edi, [ebp-frame.next] */ - - getEmitter()->emitIns_R_S(ins_Load(TYP_I_IMPL), EA_PTRSIZE, reg2, compiler->lvaInlinedPInvokeFrameVar, - pInfo->inlinedCallFrameInfo.offsetOfFrameLink); - regTracker.rsTrackRegTrash(reg2); - - /* mov [esi+offsetOfThreadFrame], edi */ - - getEmitter()->emitIns_AR_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, reg2, reg, pInfo->offsetOfThreadFrame); - - noway_assert(!(regSet.rsMaskUsed & regTrashCheck)); - - if (compiler->genReturnLocal != BAD_VAR_NUM && compiler->lvaTable[compiler->genReturnLocal].lvTracked && - compiler->lvaTable[compiler->genReturnLocal].lvRegister) - { - // really make sure we're not clobbering compiler->genReturnLocal. - noway_assert( - !(genRegMask(compiler->lvaTable[compiler->genReturnLocal].lvRegNum) & - ((varDsc->lvRegister ? genRegMask(varDsc->lvRegNum) : 0) | RBM_PINVOKE_TCB | RBM_PINVOKE_FRAME))); - } - - (void)regTrashCheck; - - // Restore the registers ESI and EDI. - if (compiler->compCurBB->bbFlags & BBF_HAS_JMP) - { - if (compiler->rpMaskPInvokeEpilogIntf & RBM_PINVOKE_FRAME) - { -#if FEATURE_FIXED_OUT_ARGS - // Restore the register from the reserved local var slot. - getEmitter()->emitIns_R_S(ins_Load(TYP_I_IMPL), EA_PTRSIZE, REG_PINVOKE_FRAME, - compiler->lvaPInvokeFrameRegSaveVar, REGSIZE_BYTES); -#else - inst_RV(INS_pop, REG_PINVOKE_FRAME, TYP_I_IMPL); -#endif - regTracker.rsTrackRegTrash(REG_PINVOKE_FRAME); - } - if (compiler->rpMaskPInvokeEpilogIntf & RBM_PINVOKE_TCB) - { -#if FEATURE_FIXED_OUT_ARGS - // Restore the register from the reserved local var slot. - getEmitter()->emitIns_R_S(ins_Load(TYP_I_IMPL), EA_PTRSIZE, REG_PINVOKE_TCB, - compiler->lvaPInvokeFrameRegSaveVar, 0); -#else - inst_RV(INS_pop, REG_PINVOKE_TCB, TYP_I_IMPL); -#endif - regTracker.rsTrackRegTrash(REG_PINVOKE_TCB); - } - } - getEmitter()->emitEnableRandomNops(); -} - -/***************************************************************************** - This function emits the call-site prolog for direct calls to unmanaged code. - It does all the necessary setup of the InlinedCallFrame. - frameListRoot specifies the local containing the thread control block. - argSize or methodToken is the value to be copied into the m_datum - field of the frame (methodToken may be indirected & have a reloc) - The function returns the register now containing the thread control block, - (it could be either enregistered or loaded into one of the scratch registers) -*/ - -regNumber CodeGen::genPInvokeCallProlog(LclVarDsc* frameListRoot, - int argSize, - CORINFO_METHOD_HANDLE methodToken, - BasicBlock* returnLabel) -{ - // Some stack locals might be 'cached' in registers, we need to trash them - // from the regTracker *and* also ensure the gc tracker does not consider - // them live (see the next assert). However, they might be live reg vars - // that are non-pointers CSE'd from pointers. - // That means the register will be live in rsMaskVars, so we can't just - // call gcMarkSetNpt(). - { - regMaskTP deadRegs = regTracker.rsTrashRegsForGCInterruptability() & ~RBM_ARG_REGS; - gcInfo.gcRegGCrefSetCur &= ~deadRegs; - gcInfo.gcRegByrefSetCur &= ~deadRegs; - -#ifdef DEBUG - deadRegs &= regSet.rsMaskVars; - if (deadRegs) - { - for (LclVarDsc* varDsc = compiler->lvaTable; - ((varDsc < (compiler->lvaTable + compiler->lvaCount)) && deadRegs); varDsc++) - { - if (!varDsc->lvTracked || !varDsc->lvRegister) - continue; - - if (!VarSetOps::IsMember(compiler, compiler->compCurLife, varDsc->lvVarIndex)) - continue; - - regMaskTP varRegMask = genRegMask(varDsc->lvRegNum); - if (isRegPairType(varDsc->lvType) && varDsc->lvOtherReg != REG_STK) - varRegMask |= genRegMask(varDsc->lvOtherReg); - - if (varRegMask & deadRegs) - { - // We found the enregistered var that should not be live if it - // was a GC pointer. - noway_assert(!varTypeIsGC(varDsc)); - deadRegs &= ~varRegMask; - } - } - } -#endif // DEBUG - } - - /* Since we are using the InlinedCallFrame, we should have spilled all - GC pointers to it - even from callee-saved registers */ - - noway_assert(((gcInfo.gcRegGCrefSetCur | gcInfo.gcRegByrefSetCur) & ~RBM_ARG_REGS) == 0); - - /* must specify only one of these parameters */ - noway_assert((argSize == 0) || (methodToken == NULL)); - - /* We are about to call unmanaged code directly. - Before we can do that we have to emit the following sequence: - - mov dword ptr [frame.callTarget], MethodToken - mov dword ptr [frame.callSiteTracker], esp - mov reg, dword ptr [tcb_address] - mov byte ptr [tcb+offsetOfGcState], 0 - - */ - - CORINFO_EE_INFO* pInfo = compiler->eeGetEEInfo(); - - noway_assert(compiler->lvaInlinedPInvokeFrameVar != BAD_VAR_NUM); - -#ifdef _TARGET_ARM_ - if (compiler->opts.ShouldUsePInvokeHelpers()) - { - regNumber baseReg; - int adr = compiler->lvaFrameAddress(compiler->lvaInlinedPInvokeFrameVar, false, &baseReg, 0); - - getEmitter()->emitIns_R_R_I(INS_add, EA_PTRSIZE, REG_ARG_0, baseReg, adr); - genEmitHelperCall(CORINFO_HELP_JIT_PINVOKE_BEGIN, - 0, // argSize - EA_UNKNOWN); // retSize - regTracker.rsTrackRegTrash(REG_ARG_0); - return REG_ARG_0; - } -#endif - - /* mov dword ptr [frame.callSiteTarget], value */ - - if (methodToken == NULL) - { - /* mov dword ptr [frame.callSiteTarget], argSize */ - instGen_Store_Imm_Into_Lcl(TYP_INT, EA_4BYTE, argSize, compiler->lvaInlinedPInvokeFrameVar, - pInfo->inlinedCallFrameInfo.offsetOfCallTarget); - } - else - { - void *embedMethHnd, *pEmbedMethHnd; - - embedMethHnd = (void*)compiler->info.compCompHnd->embedMethodHandle(methodToken, &pEmbedMethHnd); - - noway_assert((!embedMethHnd) != (!pEmbedMethHnd)); - - if (embedMethHnd != NULL) - { - /* mov dword ptr [frame.callSiteTarget], "MethodDesc" */ - - instGen_Store_Imm_Into_Lcl(TYP_I_IMPL, EA_HANDLE_CNS_RELOC, (ssize_t)embedMethHnd, - compiler->lvaInlinedPInvokeFrameVar, - pInfo->inlinedCallFrameInfo.offsetOfCallTarget); - } - else - { - /* mov reg, dword ptr [MethodDescIndir] - mov dword ptr [frame.callSiteTarget], reg */ - - regNumber reg = regSet.rsPickFreeReg(); - -#if CPU_LOAD_STORE_ARCH - instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, reg, (ssize_t)pEmbedMethHnd); - getEmitter()->emitIns_R_AR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, reg, reg, 0); -#else // !CPU_LOAD_STORE_ARCH - getEmitter()->emitIns_R_AI(ins_Load(TYP_I_IMPL), EA_PTR_DSP_RELOC, reg, (ssize_t)pEmbedMethHnd); -#endif // !CPU_LOAD_STORE_ARCH - regTracker.rsTrackRegTrash(reg); - getEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, reg, compiler->lvaInlinedPInvokeFrameVar, - pInfo->inlinedCallFrameInfo.offsetOfCallTarget); - } - } - - regNumber tcbReg = REG_NA; - - if (frameListRoot->lvRegister) - { - tcbReg = frameListRoot->lvRegNum; - } - else - { - tcbReg = regSet.rsGrabReg(RBM_ALLINT); - - /* mov reg, dword ptr [tcb address] */ - - getEmitter()->emitIns_R_S(ins_Load(TYP_I_IMPL), EA_PTRSIZE, tcbReg, - (unsigned)(frameListRoot - compiler->lvaTable), 0); - regTracker.rsTrackRegTrash(tcbReg); - } - -#ifdef _TARGET_X86_ - /* mov dword ptr [frame.callSiteTracker], esp */ - - getEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, REG_SPBASE, compiler->lvaInlinedPInvokeFrameVar, - pInfo->inlinedCallFrameInfo.offsetOfCallSiteSP); -#endif // _TARGET_X86_ - -#if CPU_LOAD_STORE_ARCH - regNumber tmpReg = regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(tcbReg)); - getEmitter()->emitIns_R_L(INS_adr, EA_PTRSIZE, returnLabel, tmpReg); - regTracker.rsTrackRegTrash(tmpReg); - getEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, tmpReg, compiler->lvaInlinedPInvokeFrameVar, - pInfo->inlinedCallFrameInfo.offsetOfReturnAddress); -#else // !CPU_LOAD_STORE_ARCH - /* mov dword ptr [frame.callSiteReturnAddress], label */ - - getEmitter()->emitIns_J_S(ins_Store(TYP_I_IMPL), EA_PTRSIZE, returnLabel, compiler->lvaInlinedPInvokeFrameVar, - pInfo->inlinedCallFrameInfo.offsetOfReturnAddress); -#endif // !CPU_LOAD_STORE_ARCH - -#if CPU_LOAD_STORE_ARCH - instGen_Set_Reg_To_Zero(EA_1BYTE, tmpReg); - - noway_assert(tmpReg != tcbReg); - - getEmitter()->emitIns_AR_R(ins_Store(TYP_BYTE), EA_1BYTE, tmpReg, tcbReg, pInfo->offsetOfGCState); -#else // !CPU_LOAD_STORE_ARCH - /* mov byte ptr [tcbReg+offsetOfGcState], 0 */ - - getEmitter()->emitIns_I_AR(ins_Store(TYP_BYTE), EA_1BYTE, 0, tcbReg, pInfo->offsetOfGCState); -#endif // !CPU_LOAD_STORE_ARCH - - return tcbReg; -} - -/***************************************************************************** - * - First we have to mark in the hoisted NDirect stub that we are back - in managed code. Then we have to check (a global flag) whether GC is - pending or not. If so, we just call into a jit-helper. - Right now we have this call always inlined, i.e. we always skip around - the jit-helper call. - Note: - The tcb address is a regular local (initialized in the prolog), so it is either - enregistered or in the frame: - - tcb_reg = [tcb_address is enregistered] OR [mov ecx, tcb_address] - mov byte ptr[tcb_reg+offsetOfGcState], 1 - cmp 'global GC pending flag', 0 - je @f - [mov ECX, tcb_reg] OR [ecx was setup above] ; we pass the tcb value to callGC - [mov [EBP+spill_area+0], eax] ; spill the int return value if any - [mov [EBP+spill_area+4], edx] ; spill the long return value if any - call @callGC - [mov eax, [EBP+spill_area+0] ] ; reload the int return value if any - [mov edx, [EBP+spill_area+4] ] ; reload the long return value if any - @f: - */ - -void CodeGen::genPInvokeCallEpilog(LclVarDsc* frameListRoot, regMaskTP retVal) -{ -#ifdef _TARGET_ARM_ - if (compiler->opts.ShouldUsePInvokeHelpers()) - { - noway_assert(compiler->lvaInlinedPInvokeFrameVar != BAD_VAR_NUM); - - regNumber baseReg; - int adr = compiler->lvaFrameAddress(compiler->lvaInlinedPInvokeFrameVar, false, &baseReg, 0); - - getEmitter()->emitIns_R_R_I(INS_add, EA_PTRSIZE, REG_ARG_0, baseReg, adr); - genEmitHelperCall(CORINFO_HELP_JIT_PINVOKE_END, - 0, // argSize - EA_UNKNOWN); // retSize - regTracker.rsTrackRegTrash(REG_ARG_0); - return; - } -#endif - - BasicBlock* clab_nostop; - CORINFO_EE_INFO* pInfo = compiler->eeGetEEInfo(); - regNumber reg2; - regNumber reg3; - -#ifdef _TARGET_ARM_ - reg3 = REG_R3; -#else - reg3 = REG_EDX; -#endif - - getEmitter()->emitDisableRandomNops(); - - if (frameListRoot->lvRegister) - { - /* make sure that register is live across the call */ - - reg2 = frameListRoot->lvRegNum; - noway_assert(genRegMask(reg2) & RBM_INT_CALLEE_SAVED); - } - else - { - /* mov reg2, dword ptr [tcb address] */ - CLANG_FORMAT_COMMENT_ANCHOR; - -#ifdef _TARGET_ARM_ - reg2 = REG_R2; -#else - reg2 = REG_ECX; -#endif - - getEmitter()->emitIns_R_S(ins_Load(TYP_I_IMPL), EA_PTRSIZE, reg2, - (unsigned)(frameListRoot - compiler->lvaTable), 0); - regTracker.rsTrackRegTrash(reg2); - } - -#ifdef _TARGET_ARM_ - /* mov r3, 1 */ - /* strb [r2+offsetOfGcState], r3 */ - instGen_Set_Reg_To_Imm(EA_PTRSIZE, reg3, 1); - getEmitter()->emitIns_AR_R(ins_Store(TYP_BYTE), EA_1BYTE, reg3, reg2, pInfo->offsetOfGCState); -#else - /* mov byte ptr [tcb+offsetOfGcState], 1 */ - getEmitter()->emitIns_I_AR(ins_Store(TYP_BYTE), EA_1BYTE, 1, reg2, pInfo->offsetOfGCState); -#endif - - /* test global flag (we return to managed code) */ - - LONG *addrOfCaptureThreadGlobal, **pAddrOfCaptureThreadGlobal; - - addrOfCaptureThreadGlobal = - compiler->info.compCompHnd->getAddrOfCaptureThreadGlobal((void**)&pAddrOfCaptureThreadGlobal); - noway_assert((!addrOfCaptureThreadGlobal) != (!pAddrOfCaptureThreadGlobal)); - - // Can we directly use addrOfCaptureThreadGlobal? - - if (addrOfCaptureThreadGlobal) - { -#ifdef _TARGET_ARM_ - instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, reg3, (ssize_t)addrOfCaptureThreadGlobal); - getEmitter()->emitIns_R_R_I(ins_Load(TYP_INT), EA_4BYTE, reg3, reg3, 0); - regTracker.rsTrackRegTrash(reg3); - getEmitter()->emitIns_R_I(INS_cmp, EA_4BYTE, reg3, 0); -#else - getEmitter()->emitIns_C_I(INS_cmp, EA_PTR_DSP_RELOC, FLD_GLOBAL_DS, (ssize_t)addrOfCaptureThreadGlobal, 0); -#endif - } - else - { -#ifdef _TARGET_ARM_ - instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, reg3, (ssize_t)pAddrOfCaptureThreadGlobal); - getEmitter()->emitIns_R_R_I(ins_Load(TYP_INT), EA_4BYTE, reg3, reg3, 0); - regTracker.rsTrackRegTrash(reg3); - getEmitter()->emitIns_R_R_I(ins_Load(TYP_INT), EA_4BYTE, reg3, reg3, 0); - getEmitter()->emitIns_R_I(INS_cmp, EA_4BYTE, reg3, 0); -#else // !_TARGET_ARM_ - - getEmitter()->emitIns_R_AI(ins_Load(TYP_I_IMPL), EA_PTR_DSP_RELOC, REG_ECX, - (ssize_t)pAddrOfCaptureThreadGlobal); - regTracker.rsTrackRegTrash(REG_ECX); - - getEmitter()->emitIns_I_AR(INS_cmp, EA_4BYTE, 0, REG_ECX, 0); - -#endif // !_TARGET_ARM_ - } - - /* */ - clab_nostop = genCreateTempLabel(); - - /* Generate the conditional jump */ - emitJumpKind jmpEqual = genJumpKindForOper(GT_EQ, CK_SIGNED); - inst_JMP(jmpEqual, clab_nostop); - -#ifdef _TARGET_ARM_ -// The helper preserves the return value on ARM -#else - /* save return value (if necessary) */ - if (retVal != RBM_NONE) - { - if (retVal == RBM_INTRET || retVal == RBM_LNGRET) - { - /* push eax */ - - inst_RV(INS_push, REG_INTRET, TYP_INT); - - if (retVal == RBM_LNGRET) - { - /* push edx */ - - inst_RV(INS_push, REG_EDX, TYP_INT); - } - } - } -#endif - - /* emit the call to the EE-helper that stops for GC (or other reasons) */ - - genEmitHelperCall(CORINFO_HELP_STOP_FOR_GC, 0, /* argSize */ - EA_UNKNOWN); /* retSize */ - -#ifdef _TARGET_ARM_ -// The helper preserves the return value on ARM -#else - /* restore return value (if necessary) */ - - if (retVal != RBM_NONE) - { - if (retVal == RBM_INTRET || retVal == RBM_LNGRET) - { - if (retVal == RBM_LNGRET) - { - /* pop edx */ - - inst_RV(INS_pop, REG_EDX, TYP_INT); - regTracker.rsTrackRegTrash(REG_EDX); - } - - /* pop eax */ - - inst_RV(INS_pop, REG_INTRET, TYP_INT); - regTracker.rsTrackRegTrash(REG_INTRET); - } - } -#endif - - /* genCondJump() closes the current emitter block */ - - genDefineTempLabel(clab_nostop); - - // This marks the InlinedCallFrame as "inactive". In fully interruptible code, this is not atomic with - // the above code. So the process is: - // 1) Return to cooperative mode - // 2) Check to see if we need to stop for GC - // 3) Return from the p/invoke (as far as the stack walker is concerned). - - /* mov dword ptr [frame.callSiteTracker], 0 */ - - instGen_Store_Imm_Into_Lcl(TYP_I_IMPL, EA_PTRSIZE, 0, compiler->lvaInlinedPInvokeFrameVar, - pInfo->inlinedCallFrameInfo.offsetOfReturnAddress); - - getEmitter()->emitEnableRandomNops(); -} - -/*****************************************************************************/ - -/***************************************************************************** -* TRACKING OF FLAGS -*****************************************************************************/ - -void CodeGen::genFlagsEqualToNone() -{ - genFlagsEqReg = REG_NA; - genFlagsEqVar = (unsigned)-1; - genFlagsEqLoc.Init(); -} - -/***************************************************************************** - * - * Record the fact that the flags register has a value that reflects the - * contents of the given register. - */ - -void CodeGen::genFlagsEqualToReg(GenTree* tree, regNumber reg) -{ - genFlagsEqLoc.CaptureLocation(getEmitter()); - genFlagsEqReg = reg; - - /* previous setting of flags by a var becomes invalid */ - - genFlagsEqVar = 0xFFFFFFFF; - - /* Set appropriate flags on the tree */ - - if (tree) - { - tree->gtFlags |= GTF_ZSF_SET; - assert(tree->gtSetFlags()); - } -} - -/***************************************************************************** - * - * Record the fact that the flags register has a value that reflects the - * contents of the given local variable. - */ - -void CodeGen::genFlagsEqualToVar(GenTree* tree, unsigned var) -{ - genFlagsEqLoc.CaptureLocation(getEmitter()); - genFlagsEqVar = var; - - /* previous setting of flags by a register becomes invalid */ - - genFlagsEqReg = REG_NA; - - /* Set appropriate flags on the tree */ - - if (tree) - { - tree->gtFlags |= GTF_ZSF_SET; - assert(tree->gtSetFlags()); - } -} - -/***************************************************************************** - * - * Return an indication of whether the flags register is set to the current - * value of the given register/variable. The return value is as follows: - * - * false .. nothing - * true .. the zero flag (ZF) and sign flag (SF) is set - */ - -bool CodeGen::genFlagsAreReg(regNumber reg) -{ - if ((genFlagsEqReg == reg) && genFlagsEqLoc.IsCurrentLocation(getEmitter())) - { - return true; - } - - return false; -} - -bool CodeGen::genFlagsAreVar(unsigned var) -{ - if ((genFlagsEqVar == var) && genFlagsEqLoc.IsCurrentLocation(getEmitter())) - { - return true; - } - - return false; -} - -/***************************************************************************** - * This utility function returns true iff the execution path from "from" - * (inclusive) to "to" (exclusive) contains a death of the given var - */ -bool CodeGen::genContainsVarDeath(GenTree* from, GenTree* to, unsigned varNum) -{ - GenTree* tree; - for (tree = from; tree != NULL && tree != to; tree = tree->gtNext) - { - if (tree->IsLocal() && (tree->gtFlags & GTF_VAR_DEATH)) - { - unsigned dyingVarNum = tree->gtLclVarCommon.gtLclNum; - if (dyingVarNum == varNum) - return true; - LclVarDsc* varDsc = &(compiler->lvaTable[varNum]); - if (varDsc->lvPromoted) - { - assert(varDsc->lvType == TYP_STRUCT); - unsigned firstFieldNum = varDsc->lvFieldLclStart; - if (varNum >= firstFieldNum && varNum < firstFieldNum + varDsc->lvFieldCnt) - { - return true; - } - } - } - } - assert(tree != NULL); - return false; -} - -// Update liveness (always var liveness, i.e., compCurLife, and also, if "ForCodeGen" is true, reg liveness, i.e., -// regSet.rsMaskVars as well) -// if the given lclVar (or indir(addr(local)))/regVar node is going live (being born) or dying. -template -void Compiler::compUpdateLifeVar(GenTree* tree, VARSET_TP* pLastUseVars) -{ - GenTree* indirAddrLocal = fgIsIndirOfAddrOfLocal(tree); - assert(tree->OperIsNonPhiLocal() || indirAddrLocal != nullptr); - - // Get the local var tree -- if "tree" is "Ldobj(addr(x))", or "ind(addr(x))" this is "x", else it's "tree". - GenTree* lclVarTree = indirAddrLocal; - if (lclVarTree == nullptr) - { - lclVarTree = tree; - } - unsigned int lclNum = lclVarTree->gtLclVarCommon.gtLclNum; - LclVarDsc* varDsc = lvaTable + lclNum; - -#ifdef DEBUG -#if !defined(_TARGET_AMD64_) - // There are no addr nodes on ARM and we are experimenting with encountering vars in 'random' order. - // Struct fields are not traversed in a consistent order, so ignore them when - // verifying that we see the var nodes in execution order - if (ForCodeGen) - { - if (tree->OperIsIndir()) - { - assert(indirAddrLocal != NULL); - } - else if (tree->gtNext != NULL && tree->gtNext->gtOper == GT_ADDR && - ((tree->gtNext->gtNext == NULL || !tree->gtNext->gtNext->OperIsIndir()))) - { - assert(tree->IsLocal()); // Can only take the address of a local. - // The ADDR might occur in a context where the address it contributes is eventually - // dereferenced, so we can't say that this is not a use or def. - } -#if 0 - // TODO-ARM64-Bug?: These asserts don't seem right for ARM64: I don't understand why we have to assert - // two consecutive lclvars (in execution order) can only be observed if the first one is a struct field. - // It seems to me this is code only applicable to the legacy JIT and not RyuJIT (and therefore why it was - // ifdef'ed out for AMD64). - else if (!varDsc->lvIsStructField) - { - GenTree* prevTree; - for (prevTree = tree->gtPrev; - prevTree != NULL && prevTree != compCurLifeTree; - prevTree = prevTree->gtPrev) - { - if ((prevTree->gtOper == GT_LCL_VAR) || (prevTree->gtOper == GT_REG_VAR)) - { - LclVarDsc * prevVarDsc = lvaTable + prevTree->gtLclVarCommon.gtLclNum; - - // These are the only things for which this method MUST be called - assert(prevVarDsc->lvIsStructField); - } - } - assert(prevTree == compCurLifeTree); - } -#endif // 0 - } -#endif // !_TARGET_AMD64_ -#endif // DEBUG - - compCurLifeTree = tree; - VARSET_TP newLife(VarSetOps::MakeCopy(this, compCurLife)); - - // By codegen, a struct may not be TYP_STRUCT, so we have to - // check lvPromoted, for the case where the fields are being - // tracked. - if (!varDsc->lvTracked && !varDsc->lvPromoted) - { - return; - } - - bool isBorn = ((tree->gtFlags & GTF_VAR_DEF) != 0 && (tree->gtFlags & GTF_VAR_USEASG) == 0); // if it's "x = - // ..." then variable - // "x" must have had a - // previous, original, - // site to be born. - bool isDying = ((tree->gtFlags & GTF_VAR_DEATH) != 0); -#ifndef LEGACY_BACKEND - bool spill = ((tree->gtFlags & GTF_SPILL) != 0); -#endif // !LEGACY_BACKEND - -#ifndef LEGACY_BACKEND - // For RyuJIT backend, since all tracked vars are register candidates, but not all are in registers at all times, - // we maintain two separate sets of variables - the total set of variables that are either - // born or dying here, and the subset of those that are on the stack - VARSET_TP stackVarDeltaSet(VarSetOps::MakeEmpty(this)); -#endif // !LEGACY_BACKEND - - if (isBorn || isDying) - { - bool hasDeadTrackedFieldVars = false; // If this is true, then, for a LDOBJ(ADDR()), - VARSET_TP* deadTrackedFieldVars = - nullptr; // *deadTrackedFieldVars indicates which tracked field vars are dying. - VARSET_TP varDeltaSet(VarSetOps::MakeEmpty(this)); - - if (varDsc->lvTracked) - { - VarSetOps::AddElemD(this, varDeltaSet, varDsc->lvVarIndex); - if (ForCodeGen) - { -#ifndef LEGACY_BACKEND - if (isBorn && varDsc->lvIsRegCandidate() && tree->gtHasReg()) - { - codeGen->genUpdateVarReg(varDsc, tree); - } -#endif // !LEGACY_BACKEND - if (varDsc->lvIsInReg() -#ifndef LEGACY_BACKEND - && tree->gtRegNum != REG_NA -#endif // !LEGACY_BACKEND - ) - { - codeGen->genUpdateRegLife(varDsc, isBorn, isDying DEBUGARG(tree)); - } -#ifndef LEGACY_BACKEND - else - { - VarSetOps::AddElemD(this, stackVarDeltaSet, varDsc->lvVarIndex); - } -#endif // !LEGACY_BACKEND - } - } - else if (varDsc->lvPromoted) - { - if (indirAddrLocal != nullptr && isDying) - { - assert(!isBorn); // GTF_VAR_DEATH only set for LDOBJ last use. - hasDeadTrackedFieldVars = GetPromotedStructDeathVars()->Lookup(indirAddrLocal, &deadTrackedFieldVars); - if (hasDeadTrackedFieldVars) - { - VarSetOps::Assign(this, varDeltaSet, *deadTrackedFieldVars); - } - } - - for (unsigned i = varDsc->lvFieldLclStart; i < varDsc->lvFieldLclStart + varDsc->lvFieldCnt; ++i) - { - LclVarDsc* fldVarDsc = &(lvaTable[i]); - noway_assert(fldVarDsc->lvIsStructField); - if (fldVarDsc->lvTracked) - { - unsigned fldVarIndex = fldVarDsc->lvVarIndex; - noway_assert(fldVarIndex < lvaTrackedCount); - if (!hasDeadTrackedFieldVars) - { - VarSetOps::AddElemD(this, varDeltaSet, fldVarIndex); - if (ForCodeGen) - { - // We repeat this call here and below to avoid the VarSetOps::IsMember - // test in this, the common case, where we have no deadTrackedFieldVars. - if (fldVarDsc->lvIsInReg()) - { -#ifndef LEGACY_BACKEND - if (isBorn) - { - codeGen->genUpdateVarReg(fldVarDsc, tree); - } -#endif // !LEGACY_BACKEND - codeGen->genUpdateRegLife(fldVarDsc, isBorn, isDying DEBUGARG(tree)); - } -#ifndef LEGACY_BACKEND - else - { - VarSetOps::AddElemD(this, stackVarDeltaSet, fldVarIndex); - } -#endif // !LEGACY_BACKEND - } - } - else if (ForCodeGen && VarSetOps::IsMember(this, varDeltaSet, fldVarIndex)) - { - if (lvaTable[i].lvIsInReg()) - { -#ifndef LEGACY_BACKEND - if (isBorn) - { - codeGen->genUpdateVarReg(fldVarDsc, tree); - } -#endif // !LEGACY_BACKEND - codeGen->genUpdateRegLife(fldVarDsc, isBorn, isDying DEBUGARG(tree)); - } -#ifndef LEGACY_BACKEND - else - { - VarSetOps::AddElemD(this, stackVarDeltaSet, fldVarIndex); - } -#endif // !LEGACY_BACKEND - } - } - } - } - - // First, update the live set - if (isDying) - { - // We'd like to be able to assert the following, however if we are walking - // through a qmark/colon tree, we may encounter multiple last-use nodes. - // assert (VarSetOps::IsSubset(compiler, regVarDeltaSet, newLife)); - VarSetOps::DiffD(this, newLife, varDeltaSet); - if (pLastUseVars != nullptr) - { - VarSetOps::Assign(this, *pLastUseVars, varDeltaSet); - } - } - else - { - // This shouldn't be in newLife, unless this is debug code, in which - // case we keep vars live everywhere, OR the variable is address-exposed, - // OR this block is part of a try block, in which case it may be live at the handler - // Could add a check that, if it's in newLife, that it's also in - // fgGetHandlerLiveVars(compCurBB), but seems excessive - // - // For a dead store, it can be the case that we set both isBorn and isDying to true. - // (We don't eliminate dead stores under MinOpts, so we can't assume they're always - // eliminated.) If it's both, we handled it above. - VarSetOps::UnionD(this, newLife, varDeltaSet); - } - } - - if (!VarSetOps::Equal(this, compCurLife, newLife)) - { -#ifdef DEBUG - if (verbose) - { - printf("\t\t\t\t\t\t\tLive vars: "); - dumpConvertedVarSet(this, compCurLife); - printf(" => "); - dumpConvertedVarSet(this, newLife); - printf("\n"); - } -#endif // DEBUG - - VarSetOps::Assign(this, compCurLife, newLife); - - if (ForCodeGen) - { -#ifndef LEGACY_BACKEND - - // Only add vars to the gcInfo.gcVarPtrSetCur if they are currently on stack, since the - // gcInfo.gcTrkStkPtrLcls - // includes all TRACKED vars that EVER live on the stack (i.e. are not always in a register). - VARSET_TP gcTrkStkDeltaSet( - VarSetOps::Intersection(this, codeGen->gcInfo.gcTrkStkPtrLcls, stackVarDeltaSet)); - if (!VarSetOps::IsEmpty(this, gcTrkStkDeltaSet)) - { -#ifdef DEBUG - if (verbose) - { - printf("\t\t\t\t\t\t\tGCvars: "); - dumpConvertedVarSet(this, codeGen->gcInfo.gcVarPtrSetCur); - printf(" => "); - } -#endif // DEBUG - - if (isBorn) - { - VarSetOps::UnionD(this, codeGen->gcInfo.gcVarPtrSetCur, gcTrkStkDeltaSet); - } - else - { - VarSetOps::DiffD(this, codeGen->gcInfo.gcVarPtrSetCur, gcTrkStkDeltaSet); - } - -#ifdef DEBUG - if (verbose) - { - dumpConvertedVarSet(this, codeGen->gcInfo.gcVarPtrSetCur); - printf("\n"); - } -#endif // DEBUG - } - -#else // LEGACY_BACKEND - -#ifdef DEBUG - if (verbose) - { - VARSET_TP gcVarPtrSetNew(VarSetOps::Intersection(this, newLife, codeGen->gcInfo.gcTrkStkPtrLcls)); - if (!VarSetOps::Equal(this, codeGen->gcInfo.gcVarPtrSetCur, gcVarPtrSetNew)) - { - printf("\t\t\t\t\t\t\tGCvars: "); - dumpConvertedVarSet(this, codeGen->gcInfo.gcVarPtrSetCur); - printf(" => "); - dumpConvertedVarSet(this, gcVarPtrSetNew); - printf("\n"); - } - } -#endif // DEBUG - - VarSetOps::AssignNoCopy(this, codeGen->gcInfo.gcVarPtrSetCur, - VarSetOps::Intersection(this, newLife, codeGen->gcInfo.gcTrkStkPtrLcls)); - -#endif // LEGACY_BACKEND - - codeGen->siUpdate(); - } - } - -#ifndef LEGACY_BACKEND - if (ForCodeGen && spill) - { - assert(!varDsc->lvPromoted); - codeGen->genSpillVar(tree); - if (VarSetOps::IsMember(this, codeGen->gcInfo.gcTrkStkPtrLcls, varDsc->lvVarIndex)) - { - if (!VarSetOps::IsMember(this, codeGen->gcInfo.gcVarPtrSetCur, varDsc->lvVarIndex)) - { - VarSetOps::AddElemD(this, codeGen->gcInfo.gcVarPtrSetCur, varDsc->lvVarIndex); -#ifdef DEBUG - if (verbose) - { - printf("\t\t\t\t\t\t\tVar V%02u becoming live\n", varDsc - lvaTable); - } -#endif // DEBUG - } - } - } -#endif // !LEGACY_BACKEND -} - -// Need an explicit instantiation. -template void Compiler::compUpdateLifeVar(GenTree* tree, VARSET_TP* pLastUseVars); -template void Compiler::compUpdateLifeVar(GenTree* tree, VARSET_TP* pLastUseVars); - -/***************************************************************************** - * - * Update the current set of live variables based on the life set recorded - * in the given expression tree node. - */ -template -inline void Compiler::compUpdateLife(GenTree* tree) -{ - // TODO-Cleanup: We shouldn't really be calling this more than once - if (tree == compCurLifeTree) - { - return; - } - - if (!tree->OperIsNonPhiLocal() && fgIsIndirOfAddrOfLocal(tree) == nullptr) - { - return; - } - - compUpdateLifeVar(tree); -} - -template void Compiler::compUpdateLife(GenTree* tree); -template void Compiler::compUpdateLife(GenTree* tree); - -#endif // LEGACY_BACKEND diff --git a/src/jit/codegenlinear.cpp b/src/jit/codegenlinear.cpp index 26b52c20d2..0d70ba07ad 100644 --- a/src/jit/codegenlinear.cpp +++ b/src/jit/codegenlinear.cpp @@ -15,7 +15,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #pragma hdrstop #endif -#ifndef LEGACY_BACKEND // This file is ONLY used for the RyuJIT backend that uses the linear scan register allocator. #include "emit.h" #include "codegen.h" @@ -695,21 +694,6 @@ XX XX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ -// - -//------------------------------------------------------------------------ -// genGetAssignedReg: Get the register assigned to the given node -// -// Arguments: -// tree - the lclVar node whose assigned register we want -// -// Return Value: -// The assigned regNumber -// -regNumber CodeGenInterface::genGetAssignedReg(GenTree* tree) -{ - return tree->gtRegNum; -} //------------------------------------------------------------------------ // genSpillVar: Spill a local variable @@ -749,22 +733,8 @@ void CodeGen::genSpillVar(GenTree* tree) } instruction storeIns = ins_Store(lclTyp, compiler->isSIMDTypeLocalAligned(varNum)); -#if CPU_LONG_USES_REGPAIR - if (varTypeIsMultiReg(tree)) - { - assert(varDsc->lvRegNum == genRegPairLo(tree->gtRegPair)); - assert(varDsc->lvOtherReg == genRegPairHi(tree->gtRegPair)); - regNumber regLo = genRegPairLo(tree->gtRegPair); - regNumber regHi = genRegPairHi(tree->gtRegPair); - inst_TT_RV(storeIns, tree, regLo); - inst_TT_RV(storeIns, tree, regHi, 4); - } - else -#endif - { - assert(varDsc->lvRegNum == tree->gtRegNum); - inst_TT_RV(storeIns, tree, tree->gtRegNum, 0, size); - } + assert(varDsc->lvRegNum == tree->gtRegNum); + inst_TT_RV(storeIns, tree, tree->gtRegNum, 0, size); if (restoreRegVar) { @@ -1929,5 +1899,3 @@ void CodeGen::genCodeForCast(GenTreeOp* tree) } // The per-case functions call genProduceReg() } - -#endif // !LEGACY_BACKEND diff --git a/src/jit/codegenlinear.h b/src/jit/codegenlinear.h index 7ef7e95d10..5fa5b4aef0 100644 --- a/src/jit/codegenlinear.h +++ b/src/jit/codegenlinear.h @@ -8,8 +8,6 @@ // definition of the CodeGen class. // -#ifndef LEGACY_BACKEND // Not necessary (it's this way in the #include location), but helpful to IntelliSense - void genSetRegToConst(regNumber targetReg, var_types targetType, GenTree* tree); void genCodeForTreeNode(GenTree* treeNode); void genCodeForBinary(GenTree* treeNode); @@ -370,5 +368,3 @@ inline void genCheckConsumeNode(GenTree* treeNode) { } #endif // DEBUG - -#endif // !LEGACY_BACKEND diff --git a/src/jit/codegenxarch.cpp b/src/jit/codegenxarch.cpp index e677923863..a98c9e3727 100644 --- a/src/jit/codegenxarch.cpp +++ b/src/jit/codegenxarch.cpp @@ -15,8 +15,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #pragma hdrstop #endif -#ifndef LEGACY_BACKEND // This file is ONLY used for the RyuJIT backend that uses the linear scan register allocator. - #ifdef _TARGET_XARCH_ #include "emit.h" #include "codegen.h" @@ -8770,5 +8768,3 @@ void CodeGen::genAmd64EmitterUnitTests() #endif // defined(DEBUG) && defined(LATE_DISASM) && defined(_TARGET_AMD64_) #endif // _TARGET_AMD64_ - -#endif // !LEGACY_BACKEND diff --git a/src/jit/compiler.cpp b/src/jit/compiler.cpp index 8cad182e2a..0a64394e1b 100644 --- a/src/jit/compiler.cpp +++ b/src/jit/compiler.cpp @@ -19,12 +19,8 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #include "ssabuilder.h" #include "valuenum.h" #include "rangecheck.h" - -#ifndef LEGACY_BACKEND #include "lower.h" #include "stacklevelsetter.h" -#endif // !LEGACY_BACKEND - #include "jittelemetry.h" #if defined(DEBUG) @@ -1006,14 +1002,9 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, // if (structSize <= sizeof(double)) { -#if defined LEGACY_BACKEND - if (!IsHfa(clsHnd)) -#endif - { - // We set the "primitive" useType based upon the structSize - // and also examine the clsHnd to see if it is an HFA of count one - useType = getPrimitiveTypeForStruct(structSize, clsHnd); - } + // We set the "primitive" useType based upon the structSize + // and also examine the clsHnd to see if it is an HFA of count one + useType = getPrimitiveTypeForStruct(structSize, clsHnd); } #endif // UNIX_AMD64_ABI @@ -1051,10 +1042,8 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, // Structs that are HFA's are returned in multiple registers if (IsHfa(clsHnd)) { -#if !defined(LEGACY_BACKEND) // HFA's of count one should have been handled by getPrimitiveTypeForStruct assert(GetHfaCount(clsHnd) >= 2); -#endif // !defined(LEGACY_BACKEND) // setup wbPassType and useType indicate that this is returned by value as an HFA // using multiple registers @@ -1852,11 +1841,6 @@ void Compiler::compDisplayStaticSizes(FILE* fout) sizeof(bbDummy->bbTypesOut)); #endif // VERIFIER -#if FEATURE_STACK_FP_X87 - fprintf(fout, "Offset / size of bbFPStateX87 = %3u / %3u\n", offsetof(BasicBlock, bbFPStateX87), - sizeof(bbDummy->bbFPStateX87)); -#endif // FEATURE_STACK_FP_X87 - #ifdef DEBUG fprintf(fout, "Offset / size of bbLoopNum = %3u / %3u\n", offsetof(BasicBlock, bbLoopNum), sizeof(bbDummy->bbLoopNum)); @@ -1907,10 +1891,6 @@ void Compiler::compInit(ArenaAllocator* pAlloc, InlineInfo* inlineInfo) compAllocatorDebugOnly = nullptr; #endif // DEBUG #endif // MEASURE_MEM_ALLOC - -#ifdef LEGACY_BACKEND - compQMarks = nullptr; -#endif } else { @@ -1926,10 +1906,6 @@ void Compiler::compInit(ArenaAllocator* pAlloc, InlineInfo* inlineInfo) compAllocatorDebugOnly = new (this, CMK_Unknown) CompAllocator(this, CMK_DebugOnly); #endif // DEBUG #endif // MEASURE_MEM_ALLOC - -#ifdef LEGACY_BACKEND - compQMarks = new (this, CMK_Unknown) JitExpandArrayStack(getAllocator()); -#endif } #ifdef FEATURE_TRACELOGGING @@ -1956,13 +1932,8 @@ void Compiler::compInit(ArenaAllocator* pAlloc, InlineInfo* inlineInfo) if (!compIsForInlining()) { codeGen = getCodeGenerator(this); -#ifdef LEGACY_BACKEND - raInit(); -#endif // LEGACY_BACKEND optInit(); -#ifndef LEGACY_BACKEND hashBv::Init(this); -#endif // !LEGACY_BACKEND compVarScopeMap = nullptr; @@ -2011,9 +1982,6 @@ void Compiler::compInit(ArenaAllocator* pAlloc, InlineInfo* inlineInfo) compUnsafeCastUsed = false; #if CPU_USES_BLOCK_MOVE compBlkOpUsed = false; -#endif -#if FEATURE_STACK_FP_X87 - compMayHaveTransitionBlocks = false; #endif compNeedsGSSecurityCookie = false; compGSReorderStackLayout = false; @@ -2024,9 +1992,7 @@ void Compiler::compInit(ArenaAllocator* pAlloc, InlineInfo* inlineInfo) compGeneratingProlog = false; compGeneratingEpilog = false; -#ifndef LEGACY_BACKEND - compLSRADone = false; -#endif // !LEGACY_BACKEND + compLSRADone = false; compRationalIRForm = false; #ifdef DEBUG @@ -2285,11 +2251,7 @@ VarName Compiler::compVarName(regNumber reg, bool isFloatReg) { if (isFloatReg) { -#if FEATURE_STACK_FP_X87 - assert(reg < FP_STK_SIZE); // would like to have same assert as below but sometimes you get -1? -#else assert(genIsValidFloatReg(reg)); -#endif } else { @@ -2322,34 +2284,8 @@ VarName Compiler::compVarName(regNumber reg, bool isFloatReg) } } } - -#ifdef LEGACY_BACKEND - // maybe var is marked dead, but still used (last use) - if (!isFloatReg && codeGen->regSet.rsUsedTree[reg] != NULL) - { - GenTree* nodePtr; - - if (GenTree::OperIsUnary(codeGen->regSet.rsUsedTree[reg]->OperGet())) - { - assert(codeGen->regSet.rsUsedTree[reg]->gtOp.gtOp1 != NULL); - nodePtr = codeGen->regSet.rsUsedTree[reg]->gtOp.gtOp1; - } - else - { - nodePtr = codeGen->regSet.rsUsedTree[reg]; - } - - if ((nodePtr->gtOper == GT_REG_VAR) && (nodePtr->gtRegVar.gtRegNum == reg) && - (nodePtr->gtRegVar.gtLclNum < info.compVarScopesCount)) - { - VarScopeDsc* varScope = - compFindLocalVar(nodePtr->gtRegVar.gtLclNum, compCurBB->bbCodeOffs, compCurBB->bbCodeOffsEnd); - if (varScope) - return varScope->vsdName; - } - } -#endif // LEGACY_BACKEND } + return nullptr; } @@ -2385,25 +2321,6 @@ const char* Compiler::compRegVarName(regNumber reg, bool displayVar, bool isFloa return getRegName(reg, isFloatReg); } -#define MAX_REG_PAIR_NAME_LENGTH 10 - -const char* Compiler::compRegPairName(regPairNo regPair) -{ - static char regNameLong[MAX_REG_PAIR_NAME_LENGTH]; - - if (regPair == REG_PAIR_NONE) - { - return "NA|NA"; - } - - assert(regPair >= REG_PAIR_FIRST && regPair <= REG_PAIR_LAST); - - strcpy_s(regNameLong, sizeof(regNameLong), compRegVarName(genRegPairLo(regPair))); - strcat_s(regNameLong, sizeof(regNameLong), "|"); - strcat_s(regNameLong, sizeof(regNameLong), compRegVarName(genRegPairHi(regPair))); - return regNameLong; -} - const char* Compiler::compRegNameForSize(regNumber reg, size_t size) { if (size == 0 || size >= 4) @@ -2452,30 +2369,6 @@ const char* Compiler::compFPregVarName(unsigned fpReg, bool displayVar) index = (index + 1) % 2; // circular reuse of index -#if FEATURE_STACK_FP_X87 - /* 'fpReg' is the distance from the bottom of the stack, ie. - * it is independant of the current FP stack level - */ - - if (displayVar && codeGen->genFPregCnt) - { - assert(fpReg < FP_STK_SIZE); - assert(compCodeGenDone || (fpReg <= codeGen->compCurFPState.m_uStackSize)); - - int pos = codeGen->genFPregCnt - (fpReg + 1 - codeGen->genGetFPstkLevel()); - if (pos >= 0) - { - VarName varName = compVarName((regNumber)pos, true); - - if (varName) - { - sprintf_s(nameVarReg[index], NAME_VAR_REG_BUFFER_LEN, "ST(%d)'%s'", fpReg, VarNameToStr(varName)); - return nameVarReg[index]; - } - } - } -#endif // FEATURE_STACK_FP_X87 - /* no debug info required or no variable in that register -> return standard name */ @@ -2570,7 +2463,7 @@ void Compiler::compSetProcessor() #if defined(_TARGET_ARM_) info.genCPU = CPU_ARM; #elif defined(_TARGET_AMD64_) - info.genCPU = CPU_X64; + info.genCPU = CPU_X64; #elif defined(_TARGET_X86_) if (jitFlags.IsSet(JitFlags::JIT_FLAG_TARGET_P4)) info.genCPU = CPU_X86_PENTIUM_4; @@ -2584,52 +2477,17 @@ void Compiler::compSetProcessor() CLANG_FORMAT_COMMENT_ANCHOR; #ifdef _TARGET_AMD64_ - opts.compUseFCOMI = false; - opts.compUseCMOV = true; - opts.compCanUseSSE2 = true; + opts.compUseFCOMI = false; + opts.compUseCMOV = true; #elif defined(_TARGET_X86_) - opts.compUseFCOMI = jitFlags.IsSet(JitFlags::JIT_FLAG_USE_FCOMI); - opts.compUseCMOV = jitFlags.IsSet(JitFlags::JIT_FLAG_USE_CMOV); - -#ifdef LEGACY_BACKEND - opts.compCanUseSSE2 = jitFlags.IsSet(JitFlags::JIT_FLAG_USE_SSE2); -#else - // RyuJIT/x86 requires SSE2 to be available: there is no support for generating floating-point - // code with x87 instructions. - opts.compCanUseSSE2 = true; -#endif + opts.compUseFCOMI = jitFlags.IsSet(JitFlags::JIT_FLAG_USE_FCOMI); + opts.compUseCMOV = jitFlags.IsSet(JitFlags::JIT_FLAG_USE_CMOV); #ifdef DEBUG if (opts.compUseFCOMI) opts.compUseFCOMI = !compStressCompile(STRESS_USE_FCOMI, 50); if (opts.compUseCMOV) opts.compUseCMOV = !compStressCompile(STRESS_USE_CMOV, 50); - -#ifdef LEGACY_BACKEND - - // Should we override the SSE2 setting? - enum - { - SSE2_FORCE_DISABLE = 0, - SSE2_FORCE_USE = 1, - SSE2_FORCE_INVALID = -1 - }; - - if (JitConfig.JitCanUseSSE2() == SSE2_FORCE_DISABLE) - opts.compCanUseSSE2 = false; - else if (JitConfig.JitCanUseSSE2() == SSE2_FORCE_USE) - opts.compCanUseSSE2 = true; - else if (opts.compCanUseSSE2) - opts.compCanUseSSE2 = !compStressCompile(STRESS_GENERIC_VARN, 50); - -#else // !LEGACY_BACKEND - - // RyuJIT/x86 requires SSE2 to be available and hence - // don't turn off compCanUseSSE2 under stress. - assert(opts.compCanUseSSE2); - -#endif // !LEGACY_BACKEND - #endif // DEBUG #endif // _TARGET_X86_ @@ -2640,114 +2498,111 @@ void Compiler::compSetProcessor() if (!jitFlags.IsSet(JitFlags::JIT_FLAG_PREJIT)) { - if (opts.compCanUseSSE2) + if (configEnableISA(InstructionSet_SSE)) + { + opts.setSupportedISA(InstructionSet_SSE); + } + if (configEnableISA(InstructionSet_SSE2)) + { + opts.setSupportedISA(InstructionSet_SSE2); + } + if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_AES)) { - if (configEnableISA(InstructionSet_SSE)) + if (configEnableISA(InstructionSet_AES)) { - opts.setSupportedISA(InstructionSet_SSE); + opts.setSupportedISA(InstructionSet_AES); } - if (configEnableISA(InstructionSet_SSE2)) + } + if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_AVX)) + { + if (configEnableISA(InstructionSet_AVX)) { - opts.setSupportedISA(InstructionSet_SSE2); + opts.setSupportedISA(InstructionSet_AVX); } - if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_AES)) + } + if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_AVX2)) + { + // COMPlus_EnableAVX is also used to control the code generation of + // System.Numerics.Vectors and floating-point arithmetics + if (configEnableISA(InstructionSet_AVX) && configEnableISA(InstructionSet_AVX2)) { - if (configEnableISA(InstructionSet_AES)) - { - opts.setSupportedISA(InstructionSet_AES); - } + opts.setSupportedISA(InstructionSet_AVX2); } - if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_AVX)) + } + if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_BMI1)) + { + if (configEnableISA(InstructionSet_BMI1)) { - if (configEnableISA(InstructionSet_AVX)) - { - opts.setSupportedISA(InstructionSet_AVX); - } + opts.setSupportedISA(InstructionSet_BMI1); } - if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_AVX2)) + } + if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_BMI2)) + { + if (configEnableISA(InstructionSet_BMI2)) { - // COMPlus_EnableAVX is also used to control the code generation of - // System.Numerics.Vectors and floating-point arithmetics - if (configEnableISA(InstructionSet_AVX) && configEnableISA(InstructionSet_AVX2)) - { - opts.setSupportedISA(InstructionSet_AVX2); - } + opts.setSupportedISA(InstructionSet_BMI2); } - if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_BMI1)) + } + if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_FMA)) + { + if (configEnableISA(InstructionSet_FMA)) { - if (configEnableISA(InstructionSet_BMI1)) - { - opts.setSupportedISA(InstructionSet_BMI1); - } + opts.setSupportedISA(InstructionSet_FMA); } - if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_BMI2)) + } + if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_LZCNT)) + { + if (configEnableISA(InstructionSet_LZCNT)) { - if (configEnableISA(InstructionSet_BMI2)) - { - opts.setSupportedISA(InstructionSet_BMI2); - } + opts.setSupportedISA(InstructionSet_LZCNT); } - if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_FMA)) + } + if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_PCLMULQDQ)) + { + if (configEnableISA(InstructionSet_PCLMULQDQ)) { - if (configEnableISA(InstructionSet_FMA)) - { - opts.setSupportedISA(InstructionSet_FMA); - } + opts.setSupportedISA(InstructionSet_PCLMULQDQ); } - if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_LZCNT)) + } + if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_POPCNT)) + { + if (configEnableISA(InstructionSet_POPCNT)) { - if (configEnableISA(InstructionSet_LZCNT)) - { - opts.setSupportedISA(InstructionSet_LZCNT); - } + opts.setSupportedISA(InstructionSet_POPCNT); } - if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_PCLMULQDQ)) + } + + // There are currently two sets of flags that control SSE3 through SSE4.2 support + // This is the general EnableSSE3_4 flag and the individual ISA flags. We need to + // check both for any given ISA. + if (JitConfig.EnableSSE3_4()) + { + if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_SSE3)) { - if (configEnableISA(InstructionSet_PCLMULQDQ)) + if (configEnableISA(InstructionSet_SSE3)) { - opts.setSupportedISA(InstructionSet_PCLMULQDQ); + opts.setSupportedISA(InstructionSet_SSE3); } } - if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_POPCNT)) + if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_SSE41)) { - if (configEnableISA(InstructionSet_POPCNT)) + if (configEnableISA(InstructionSet_SSE41)) { - opts.setSupportedISA(InstructionSet_POPCNT); + opts.setSupportedISA(InstructionSet_SSE41); } } - - // There are currently two sets of flags that control SSE3 through SSE4.2 support - // This is the general EnableSSE3_4 flag and the individual ISA flags. We need to - // check both for any given ISA. - if (JitConfig.EnableSSE3_4()) + if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_SSE42)) { - if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_SSE3)) + if (configEnableISA(InstructionSet_SSE42)) { - if (configEnableISA(InstructionSet_SSE3)) - { - opts.setSupportedISA(InstructionSet_SSE3); - } + opts.setSupportedISA(InstructionSet_SSE42); } - if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_SSE41)) - { - if (configEnableISA(InstructionSet_SSE41)) - { - opts.setSupportedISA(InstructionSet_SSE41); - } - } - if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_SSE42)) + } + if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_SSSE3)) + { + if (configEnableISA(InstructionSet_SSSE3)) { - if (configEnableISA(InstructionSet_SSE42)) - { - opts.setSupportedISA(InstructionSet_SSE42); - } - } - if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_SSSE3)) - { - if (configEnableISA(InstructionSet_SSSE3)) - { - opts.setSupportedISA(InstructionSet_SSSE3); - } + opts.setSupportedISA(InstructionSet_SSSE3); } } } @@ -3727,8 +3582,8 @@ void Compiler::compInitOptions(JitFlags* jitFlags) opts.compReloc = jitFlags->IsSet(JitFlags::JIT_FLAG_RELOC); #ifdef DEBUG -#if defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND) - // Whether encoding of absolute addr as PC-rel offset is enabled in RyuJIT +#if defined(_TARGET_XARCH_) + // Whether encoding of absolute addr as PC-rel offset is enabled opts.compEnablePCRelAddr = (JitConfig.EnablePCRelAddr() != 0); #endif #endif // DEBUG @@ -4995,11 +4850,9 @@ void Compiler::compCompile(void** methodCodePtr, ULONG* methodCodeSize, JitFlags } #endif -#ifndef LEGACY_BACKEND // rationalize trees Rationalizer rat(this); // PHASE_RATIONALIZE rat.Run(); -#endif // !LEGACY_BACKEND // Here we do "simple lowering". When the RyuJIT backend works for all // platforms, this will be part of the more general lowering phase. For now, though, we do a separate @@ -5008,12 +4861,6 @@ void Compiler::compCompile(void** methodCodePtr, ULONG* methodCodeSize, JitFlags fgSimpleLowering(); EndPhase(PHASE_SIMPLE_LOWERING); -#ifdef LEGACY_BACKEND - /* Local variable liveness */ - fgLocalVarLiveness(); - EndPhase(PHASE_LCLVARLIVENESS); -#endif // !LEGACY_BACKEND - #ifdef DEBUG fgDebugCheckBBlist(); fgDebugCheckLinks(); @@ -5031,39 +4878,9 @@ void Compiler::compCompile(void** methodCodePtr, ULONG* methodCodeSize, JitFlags codeGen->regSet.rsMaskResvd |= RBM_SAVED_LOCALLOC_SP; } #endif // _TARGET_ARM_ -#if defined(_TARGET_ARMARCH_) && defined(LEGACY_BACKEND) - // Determine whether we need to reserve a register for large lclVar offsets. - // The determination depends heavily on the number of locals, which changes for RyuJIT backend - // due to the introduction of new temps during Rationalizer and Lowering. - // In LEGACY_BACKEND we do that here even though the decision to have a frame pointer or not may - // change during register allocation, changing the computation somewhat. - // In RyuJIT backend we do this after determining the frame type, and before beginning - // register allocation. - if (compRsvdRegCheck(PRE_REGALLOC_FRAME_LAYOUT)) - { - // We reserve R10/IP1 in this case to hold the offsets in load/store instructions - codeGen->regSet.rsMaskResvd |= RBM_OPT_RSVD; - assert(REG_OPT_RSVD != REG_FP); - JITDUMP(" Reserved REG_OPT_RSVD (%s) due to large frame\n", getRegName(REG_OPT_RSVD)); - } - // compRsvdRegCheck() has read out the FramePointerUsed property, but doLinearScan() - // tries to overwrite it later. This violates the PhasedVar rule and triggers an assertion. - // TODO-ARM-Bug?: What is the proper way to handle this situation? - codeGen->resetFramePointerUsedWritePhase(); - -#ifdef DEBUG - // - // Display the pre-regalloc frame offsets that we have tentatively decided upon - // - if (verbose) - lvaTableDump(); -#endif -#endif // _TARGET_ARMARCH_ /* Assign registers to variables, etc. */ - CLANG_FORMAT_COMMENT_ANCHOR; -#ifndef LEGACY_BACKEND /////////////////////////////////////////////////////////////////////////////// // Dominator and reachability sets are no longer valid. They haven't been // maintained up to here, and shouldn't be used (unless recomputed). @@ -5089,15 +4906,6 @@ void Compiler::compCompile(void** methodCodePtr, ULONG* methodCodeSize, JitFlags // Copied from rpPredictRegUse() genFullPtrRegMap = (codeGen->genInterruptible || !codeGen->isFramePointerUsed()); -#else // LEGACY_BACKEND - - lvaTrackedFixed = true; // We cannot add any new tracked variables after this point. - // For the classic JIT32 at this point lvaSortAgain can be set and raAssignVars() will call lvaSortOnly() - - // Now do "classic" register allocation. - raAssignVars(); - EndPhase(PHASE_RA_ASSIGN_VARS); -#endif // LEGACY_BACKEND #ifdef DEBUG fgDebugCheckLinks(); @@ -5698,12 +5506,10 @@ void Compiler::compCompileFinish() !opts.optRepeat && // We need extra memory to repeat opts !compAllocator->bypassHostAllocator() && // ArenaAllocator::getDefaultPageSize() is artificially low for // DirectAlloc + // Factor of 2x is because data-structures are bigger under DEBUG (compAllocator->getTotalBytesAllocated() > (2 * ArenaAllocator::getDefaultPageSize())) && -// Factor of 2x is because data-structures are bigger under DEBUG -#ifndef LEGACY_BACKEND // RyuJIT backend needs memory tuning! TODO-Cleanup: remove this case when memory tuning is complete. (compAllocator->getTotalBytesAllocated() > (10 * ArenaAllocator::getDefaultPageSize())) && -#endif !verbose) // We allocate lots of memory to convert sets to strings for JitDump { genSmallMethodsNeedingExtraMemoryCnt++; @@ -5843,11 +5649,7 @@ void Compiler::compCompileFinish() #endif // FEATURE_ANYCSE } -#ifndef LEGACY_BACKEND printf(" LSRA |"); // TODO-Cleanup: dump some interesting LSRA stat into the order file? -#else // LEGACY_BACKEND - printf("%s%4d p%1d |", (tmpCount > 0) ? "T" : " ", rpStkPredict / BB_UNITY_WEIGHT, rpPasses); -#endif // LEGACY_BACKEND printf(" %4d |", info.compMethodInfo->ILCodeSize); printf(" %5d |", info.compTotalHotCodeSize); printf(" %5d |", info.compTotalColdCodeSize); @@ -7496,106 +7298,6 @@ void Compiler::compCallArgStats() argNonVirtualCalls++; } } - -#ifdef LEGACY_BACKEND - // TODO-Cleaenup: We need to add support below for additional node types that RyuJIT backend has in the - // IR. - // Gather arguments information. - - for (args = call->gtCall.gtCallArgs; args; args = args->gtOp.gtOp2) - { - argx = args->gtOp.gtOp1; - - argNum++; - - switch (genActualType(argx->TypeGet())) - { - case TYP_INT: - case TYP_REF: - case TYP_BYREF: - argDWordNum++; - break; - - case TYP_LONG: - argLngNum++; - break; - - case TYP_FLOAT: - argFltNum++; - break; - - case TYP_DOUBLE: - argDblNum++; - break; - - case TYP_VOID: - /* This is a deferred register argument */ - assert(argx->gtOper == GT_NOP); - assert(argx->gtFlags & GTF_LATE_ARG); - argDWordNum++; - break; - } - - /* Is this argument a register argument? */ - - if (argx->gtFlags & GTF_LATE_ARG) - { - regArgNum++; - - /* We either have a deferred argument or a temp */ - - if (argx->gtOper == GT_NOP) - { - regArgDeferred++; - } - else - { - assert(argx->gtOper == GT_ASG); - regArgTemp++; - } - } - } - - /* Look at the register arguments and count how many constants, local vars */ - - for (args = call->gtCall.gtCallLateArgs; args; args = args->gtOp.gtOp2) - { - argx = args->gtOp.gtOp1; - - switch (argx->gtOper) - { - case GT_CNS_INT: - regArgConst++; - break; - - case GT_LCL_VAR: - regArgLclVar++; - break; - } - } - - assert(argNum == argDWordNum + argLngNum + argFltNum + argDblNum); - assert(regArgNum == regArgDeferred + regArgTemp); - - argTotalArgs += argNum; - argTotalRegArgs += regArgNum; - - argTotalDWordArgs += argDWordNum; - argTotalLongArgs += argLngNum; - argTotalFloatArgs += argFltNum; - argTotalDoubleArgs += argDblNum; - - argTotalDeferred += regArgDeferred; - argTotalTemps += regArgTemp; - argTotalConst += regArgConst; - argTotalLclVar += regArgLclVar; - - argTempsThisMethod += regArgTemp; - - argCntTable.record(argNum); - argDWordCntTable.record(argDWordNum); - argDWordLngCntTable.record(argDWordNum + (2 * argLngNum)); -#endif // LEGACY_BACKEND } } } @@ -9419,12 +9121,6 @@ int cTreeKindsIR(Compiler* comp, GenTree* tree) { chars += printf("[LOGOP]"); } -#ifdef LEGACY_BACKEND - if (kind & GTK_ASGOP) - { - chars += printf("[ASGOP]"); - } -#endif if (kind & GTK_COMMUTE) { chars += printf("[COMMUTE]"); @@ -9646,7 +9342,7 @@ int cTreeFlagsIR(Compiler* comp, GenTree* tree) break; case GT_MUL: -#if !defined(_TARGET_64BIT_) && !defined(LEGACY_BACKEND) +#if !defined(_TARGET_64BIT_) case GT_MUL_LONG: #endif @@ -9678,14 +9374,6 @@ int cTreeFlagsIR(Compiler* comp, GenTree* tree) case GT_MOD: case GT_UMOD: - -#ifdef LEGACY_BACKEND - if (tree->gtFlags & GTF_MOD_INT_RESULT) - { - chars += printf("[MOD_INT_RESULT]"); - } -#endif // LEGACY_BACKEND - break; case GT_EQ: @@ -9873,12 +9561,6 @@ int cTreeFlagsIR(Compiler* comp, GenTree* tree) { chars += printf("[CALL_HOISTABLE]"); } -#ifdef LEGACY_BACKEND - if (tree->gtFlags & GTF_CALL_REG_SAVE) - { - chars += printf("[CALL_REG_SAVE]"); - } -#endif // LEGACY_BACKEND // More flags associated with calls. @@ -9940,12 +9622,10 @@ int cTreeFlagsIR(Compiler* comp, GenTree* tree) { chars += printf("[CALL_M_FRAME_VAR_DEATH]"); } -#ifndef LEGACY_BACKEND if (call->gtCallMoreFlags & GTF_CALL_M_TAILCALL_VIA_HELPER) { chars += printf("[CALL_M_TAILCALL_VIA_HELPER]"); } -#endif #if FEATURE_TAILCALL_OPT if (call->gtCallMoreFlags & GTF_CALL_M_IMPLICIT_TAILCALL) { @@ -9999,10 +9679,6 @@ int cTreeFlagsIR(Compiler* comp, GenTree* tree) case GT_CAST: case GT_ADD: case GT_SUB: -#ifdef LEGACY_BACKEND - case GT_ASG_ADD: - case GT_ASG_SUB: -#endif if (tree->gtFlags & GTF_OVERFLOW) { chars += printf("[OVERFLOW]"); @@ -10034,20 +9710,6 @@ int cTreeFlagsIR(Compiler* comp, GenTree* tree) { chars += printf("[SPILLED_OPER]"); } -#if defined(LEGACY_BACKEND) - if (tree->InReg()) - { - chars += printf("[REG_VAL]"); - } - if (tree->gtFlags & GTF_SPILLED_OP2) - { - chars += printf("[SPILLED_OP2]"); - } - if (tree->gtFlags & GTF_ZSF_SET) - { - chars += printf("[ZSF_SET]"); - } -#endif #if FEATURE_SET_FLAGS if (tree->gtFlags & GTF_SET_FLAGS) { @@ -10076,12 +9738,6 @@ int cTreeFlagsIR(Compiler* comp, GenTree* tree) { chars += printf("[BOOLEAN]"); } -#if CPU_HAS_BYTE_REGS && defined(LEGACY_BACKEND) - if (tree->gtFlags & GTF_SMALL_OK) - { - chars += printf("[SMALL_OK]"); - } -#endif if (tree->gtFlags & GTF_UNSIGNED) { chars += printf("[SMALL_UNSIGNED]"); @@ -10260,16 +9916,7 @@ int cLeafIR(Compiler* comp, GenTree* tree) { if (varDsc->lvRegister) { - if (isRegPairType(varDsc->TypeGet())) - { - chars += printf(":%s:%s", - getRegName(varDsc->lvOtherReg), // hi32 - getRegName(varDsc->lvRegNum)); // lo32 - } - else - { - chars += printf(":%s", getRegName(varDsc->lvRegNum)); - } + chars += printf(":%s", getRegName(varDsc->lvRegNum)); } else { @@ -10278,11 +9925,6 @@ int cLeafIR(Compiler* comp, GenTree* tree) case GenTree::GT_REGTAG_REG: chars += printf(":%s", comp->compRegVarName(tree->gtRegNum)); break; -#if CPU_LONG_USES_REGPAIR - case GenTree::GT_REGTAG_REGPAIR: - chars += printf(":%s", comp->compRegPairName(tree->gtRegPair)); - break; -#endif default: break; } @@ -10294,18 +9936,7 @@ int cLeafIR(Compiler* comp, GenTree* tree) { if (varDsc->lvRegister) { - chars += printf("("); - if (isRegPairType(varDsc->TypeGet())) - { - chars += printf("%s:%s", - getRegName(varDsc->lvOtherReg), // hi32 - getRegName(varDsc->lvRegNum)); // lo32 - } - else - { - chars += printf("%s", getRegName(varDsc->lvRegNum)); - } - chars += printf(")"); + chars += printf("(%s)", getRegName(varDsc->lvRegNum)); } else { @@ -10314,11 +9945,6 @@ int cLeafIR(Compiler* comp, GenTree* tree) case GenTree::GT_REGTAG_REG: chars += printf("(%s)", comp->compRegVarName(tree->gtRegNum)); break; -#if CPU_LONG_USES_REGPAIR - case GenTree::GT_REGTAG_REGPAIR: - chars += printf("(%s)", comp->compRegPairName(tree->gtRegPair)); - break; -#endif default: break; } @@ -10367,16 +9993,7 @@ int cLeafIR(Compiler* comp, GenTree* tree) { if (varDsc->lvRegister) { - if (isRegPairType(varDsc->TypeGet())) - { - chars += printf(":%s:%s", - getRegName(varDsc->lvOtherReg), // hi32 - getRegName(varDsc->lvRegNum)); // lo32 - } - else - { - chars += printf(":%s", getRegName(varDsc->lvRegNum)); - } + chars += printf(":%s", getRegName(varDsc->lvRegNum)); } else { @@ -10385,11 +10002,6 @@ int cLeafIR(Compiler* comp, GenTree* tree) case GenTree::GT_REGTAG_REG: chars += printf(":%s", comp->compRegVarName(tree->gtRegNum)); break; -#if CPU_LONG_USES_REGPAIR - case GenTree::GT_REGTAG_REGPAIR: - chars += printf(":%s", comp->compRegPairName(tree->gtRegPair)); - break; -#endif default: break; } @@ -10401,18 +10013,7 @@ int cLeafIR(Compiler* comp, GenTree* tree) { if (varDsc->lvRegister) { - chars += printf("("); - if (isRegPairType(varDsc->TypeGet())) - { - chars += printf("%s:%s", - getRegName(varDsc->lvOtherReg), // hi32 - getRegName(varDsc->lvRegNum)); // lo32 - } - else - { - chars += printf("%s", getRegName(varDsc->lvRegNum)); - } - chars += printf(")"); + chars += printf("(%s)", getRegName(varDsc->lvRegNum)); } else { @@ -10421,11 +10022,6 @@ int cLeafIR(Compiler* comp, GenTree* tree) case GenTree::GT_REGTAG_REG: chars += printf("(%s)", comp->compRegVarName(tree->gtRegNum)); break; -#if CPU_LONG_USES_REGPAIR - case GenTree::GT_REGTAG_REGPAIR: - chars += printf("(%s)", comp->compRegPairName(tree->gtRegPair)); - break; -#endif default: break; } @@ -10610,9 +10206,7 @@ int cLeafIR(Compiler* comp, GenTree* tree) case GT_MEMORYBARRIER: case GT_ARGPLACE: case GT_PINVOKE_PROLOG: -#ifndef LEGACY_BACKEND case GT_JMPTABLE: -#endif // Do nothing. break; diff --git a/src/jit/compiler.h b/src/jit/compiler.h index 81148e9d7a..c3634a365f 100644 --- a/src/jit/compiler.h +++ b/src/jit/compiler.h @@ -39,7 +39,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #include "blockset.h" #include "arraystack.h" #include "hashbv.h" -#include "fp.h" #include "jitexpandarray.h" #include "tinyarray.h" #include "valuenum.h" @@ -76,9 +75,6 @@ class emitter; // defined in emit.h struct ShadowParamVarInfo; // defined in GSChecks.cpp struct InitVarDscInfo; // defined in register_arg_convention.h class FgStack; // defined in flowgraph.cpp -#if FEATURE_STACK_FP_X87 -struct FlatFPStateX87; // defined in fp.h -#endif #if FEATURE_ANYCSE class CSE_DataFlow; // defined in OptCSE.cpp #endif @@ -86,9 +82,7 @@ class CSE_DataFlow; // defined in OptCSE.cpp struct IndentStack; #endif -#ifndef LEGACY_BACKEND class Lowering; // defined in lower.h -#endif // The following are defined in this file, Compiler.h @@ -215,12 +209,9 @@ public: unsigned char lvStructGcCount : 3; // if struct, how many GC pointer (stop counting at 7). The only use of values >1 // is to help determine whether to use block init in the prolog. unsigned char lvOnFrame : 1; // (part of) the variable lives on the frame -#ifdef LEGACY_BACKEND - unsigned char lvDependReg : 1; // did the predictor depend upon this being enregistered -#endif - unsigned char lvRegister : 1; // assigned to live in a register? For RyuJIT backend, this is only set if the - // variable is in the same register for the entire function. - unsigned char lvTracked : 1; // is this a tracked variable? + unsigned char lvRegister : 1; // assigned to live in a register? For RyuJIT backend, this is only set if the + // variable is in the same register for the entire function. + unsigned char lvTracked : 1; // is this a tracked variable? bool lvTrackedNonStruct() { return lvTracked && lvType != TYP_STRUCT; @@ -249,10 +240,7 @@ public: unsigned char lvLclBlockOpAddr : 1; // The variable was written to via a block operation that took its address. unsigned char lvLiveAcrossUCall : 1; // The variable is live across an unmanaged call. #endif - unsigned char lvIsCSE : 1; // Indicates if this LclVar is a CSE variable. -#ifdef LEGACY_BACKEND - unsigned char lvRefAssign : 1; // involved in pointer assignment -#endif + unsigned char lvIsCSE : 1; // Indicates if this LclVar is a CSE variable. unsigned char lvHasLdAddrOp : 1; // has ldloca or ldarga opcode on this local. unsigned char lvStackByref : 1; // This is a compiler temporary of TYP_BYREF that is known to point into our local // stack frame. @@ -271,9 +259,6 @@ public: unsigned char lvVolatileHint : 1; // hint for AssertionProp #endif -#ifdef LEGACY_BACKEND - unsigned char lvSpilled : 1; // enregistered variable was spilled -#endif #ifndef _TARGET_64BIT_ unsigned char lvStructDoubleAlign : 1; // Must we double align this struct? #endif // !_TARGET_64BIT_ @@ -314,9 +299,8 @@ public: // I.e. there is no longer any reference to the struct directly. // In this case we can simply remove this struct local. #endif -#ifndef LEGACY_BACKEND + unsigned char lvLRACandidate : 1; // Tracked for linear scan register allocation purposes -#endif // !LEGACY_BACKEND #ifdef FEATURE_SIMD // Note that both SIMD vector args and locals are marked as lvSIMDType = true, but the @@ -445,9 +429,8 @@ public: private: regNumberSmall _lvRegNum; // Used to store the register this variable is in (or, the low register of a - // register pair). For LEGACY_BACKEND, this is only set if lvRegister is - // non-zero. For non-LEGACY_BACKEND, it is set during codegen any time the - // variable is enregistered (in non-LEGACY_BACKEND, lvRegister is only set + // register pair). It is set during codegen any time the + // variable is enregistered (lvRegister is only set // to non-zero if the variable gets the same register assignment for its entire // lifetime). #if !defined(_TARGET_64BIT_) @@ -461,12 +444,7 @@ private: // Note this is defined but not used by ARM32 #endif // FEATURE_MULTIREG_ARGS -#ifndef LEGACY_BACKEND - union { - regNumberSmall _lvArgInitReg; // the register into which the argument is moved at entry - regPairNoSmall _lvArgInitRegPair; // the register pair into which the argument is moved at entry - }; -#endif // !LEGACY_BACKEND + regNumberSmall _lvArgInitReg; // the register into which the argument is moved at entry public: // The register number is stored in a small format (8 bits), but the getters return and the setters take @@ -573,9 +551,8 @@ public: } #endif -///////////////////// + ///////////////////// -#ifndef LEGACY_BACKEND __declspec(property(get = GetArgInitReg, put = SetArgInitReg)) regNumber lvArgInitReg; regNumber GetArgInitReg() const @@ -591,24 +568,6 @@ public: ///////////////////// - __declspec(property(get = GetArgInitRegPair, put = SetArgInitRegPair)) regPairNo lvArgInitRegPair; - - regPairNo GetArgInitRegPair() const - { - regPairNo regPair = (regPairNo)_lvArgInitRegPair; - assert(regPair >= REG_PAIR_FIRST && regPair <= REG_PAIR_LAST); - return regPair; - } - - void SetArgInitRegPair(regPairNo regPair) - { - assert(regPair >= REG_PAIR_FIRST && regPair <= REG_PAIR_LAST); - _lvArgInitRegPair = (regPairNoSmall)regPair; - assert(_lvArgInitRegPair == regPair); - } - - ///////////////////// - bool lvIsRegCandidate() const { return lvLRACandidate != 0; @@ -619,20 +578,6 @@ public: return lvIsRegCandidate() && (lvRegNum != REG_STK); } -#else // LEGACY_BACKEND - - bool lvIsRegCandidate() const - { - return lvTracked != 0; - } - - bool lvIsInReg() const - { - return lvRegister != 0; - } - -#endif // LEGACY_BACKEND - regMaskTP lvRegMask() const { regMaskTP regMask = RBM_NONE; @@ -649,12 +594,6 @@ public: { regMask = genRegMask(lvRegNum); } - - // For longs we may have two regs - if (isRegPairType(lvType) && lvOtherReg != REG_STK) - { - regMask |= genRegMask(lvOtherReg); - } } return regMask; } @@ -777,9 +716,7 @@ public: lvSetHfaTypeIsFloat(type == TYP_FLOAT); } -#ifndef LEGACY_BACKEND var_types lvaArgType(); -#endif PerSsaArray lvPerSsaData; @@ -804,15 +741,7 @@ public: public: void PrintVarReg() const { - if (isRegPairType(TypeGet())) - { - printf("%s:%s", getRegName(lvOtherReg), // hi32 - getRegName(lvRegNum)); // lo32 - } - else - { - printf("%s", getRegName(lvRegNum)); - } + printf("%s", getRegName(lvRegNum)); } #endif // DEBUG @@ -2193,18 +2122,11 @@ public: void gtPrepareCost(GenTree* tree); bool gtIsLikelyRegVar(GenTree* tree); - unsigned gtSetEvalOrderAndRestoreFPstkLevel(GenTree* tree); - // Returns true iff the secondNode can be swapped with firstNode. bool gtCanSwapOrder(GenTree* firstNode, GenTree* secondNode); unsigned gtSetEvalOrder(GenTree* tree); -#if FEATURE_STACK_FP_X87 - bool gtFPstLvlRedo; - void gtComputeFPlvls(GenTree* tree); -#endif // FEATURE_STACK_FP_X87 - void gtSetStmtInfo(GenTree* stmt); // Returns "true" iff "node" has any of the side effects in "flags". @@ -2458,13 +2380,6 @@ public: // reverse map of tracked number to var number unsigned lvaTrackedToVarNum[lclMAX_TRACKED]; -#ifdef LEGACY_BACKEND - // variable interference graph - VARSET_TP lvaVarIntf[lclMAX_TRACKED]; - - // variable preference graph - VARSET_TP lvaVarPref[lclMAX_TRACKED]; -#endif #if DOUBLE_ALIGN #ifdef DEBUG // # of procs compiled a with double-aligned stack @@ -2491,7 +2406,7 @@ public: DNER_DepField, // It is a field of a dependently promoted struct DNER_NoRegVars, // opts.compFlags & CLFLG_REGVAR is not set DNER_MinOptsGC, // It is a GC Ref and we are compiling MinOpts -#if !defined(LEGACY_BACKEND) && !defined(_TARGET_64BIT_) +#if !defined(_TARGET_64BIT_) DNER_LongParamField, // It is a decomposed field of a long parameter. #endif #ifdef JIT32_GCENCODER @@ -2581,11 +2496,7 @@ public: #endif // _TARGET_ARM_ void lvaAssignFrameOffsets(FrameLayoutState curState); void lvaFixVirtualFrameOffsets(); - -#ifndef LEGACY_BACKEND void lvaUpdateArgsWithInitialReg(); -#endif // !LEGACY_BACKEND - void lvaAssignVirtualFrameOffsetsToArgs(); #ifdef UNIX_AMD64_ABI int lvaAssignVirtualFrameOffsetToArg(unsigned lclNum, unsigned argSize, int argOffs, int* callerArgOffset); @@ -3836,11 +3747,7 @@ public: bool fgFoldConditional(BasicBlock* block); -#ifdef LEGACY_BACKEND - void fgMorphStmts(BasicBlock* block, bool* mult, bool* lnot, bool* loadw); -#else void fgMorphStmts(BasicBlock* block, bool* lnot, bool* loadw); -#endif void fgMorphBlocks(); bool fgMorphBlockStmt(BasicBlock* block, GenTreeStmt* stmt DEBUGARG(const char* msg)); @@ -3879,10 +3786,6 @@ public: // lowering that is distributed between fgMorph and the lowering phase of LSRA. void fgSimpleLowering(); -#ifdef LEGACY_BACKEND - bool fgShouldCreateAssignOp(GenTree* tree, bool* bReverse); -#endif - GenTree* fgInitThisClass(); GenTreeCall* fgGetStaticsCCtorHelper(CORINFO_CLASS_HANDLE cls, CorInfoHelpFunc helper); @@ -3891,22 +3794,14 @@ public: inline bool backendRequiresLocalVarLifetimes() { -#if defined(LEGACY_BACKEND) - return true; -#else return !opts.MinOpts() || m_pLinearScan->willEnregisterLocalVars(); -#endif } void fgLocalVarLiveness(); void fgLocalVarLivenessInit(); -#ifdef LEGACY_BACKEND - GenTree* fgLegacyPerStatementLocalVarLiveness(GenTree* startNode, GenTree* relopNode); -#else void fgPerNodeLocalVarLiveness(GenTree* node); -#endif void fgPerBlockLocalVarLiveness(); VARSET_VALRET_TP fgGetHandlerLiveVars(BasicBlock* block); @@ -3918,12 +3813,6 @@ public: // at each call. VARSET_TP fgMarkIntfUnionVS; - bool fgMarkIntf(VARSET_VALARG_TP varSet); - - bool fgMarkIntf(VARSET_VALARG_TP varSet1, VARSET_VALARG_TP varSet2); - - bool fgMarkIntf(VARSET_VALARG_TP varSet1, unsigned varIndex); - void fgUpdateRefCntForClone(BasicBlock* addedToBlock, GenTree* clonedTree); void fgUpdateRefCntForExtract(GenTree* wholeTree, GenTree* keptTree); @@ -3938,9 +3827,8 @@ public: void fgComputeLifeUntrackedLocal(VARSET_TP& life, VARSET_VALARG_TP keepAliveVars, LclVarDsc& varDsc, - GenTreeLclVarCommon* lclVarNode, - GenTree* node); - bool fgComputeLifeLocal(VARSET_TP& life, VARSET_VALARG_TP keepAliveVars, GenTree* lclVarNode, GenTree* node); + GenTreeLclVarCommon* lclVarNode); + bool fgComputeLifeLocal(VARSET_TP& life, VARSET_VALARG_TP keepAliveVars, GenTree* lclVarNode); void fgComputeLife(VARSET_TP& life, GenTree* startNode, @@ -4580,15 +4468,6 @@ public: void fgDebugCheckTryFinallyExits(); #endif -#ifdef LEGACY_BACKEND - static void fgOrderBlockOps(GenTree* tree, - regMaskTP reg0, - regMaskTP reg1, - regMaskTP reg2, - GenTree** opsPtr, // OUT - regMaskTP* regsPtr); // OUT -#endif // LEGACY_BACKEND - static GenTree* fgGetFirstNode(GenTree* tree); static bool fgTreeIsInStmt(GenTree* tree, GenTreeStmt* stmt); void fgTraverseRPO(); @@ -4818,8 +4697,6 @@ private: GenTree* fgMorphStackArgForVarArgs(unsigned lclNum, var_types varType, unsigned lclOffs); - bool fgMorphRelopToQmark(GenTree* tree); - // A "MorphAddrContext" carries information from the surrounding context. If we are evaluating a byref address, // it is useful to know whether the address will be immediately dereferenced, or whether the address value will // be used, perhaps by passing it as an argument to a called method. This affects how null checking is done: @@ -5095,14 +4972,6 @@ private: bool fgIsBigOffset(size_t offset); -#if defined(LEGACY_BACKEND) - // The following are used when morphing special cases of integer div/mod operations and also by codegen - bool fgIsSignedDivOptimizable(GenTree* divisor); - bool fgIsUnsignedDivOptimizable(GenTree* divisor); - bool fgIsSignedModOptimizable(GenTree* divisor); - bool fgIsUnsignedModOptimizable(GenTree* divisor); -#endif // LEGACY_BACKEND - bool fgNeedReturnSpillTemp(); /* @@ -5590,13 +5459,6 @@ protected: bool unsignedTest, bool dupCond, unsigned* iterCount); -#if FEATURE_STACK_FP_X87 - -public: - VARSET_TP optAllFloatVars; // mask of all tracked FP variables - VARSET_TP optAllFPregVars; // mask of all enregistered FP variables - VARSET_TP optAllNonFPvars; // mask of all tracked non-FP variables -#endif // FEATURE_STACK_FP_X87 private: static fgWalkPreFn optIsVarAssgCB; @@ -6357,25 +6219,6 @@ protected: */ public: -#ifndef LEGACY_BACKEND - bool doLSRA() const - { - return true; - } -#else // LEGACY_BACKEND - bool doLSRA() const - { - return false; - } -#endif // LEGACY_BACKEND - -#ifdef LEGACY_BACKEND - void raInit(); - void raAssignVars(); // register allocation -#endif // LEGACY_BACKEND - - VARSET_TP raRegVarsMask; // Set of all enregistered variables (not including FEATURE_STACK_FP_X87 enregistered - // variables) regNumber raUpdateRegStateForArg(RegState* regState, LclVarDsc* argDsc); void raMarkStkVars(); @@ -6386,115 +6229,11 @@ protected: FrameType rpFrameType; bool rpMustCreateEBPCalled; // Set to true after we have called rpMustCreateEBPFrame once -#ifdef LEGACY_BACKEND - regMaskTP rpMaskPInvokeEpilogIntf; // pinvoke epilog trashes esi/edi holding stack args needed to setup tail call's - // args -#endif // LEGACY_BACKEND - bool rpMustCreateEBPFrame(INDEBUG(const char** wbReason)); -#if FEATURE_FP_REGALLOC - enum enumConfigRegisterFP - { - CONFIG_REGISTER_FP_NONE = 0x0, - CONFIG_REGISTER_FP_CALLEE_TRASH = 0x1, - CONFIG_REGISTER_FP_CALLEE_SAVED = 0x2, - CONFIG_REGISTER_FP_FULL = 0x3, - }; - enumConfigRegisterFP raConfigRegisterFP(); -#endif // FEATURE_FP_REGALLOC - -public: - regMaskTP raConfigRestrictMaskFP(); - private: -#ifndef LEGACY_BACKEND Lowering* m_pLowering; // Lowering; needed to Lower IR that's added or modified after Lowering. LinearScanInterface* m_pLinearScan; // Linear Scan allocator -#else // LEGACY_BACKEND - unsigned raAvoidArgRegMask; // Mask of incoming argument registers that we may need to avoid - VARSET_TP raLclRegIntf[REG_COUNT]; // variable to register interference graph - bool raNewBlocks; // True is we added killing blocks for FPU registers - unsigned rpPasses; // Number of passes made by the register predicter - unsigned rpPassesMax; // Maximum number of passes made by the register predicter - unsigned rpPassesPessimize; // Number of passes non-pessimizing made by the register predicter - unsigned rpStkPredict; // Weighted count of variables were predicted STK (lower means register allocation is better) - unsigned rpPredictSpillCnt; // Predicted number of integer spill tmps for the current tree - regMaskTP rpPredictAssignMask; // Mask of registers to consider in rpPredictAssignRegVars() - VARSET_TP rpLastUseVars; // Set of last use variables in rpPredictTreeRegUse - VARSET_TP rpUseInPlace; // Set of variables that we used in place - int rpAsgVarNum; // VarNum for the target of GT_ASG node - bool rpPredictAssignAgain; // Must rerun the rpPredictAssignRegVars() - bool rpAddedVarIntf; // Set to true if we need to add a new var intf - bool rpLostEnreg; // Set to true if we lost an enregister var that had lvDependReg set - bool rpReverseEBPenreg; // Decided to reverse the enregistration of EBP -public: - bool rpRegAllocDone; // Set to true after we have completed register allocation -private: - regMaskTP rpPredictMap[PREDICT_COUNT]; // Holds the regMaskTP for each of the enum values - - void raSetupArgMasks(RegState* r); - - const regNumber* raGetRegVarOrder(var_types regType, unsigned* wbVarOrderSize); -#ifdef DEBUG - void raDumpVarIntf(); // Dump the variable to variable interference graph - void raDumpRegIntf(); // Dump the variable to register interference graph -#endif - void raAdjustVarIntf(); - - regMaskTP rpPredictRegMask(rpPredictReg predictReg, var_types type); - - bool rpRecordRegIntf(regMaskTP regMask, VARSET_VALARG_TP life DEBUGARG(const char* msg)); - - bool rpRecordVarIntf(unsigned varNum, VARSET_VALARG_TP intfVar DEBUGARG(const char* msg)); - regMaskTP rpPredictRegPick(var_types type, rpPredictReg predictReg, regMaskTP lockedRegs); - - regMaskTP rpPredictGrabReg(var_types type, rpPredictReg predictReg, regMaskTP lockedRegs); - - static fgWalkPreFn rpMarkRegIntf; - - regMaskTP rpPredictAddressMode( - GenTree* tree, var_types type, regMaskTP lockedRegs, regMaskTP rsvdRegs, GenTree* lenCSE); - - void rpPredictRefAssign(unsigned lclNum); - - regMaskTP rpPredictBlkAsgRegUse(GenTree* tree, rpPredictReg predictReg, regMaskTP lockedRegs, regMaskTP rsvdRegs); - - regMaskTP rpPredictTreeRegUse(GenTree* tree, rpPredictReg predictReg, regMaskTP lockedRegs, regMaskTP rsvdRegs); - - regMaskTP rpPredictAssignRegVars(regMaskTP regAvail); - - void rpPredictRegUse(); // Entry point - - unsigned raPredictTreeRegUse(GenTree* tree); - unsigned raPredictListRegUse(GenTree* list); - - void raSetRegVarOrder(var_types regType, - regNumber* customVarOrder, - unsigned* customVarOrderSize, - regMaskTP prefReg, - regMaskTP avoidReg); - - // We use (unsigned)-1 as an uninitialized sentinel for rpStkPredict and - // also as the maximum value of lvRefCntWtd. Don't allow overflow, and - // saturate at UINT_MAX - 1, to avoid using the sentinel. - void raAddToStkPredict(unsigned val) - { - unsigned newStkPredict = rpStkPredict + val; - if ((newStkPredict < rpStkPredict) || (newStkPredict == UINT_MAX)) - rpStkPredict = UINT_MAX - 1; - else - rpStkPredict = newStkPredict; - } - -#ifdef DEBUG -#if !FEATURE_FP_REGALLOC - void raDispFPlifeInfo(); -#endif -#endif - - regMaskTP genReturnRegForTree(GenTree* tree); -#endif // LEGACY_BACKEND /* raIsVarargsStackArg is called by raMaskStkVars and by lvaSortByRefCount. It identifies the special case @@ -6522,23 +6261,6 @@ private: #endif // _TARGET_X86_ } -#ifdef LEGACY_BACKEND - // Records the current prediction, if it's better than any previous recorded prediction. - void rpRecordPrediction(); - // Applies the best recorded prediction, if one exists and is better than the current prediction. - void rpUseRecordedPredictionIfBetter(); - - // Data members used in the methods above. - unsigned rpBestRecordedStkPredict; - struct VarRegPrediction - { - bool m_isEnregistered; - regNumberSmall m_regNum; - regNumberSmall m_otherReg; - }; - VarRegPrediction* rpBestRecordedPrediction; -#endif // LEGACY_BACKEND - /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX @@ -6835,16 +6557,6 @@ public: #else #error Unsupported or unset target architecture #endif - -#ifdef LEGACY_BACKEND -#if defined(_TARGET_X86_) - predict = PREDICT_REG_EAX; -#elif defined(_TARGET_ARM_) - predict = PREDICT_REG_R4; -#else -#error Unsupported or unset target architecture -#endif -#endif // LEGACY_BACKEND } regNumber GetReg() const @@ -6857,20 +6569,9 @@ public: return regMask; } -#ifdef LEGACY_BACKEND - rpPredictReg GetPredict() const - { - return predict; - } -#endif - private: regNumber reg; _regMask_enum regMask; - -#ifdef LEGACY_BACKEND - rpPredictReg predict; -#endif }; VirtualStubParamInfo* virtualStubParamInfo; @@ -7033,16 +6734,9 @@ public: bool tmpAllFree() const; #endif // DEBUG -#ifndef LEGACY_BACKEND void tmpPreAllocateTemps(var_types type, unsigned count); -#endif // !LEGACY_BACKEND protected: -#ifdef LEGACY_BACKEND - unsigned tmpIntSpillMax; // number of int-sized spill temps - unsigned tmpDoubleSpillMax; // number of double-sized spill temps -#endif // LEGACY_BACKEND - unsigned tmpCount; // Number of temps unsigned tmpSize; // Size of all the temps #ifdef DEBUG @@ -7211,19 +6905,6 @@ public: compChangeLife(newLife); } -#ifdef LEGACY_BACKEND - - template - void compUpdateLife(GenTree* tree); - - // Updates "compCurLife" to its state after evaluate of "true". If "pLastUseVars" is - // non-null, sets "*pLastUseVars" to the set of tracked variables for which "tree" was a last - // use. (Can be more than one var in the case of dependently promoted struct vars.) - template - void compUpdateLifeVar(GenTree* tree, VARSET_TP* pLastUseVars = nullptr); - -#endif // LEGACY_BACKEND - template inline void compUpdateLife(VARSET_VALARG_TP newLife); @@ -7406,7 +7087,7 @@ private: // Get highest available level for SIMD codegen SIMDLevel getSIMDSupportLevel() { -#if defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND) +#if defined(_TARGET_XARCH_) if (compSupports(InstructionSet_AVX2)) { return SIMD_AVX2_Supported; @@ -7421,7 +7102,6 @@ private: } // min bar is SSE2 - assert(canUseSSE2()); return SIMD_SSE2_Supported; #else assert(!"Available instruction set(s) for SIMD codegen is not defined for target arch"); @@ -7720,7 +7400,8 @@ private: // Creates a GT_SIMD tree for Abs intrinsic. GenTree* impSIMDAbs(CORINFO_CLASS_HANDLE typeHnd, var_types baseType, unsigned simdVectorSize, GenTree* op1); -#if defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND) +#if defined(_TARGET_XARCH_) + // Transforms operands and returns the SIMD intrinsic to be applied on // transformed operands to obtain == comparison result. SIMDIntrinsicID impSIMDLongRelOpEqual(CORINFO_CLASS_HANDLE typeHnd, @@ -7747,7 +7428,8 @@ private: // and small int base type vectors. SIMDIntrinsicID impSIMDIntegralRelOpGreaterThanOrEqual( CORINFO_CLASS_HANDLE typeHnd, unsigned simdVectorSize, var_types baseType, GenTree** op1, GenTree** op2); -#endif // defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND) + +#endif // defined(_TARGET_XARCH_) void setLclRelatedToSIMDIntrinsic(GenTree* tree); bool areFieldsContiguous(GenTree* op1, GenTree* op2); @@ -7784,7 +7466,7 @@ private: // This is the maximum SIMD type supported for this target. var_types getSIMDVectorType() { -#if defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND) +#if defined(_TARGET_XARCH_) if (getSIMDSupportLevel() == SIMD_AVX2_Supported) { return TYP_SIMD32; @@ -7823,7 +7505,7 @@ private: // Note - cannot be used for System.Runtime.Intrinsic unsigned getSIMDVectorRegisterByteLength() { -#if defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND) +#if defined(_TARGET_XARCH_) if (getSIMDSupportLevel() == SIMD_AVX2_Supported) { return YMM_REGSIZE_BYTES; @@ -7977,16 +7659,6 @@ private: return false; } - // Whether SSE and SSE2 is available - bool canUseSSE2() const - { -#ifdef _TARGET_XARCH_ - return opts.compCanUseSSE2; -#else - return false; -#endif - } - bool compSupports(InstructionSet isa) const { #if defined(_TARGET_XARCH_) || defined(_TARGET_ARM64_) @@ -8038,11 +7710,6 @@ public: // NOTE: These values are only reliable after // the importing is completely finished. -#ifdef LEGACY_BACKEND - JitExpandArrayStack* compQMarks; // The set of QMark nodes created in the current compilation, so - // we can iterate over these efficiently. -#endif - #if CPU_USES_BLOCK_MOVE bool compBlkOpUsed; // Does the method do a COPYBLK or INITBLK #endif @@ -8064,9 +7731,7 @@ public: #if STACK_PROBES bool compStackProbePrologDone; #endif -#ifndef LEGACY_BACKEND bool compLSRADone; -#endif // !LEGACY_BACKEND bool compRationalIRForm; bool compUsesThrowHelper; // There is a call to a THOROW_HELPER for the compiled method. @@ -8110,9 +7775,6 @@ public: bool compUseFCOMI; bool compUseCMOV; -#ifdef _TARGET_XARCH_ - bool compCanUseSSE2; // Allow CodeGen to use "movq XMM" instructions -#endif // _TARGET_XARCH_ #if defined(_TARGET_XARCH_) || defined(_TARGET_ARM64_) uint64_t compSupportsISA; @@ -8259,7 +7921,7 @@ public: bool compReloc; // Generate relocs for pointers in code, true for all ngen/prejit codegen #ifdef DEBUG -#if defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND) +#if defined(_TARGET_XARCH_) bool compEnablePCRelAddr; // Whether absolute addr be encoded as PC-rel offset by RyuJIT where possible #endif #endif // DEBUG @@ -8726,7 +8388,7 @@ public: // In case of Amd64 this doesn't include float regs saved on stack. unsigned compCalleeRegsPushed; -#if defined(_TARGET_XARCH_) && !FEATURE_STACK_FP_X87 +#if defined(_TARGET_XARCH_) // Mask of callee saved float regs on stack. regMaskTP compCalleeFPRegsSavedMask; #endif @@ -8910,7 +8572,6 @@ public: const char* compLocalVarName(unsigned varNum, unsigned offs); VarName compVarName(regNumber reg, bool isFloatReg = false); const char* compRegVarName(regNumber reg, bool displayVar = false, bool isFloatReg = false); - const char* compRegPairName(regPairNo regPair); const char* compRegNameForSize(regNumber reg, size_t size); const char* compFPregVarName(unsigned fpReg, bool displayVar = false); void compDspSrcLinesByNativeIP(UNATIVE_OFFSET curIP); @@ -9253,82 +8914,8 @@ public: void verVerifyThisPtrInitialised(); BOOL verIsCallToInitThisPtr(CORINFO_CLASS_HANDLE context, CORINFO_CLASS_HANDLE target); - // Register allocator - void raInitStackFP(); - void raEnregisterVarsPrePassStackFP(); - void raSetRegLclBirthDeath(GenTree* tree, VARSET_VALARG_TP lastlife, bool fromLDOBJ); - void raEnregisterVarsPostPassStackFP(); - void raGenerateFPRefCounts(); - void raEnregisterVarsStackFP(); - void raUpdateHeightsForVarsStackFP(VARSET_VALARG_TP mask); - - regNumber raRegForVarStackFP(unsigned varTrackedIndex); - void raAddPayloadStackFP(VARSET_VALARG_TP mask, unsigned weight); - - // returns true if enregistering v1 would save more mem accesses than v2 - bool raVarIsGreaterValueStackFP(LclVarDsc* lv1, LclVarDsc* lv2); - #ifdef DEBUG - void raDumpHeightsStackFP(); - void raDumpVariableRegIntfFloat(); -#endif -#if FEATURE_STACK_FP_X87 - - // Currently, we use FP transition blocks in only 2 situations: - // - // -conditional jump on longs where FP stack differs with target: it's not strictly - // necessary, but its low frequency and the code would get complicated if we try to - // inline the FP stack adjustment, as we have a lot of special casing going on to try - // minimize the way we generate the jump code. - // -case statements of switch where the FP stack differs with the one of evaluating the switch () statement - // We do this as we want to codegen switch as a jumptable. Again, this is low frequency. - // - // However, transition blocks have 2 problems - // - // - Procedure splitting: current implementation of procedure splitting requires all basic blocks to - // be known at codegen time, as it generates all hot blocks first and cold blocks later. This ties - // us up in codegen and is a solvable problem (we could make procedure splitting generate blocks - // in the right place without preordering them), this causes us to have to generate the transition - // blocks in the cold area if we want procedure splitting. - // - // - // - Thread abort exceptions and transition blocks. Transition blocks were designed under the assumption - // that no exceptions can happen inside them. Unfortunately Thread.Abort can happen in any instruction, - // and if we have handlers we will have to try to call them. Fixing this the right way would imply - // having multiple try native code regions for a single try il region. This is doable and shouldnt be - // a big change in the exception. - // - // Given the low frequency of the cases where we have transition blocks, I've decided to dumb down - // optimizations. For these 2 cases: - // - // - When there is a chance that we will have FP transition blocks, we won't do procedure splitting. - // - When a method has a handler, it won't enregister any FP variables that go thru a conditional long or - // a switch statement. - // - // If at any point we find we need to optimize this, we should throw work at unblocking the restrictions our - // current procedure splitting and exception code have. - bool compMayHaveTransitionBlocks; - - VARSET_TP raMaskDontEnregFloat; // mask for additional restrictions - - VARSET_TP raLclRegIntfFloat[REG_FPCOUNT]; - - unsigned raCntStkStackFP; - unsigned raCntWtdStkDblStackFP; - unsigned raCntStkParamDblStackFP; - - // Payload in mem accesses for enregistering a variable (we dont want to mix with refcounts) - // TODO: Do we want to put this in LclVarDsc? - unsigned raPayloadStackFP[lclMAX_TRACKED]; - unsigned raHeightsStackFP[lclMAX_TRACKED][FP_VIRTUALREGISTERS + 1]; -#ifdef DEBUG - // Useful for debugging - unsigned raHeightsNonWeightedStackFP[lclMAX_TRACKED][FP_VIRTUALREGISTERS + 1]; -#endif -#endif // FEATURE_STACK_FP_X87 - -#ifdef DEBUG // One line log function. Default level is 0. Increasing it gives you // more log information @@ -9358,7 +8945,7 @@ public: static bool mayNeedShadowCopy(LclVarDsc* varDsc) { -#if defined(_TARGET_AMD64_) && !defined(LEGACY_BACKEND) +#if defined(_TARGET_AMD64_) // GS cookie logic to create shadow slots, create trees to copy reg args to shadow // slots and update all trees to refer to shadow slots is done immediately after // fgMorph(). Lsra could potentially mark a param as DoNotEnregister after JIT determines @@ -9384,7 +8971,7 @@ public: // - Whenver a parameter passed in an argument register needs to be spilled by LSRA, we // create a new spill temp if the method needs GS cookie check. return varDsc->lvIsParam; -#else // !(defined(_TARGET_AMD64_) && defined(LEGACY_BACKEND)) +#else // !defined(_TARGET_AMD64_) return varDsc->lvIsParam && !varDsc->lvIsRegArg; #endif } @@ -9896,9 +9483,7 @@ public: case GT_END_LFIN: #endif // !FEATURE_EH_FUNCLETS case GT_PHI_ARG: -#ifndef LEGACY_BACKEND case GT_JMPTABLE: -#endif // LEGACY_BACKEND case GT_REG_VAR: case GT_CLS_VAR: case GT_CLS_VAR_ADDR: @@ -10543,7 +10128,6 @@ extern const BYTE genActualTypes[]; #define REG_CORRUPT regNumber(REG_NA + 1) #define RBM_CORRUPT (RBM_ILLEGAL | regMaskTP(1)) -#define REG_PAIR_CORRUPT regPairNo(REG_PAIR_NONE + 1) /*****************************************************************************/ diff --git a/src/jit/compiler.hpp b/src/jit/compiler.hpp index 4380ea3856..4e606e6273 100644 --- a/src/jit/compiler.hpp +++ b/src/jit/compiler.hpp @@ -873,7 +873,7 @@ void* GenTree::operator new(size_t sz, Compiler* comp, genTreeOps oper) #if SMALL_TREE_NODES size_t size = GenTree::s_gtNodeSizes[oper]; #else - size_t size = TREE_NODE_SZ_LARGE; + size_t size = TREE_NODE_SZ_LARGE; #endif #if MEASURE_NODE_SIZE @@ -900,9 +900,6 @@ inline GenTree::GenTree(genTreeOps oper, var_types type DEBUGARG(bool largeNode) #ifdef DEBUG gtDebugFlags = 0; #endif // DEBUG -#ifdef LEGACY_BACKEND - gtUsedRegs = 0; -#endif // LEGACY_BACKEND #if FEATURE_ANYCSE gtCSEnum = NO_CSE; #endif // FEATURE_ANYCSE @@ -910,10 +907,6 @@ inline GenTree::GenTree(genTreeOps oper, var_types type DEBUGARG(bool largeNode) ClearAssertion(); #endif -#if FEATURE_STACK_FP_X87 - gtFPlvl = 0; -#endif - gtNext = nullptr; gtPrev = nullptr; gtRegNum = REG_NA; @@ -1041,7 +1034,7 @@ inline GenTree* Compiler::gtNewLargeOperNode(genTreeOps oper, var_types type, Ge GenTree* node = new (this, LargeOpOpcode()) GenTreeOp(oper, type, op1, op2 DEBUGARG(/*largeNode*/ true)); #else - GenTree* node = new (this, oper) GenTreeOp(oper, type, op1, op2); + GenTree* node = new (this, oper) GenTreeOp(oper, type, op1, op2); #endif return node; @@ -1067,7 +1060,7 @@ inline GenTree* Compiler::gtNewIconHandleNode(size_t value, unsigned flags, Fiel #if defined(LATE_DISASM) node = new (this, LargeOpOpcode()) GenTreeIntCon(TYP_I_IMPL, value, fields DEBUGARG(/*largeNode*/ true)); #else - node = new (this, GT_CNS_INT) GenTreeIntCon(TYP_I_IMPL, value, fields); + node = new (this, GT_CNS_INT) GenTreeIntCon(TYP_I_IMPL, value, fields); #endif node->gtFlags |= flags; return node; @@ -1219,7 +1212,7 @@ inline GenTree* Compiler::gtNewFieldRef( assert(GenTree::s_gtNodeSizes[GT_IND] <= GenTree::s_gtNodeSizes[GT_FIELD]); GenTree* tree = new (this, GT_FIELD) GenTreeField(typ); #else - GenTree* tree = new (this, GT_FIELD) GenTreeField(typ); + GenTree* tree = new (this, GT_FIELD) GenTreeField(typ); #endif tree->gtField.gtFldObj = obj; tree->gtField.gtFldHnd = fldHnd; @@ -1367,58 +1360,13 @@ inline void Compiler::gtSetStmtInfo(GenTree* stmt) assert(stmt->gtOper == GT_STMT); GenTree* expr = stmt->gtStmt.gtStmtExpr; -#if FEATURE_STACK_FP_X87 - /* We will try to compute the FP stack level at each node */ - codeGen->genResetFPstkLevel(); - - /* Sometimes we need to redo the FP level computation */ - gtFPstLvlRedo = false; -#endif // FEATURE_STACK_FP_X87 - -#ifdef DEBUG - if (verbose && 0) - { - gtDispTree(stmt); - } -#endif - /* Recursively process the expression */ gtSetEvalOrder(expr); // Set the statement to have the same costs as the top node of the tree. stmt->CopyCosts(expr); - -#if FEATURE_STACK_FP_X87 - /* Unused float values leave one operand on the stack */ - assert(codeGen->genGetFPstkLevel() == 0 || codeGen->genGetFPstkLevel() == 1); - - /* Do we need to recompute FP stack levels? */ - - if (gtFPstLvlRedo) - { - codeGen->genResetFPstkLevel(); - gtComputeFPlvls(expr); - assert(codeGen->genGetFPstkLevel() == 0 || codeGen->genGetFPstkLevel() == 1); - } -#endif // FEATURE_STACK_FP_X87 -} - -#if FEATURE_STACK_FP_X87 -inline unsigned Compiler::gtSetEvalOrderAndRestoreFPstkLevel(GenTree* tree) -{ - unsigned FPlvlSave = codeGen->genFPstkLevel; - unsigned result = gtSetEvalOrder(tree); - codeGen->genFPstkLevel = FPlvlSave; - - return result; -} -#else // !FEATURE_STACK_FP_X87 -inline unsigned Compiler::gtSetEvalOrderAndRestoreFPstkLevel(GenTree* tree) -{ - return gtSetEvalOrder(tree); } -#endif // FEATURE_STACK_FP_X87 /*****************************************************************************/ #if SMALL_TREE_NODES @@ -1469,7 +1417,7 @@ inline void GenTree::SetOper(genTreeOps oper, ValueNumberUpdate vnUpdate) gtIntCon.gtFieldSeq = nullptr; } -#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_) +#if defined(_TARGET_ARM_) if (oper == GT_MUL_LONG) { // We sometimes bash GT_MUL to GT_MUL_LONG, which converts it from GenTreeOp to GenTreeMultiRegOp. @@ -2114,17 +2062,6 @@ inline void LclVarDsc::setPrefReg(regNumber regNum, Compiler* comp) /* Overwrite the lvPrefReg field */ lvPrefReg = (regMaskSmall)regMask; - -#ifdef LEGACY_BACKEND - // This is specific to the classic register allocator. - // While walking the trees during reg predict we set the lvPrefReg mask - // and then re-sort the 'tracked' variable when the lvPrefReg mask changes. - if (lvTracked) - { - /* Flag this change, set lvaSortAgain to true */ - comp->lvaSortAgain = true; - } -#endif // LEGACY_BACKEND } /***************************************************************************** @@ -2169,17 +2106,6 @@ inline void LclVarDsc::addPrefReg(regMaskTP regMask, Compiler* comp) /* Update the lvPrefReg field */ lvPrefReg |= regMask; - -#ifdef LEGACY_BACKEND - // This is specific to the classic register allocator - // While walking the trees during reg predict we set the lvPrefReg mask - // and then resort the 'tracked' variable when the lvPrefReg mask changes - if (lvTracked) - { - /* Flag this change, set lvaSortAgain to true */ - comp->lvaSortAgain = true; - } -#endif // LEGACY_BACKEND } /***************************************************************************** @@ -2394,16 +2320,11 @@ inline // On amd64, every param has a stack location, except on Unix-like systems. assert(varDsc->lvIsParam); #endif // UNIX_AMD64_ABI -#elif !defined(LEGACY_BACKEND) - // For !LEGACY_BACKEND on other targets, a stack parameter that is enregistered or prespilled +#else // !_TARGET_AMD64_ + // For other targets, a stack parameter that is enregistered or prespilled // for profiling on ARM will have a stack location. assert((varDsc->lvIsParam && !varDsc->lvIsRegArg) || isPrespilledArg); -#else // !(_TARGET_AMD64 || defined(LEGACY_BACKEND)) - // Otherwise, we only have a valid stack location for: - // A parameter that was passed on the stack, being homed into its register home, - // or a prespilled argument on arm under profiler. - assert((varDsc->lvIsParam && !varDsc->lvIsRegArg && varDsc->lvRegister) || isPrespilledArg); -#endif // !(_TARGET_AMD64 || defined(LEGACY_BACKEND)) +#endif // !_TARGET_AMD64_ } FPbased = varDsc->lvFramePointerBased; @@ -2435,13 +2356,11 @@ inline if (lvaDoneFrameLayout == Compiler::FINAL_FRAME_LAYOUT) { TempDsc* tmpDsc = tmpFindNum(varNum); -#ifndef LEGACY_BACKEND // The temp might be in use, since this might be during code generation. if (tmpDsc == nullptr) { tmpDsc = tmpFindNum(varNum, Compiler::TEMP_USAGE_USED); } -#endif // !LEGACY_BACKEND assert(tmpDsc != nullptr); offset = tmpDsc->tdTempOffs(); type = tmpDsc->tdTempType(); @@ -3134,94 +3053,6 @@ inline bool Compiler::fgIsBigOffset(size_t offset) return (offset > compMaxUncheckedOffsetForNullObject); } -#if defined(LEGACY_BACKEND) - -/*********************************************************************************** -* -* Returns true if back-end will do other than integer division which currently occurs only -* if "divisor" is a positive integer constant and a power of 2 other than 1 and INT_MIN -*/ - -inline bool Compiler::fgIsSignedDivOptimizable(GenTree* divisor) -{ - if (!opts.MinOpts() && divisor->IsCnsIntOrI()) - { - ssize_t ival = divisor->gtIntConCommon.IconValue(); - - /* Is the divisor a power of 2 (excluding INT_MIN) ?. - The intent of the third condition below is to exclude INT_MIN on a 64-bit platform - and during codegen we need to encode ival-1 within 32 bits. If ival were INT_MIN - then ival-1 would cause underflow. - - Note that we could put #ifdef around the third check so that it is applied only on - 64-bit platforms but the below is a more generic way to express it as it is a no-op - on 32-bit platforms. - */ - return (ival > 0 && genMaxOneBit(ival) && ((ssize_t)(int)ival == ival)); - } - - return false; -} - -/************************************************************************************ -* -* Returns true if back-end will do other than integer division which currently occurs -* if "divisor" is an unsigned integer constant and a power of 2 other than 1 and zero. -*/ - -inline bool Compiler::fgIsUnsignedDivOptimizable(GenTree* divisor) -{ - if (!opts.MinOpts() && divisor->IsCnsIntOrI()) - { - size_t ival = divisor->gtIntCon.gtIconVal; - - /* Is the divisor a power of 2 ? */ - return ival && genMaxOneBit(ival); - } - - return false; -} - -/***************************************************************************** -* -* Returns true if back-end will do other than integer division which currently occurs -* if "divisor" is a positive integer constant and a power of 2 other than zero -*/ - -inline bool Compiler::fgIsSignedModOptimizable(GenTree* divisor) -{ - if (!opts.MinOpts() && divisor->IsCnsIntOrI()) - { - size_t ival = divisor->gtIntCon.gtIconVal; - - /* Is the divisor a power of 2 ? */ - return ssize_t(ival) > 0 && genMaxOneBit(ival); - } - - return false; -} - -/***************************************************************************** -* -* Returns true if back-end will do other than integer division which currently occurs -* if "divisor" is a positive integer constant and a power of 2 other than zero -*/ - -inline bool Compiler::fgIsUnsignedModOptimizable(GenTree* divisor) -{ - if (!opts.MinOpts() && divisor->IsCnsIntOrI()) - { - size_t ival = divisor->gtIntCon.gtIconVal; - - /* Is the divisor a power of 2 ? */ - return ival != 0 && ival == (unsigned)genFindLowestBit(ival); - } - - return false; -} - -#endif // LEGACY_BACKEND - /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX @@ -3549,8 +3380,6 @@ inline regMaskTP genIntAllRegArgMask(unsigned numRegs) return result; } -#if !FEATURE_STACK_FP_X87 - inline regMaskTP genFltAllRegArgMask(unsigned numRegs) { assert(numRegs <= MAX_FLOAT_REG_ARG); @@ -3563,8 +3392,6 @@ inline regMaskTP genFltAllRegArgMask(unsigned numRegs) return result; } -#endif // !FEATURE_STACK_FP_X87 - /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX @@ -4808,9 +4635,7 @@ void GenTree::VisitOperands(TVisitor visitor) case GT_END_LFIN: #endif // !FEATURE_EH_FUNCLETS case GT_PHI_ARG: -#ifndef LEGACY_BACKEND case GT_JMPTABLE: -#endif // LEGACY_BACKEND case GT_REG_VAR: case GT_CLS_VAR: case GT_CLS_VAR_ADDR: @@ -4856,7 +4681,7 @@ void GenTree::VisitOperands(TVisitor visitor) case GT_NULLCHECK: case GT_PUTARG_REG: case GT_PUTARG_STK: -#if defined(_TARGET_ARM_) && !defined(LEGACY_BACKEND) +#if defined(_TARGET_ARM_) case GT_PUTARG_SPLIT: #endif case GT_RETURNTRAP: diff --git a/src/jit/compphases.h b/src/jit/compphases.h index c8f0d75365..0a2f999dd6 100644 --- a/src/jit/compphases.h +++ b/src/jit/compphases.h @@ -87,18 +87,13 @@ CompPhaseNameMacro(PHASE_LCLVARLIVENESS_INIT, "Local var liveness init", CompPhaseNameMacro(PHASE_LCLVARLIVENESS_PERBLOCK,"Per block local var liveness", "LIV-BLK", false, PHASE_LCLVARLIVENESS, false) CompPhaseNameMacro(PHASE_LCLVARLIVENESS_INTERBLOCK, "Global local var liveness", "LIV-GLBL", false, PHASE_LCLVARLIVENESS, false) -#ifdef LEGACY_BACKEND -CompPhaseNameMacro(PHASE_RA_ASSIGN_VARS, "RA assign vars", "REGALLOC", false, -1, false) -#endif // LEGACY_BACKEND CompPhaseNameMacro(PHASE_LOWERING_DECOMP, "Lowering decomposition", "LWR-DEC", false, -1, false) CompPhaseNameMacro(PHASE_LOWERING, "Lowering nodeinfo", "LWR-INFO", false, -1, true) -#ifndef LEGACY_BACKEND CompPhaseNameMacro(PHASE_STACK_LEVEL_SETTER, "Calculate stack level slots", "STK-SET", false, -1, false) CompPhaseNameMacro(PHASE_LINEAR_SCAN, "Linear scan register alloc", "LSRA", true, -1, true) CompPhaseNameMacro(PHASE_LINEAR_SCAN_BUILD, "LSRA build intervals", "LSRA-BLD", false, PHASE_LINEAR_SCAN, false) CompPhaseNameMacro(PHASE_LINEAR_SCAN_ALLOC, "LSRA allocate", "LSRA-ALL", false, PHASE_LINEAR_SCAN, false) CompPhaseNameMacro(PHASE_LINEAR_SCAN_RESOLVE, "LSRA resolve", "LSRA-RES", false, PHASE_LINEAR_SCAN, false) -#endif // !LEGACY_BACKEND CompPhaseNameMacro(PHASE_GENERATE_CODE, "Generate code", "CODEGEN", false, -1, false) CompPhaseNameMacro(PHASE_EMIT_CODE, "Emit code", "EMIT", false, -1, false) CompPhaseNameMacro(PHASE_EMIT_GCEH, "Emit GC+EH tables", "EMT-GCEH", false, -1, false) diff --git a/src/jit/crossgen/jit_crossgen.nativeproj b/src/jit/crossgen/jit_crossgen.nativeproj index f8552dc2f5..2eaf11857f 100644 --- a/src/jit/crossgen/jit_crossgen.nativeproj +++ b/src/jit/crossgen/jit_crossgen.nativeproj @@ -10,9 +10,6 @@ jit_crossgen true LIBRARY - - $(ClDefines);LEGACY_BACKEND - $(ClDefines);LEGACY_BACKEND diff --git a/src/jit/decomposelongs.cpp b/src/jit/decomposelongs.cpp index ae5d8a73ec..9e6da62c86 100644 --- a/src/jit/decomposelongs.cpp +++ b/src/jit/decomposelongs.cpp @@ -26,7 +26,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX*/ #pragma hdrstop #endif -#ifndef LEGACY_BACKEND // This file is ONLY used for the RyuJIT backend that uses the linear scan register allocator #ifndef _TARGET_64BIT_ // DecomposeLongs is only used on 32-bit platforms #include "decomposelongs.h" @@ -2000,4 +1999,3 @@ genTreeOps DecomposeLongs::GetLoOper(genTreeOps oper) } #endif // !_TARGET_64BIT_ -#endif // !LEGACY_BACKEND diff --git a/src/jit/ee_il_dll.cpp b/src/jit/ee_il_dll.cpp index b1321bcc25..b4abcc8006 100644 --- a/src/jit/ee_il_dll.cpp +++ b/src/jit/ee_il_dll.cpp @@ -382,7 +382,7 @@ unsigned CILJit::getMaxIntrinsicSIMDVectorLength(CORJIT_FLAGS cpuCompileFlags) jitFlags.SetFromFlags(cpuCompileFlags); #ifdef FEATURE_SIMD -#if defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND) +#if defined(_TARGET_XARCH_) if (!jitFlags.IsSet(JitFlags::JIT_FLAG_PREJIT) && jitFlags.IsSet(JitFlags::JIT_FLAG_FEATURE_SIMD) && jitFlags.IsSet(JitFlags::JIT_FLAG_USE_AVX2)) { @@ -399,7 +399,7 @@ unsigned CILJit::getMaxIntrinsicSIMDVectorLength(CORJIT_FLAGS cpuCompileFlags) return 32; } } -#endif // !(defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND)) +#endif // defined(_TARGET_XARCH_) if (GetJitTls() != nullptr && JitTls::GetCompiler() != nullptr) { JITDUMP("getMaxIntrinsicSIMDVectorLength: returning 16\n"); diff --git a/src/jit/emit.cpp b/src/jit/emit.cpp index 487e849011..89a2bca1d0 100644 --- a/src/jit/emit.cpp +++ b/src/jit/emit.cpp @@ -323,7 +323,6 @@ void emitterStaticStats(FILE* fout) fprintf(fout, "Size of insPlaceholderGroupData = %u\n", sizeof(insPlaceholderGroupData)); fprintf(fout, "\n"); - fprintf(fout, "Size of tinyID = %2u\n", TINY_IDSC_SIZE); fprintf(fout, "Size of instrDesc = %2u\n", sizeof(emitter::instrDesc)); // fprintf(fout, "Offset of _idIns = %2u\n", offsetof(emitter::instrDesc, _idIns )); // fprintf(fout, "Offset of _idInsFmt = %2u\n", offsetof(emitter::instrDesc, _idInsFmt )); @@ -930,48 +929,6 @@ insGroup* emitter::emitSavIG(bool emitAdd) return ig; } -#ifdef LEGACY_BACKEND -void emitter::emitTmpSizeChanged(unsigned tmpSize) -{ - assert(emitGrowableMaxByteOffs <= SCHAR_MAX); - -#ifdef DEBUG - // Workaround for FP code - bool bAssert = JitConfig.JitMaxTempAssert() ? true : false; - - if (tmpSize > emitMaxTmpSize && bAssert) - { - // TODO-Review: We have a known issue involving floating point code and this assert. - // The generated code will be ok, This is only a warning. - // To not receive this assert again you can set the registry key: JITMaxTempAssert=0. - // - assert(!"Incorrect max tmp size set."); - } -#endif - - if (tmpSize <= emitMaxTmpSize) - return; - - unsigned change = tmpSize - emitMaxTmpSize; - - /* If we have used a small offset to access a variable, growing the - temp size is a problem if we should have used a large offset instead. - Detect if such a situation happens and bail */ - - if (emitGrowableMaxByteOffs <= SCHAR_MAX && (emitGrowableMaxByteOffs + change) > SCHAR_MAX) - { -#ifdef DEBUG - if (emitComp->verbose) - printf("Under-estimated var offset encoding size for ins #%Xh\n", emitMaxByteOffsIdNum); -#endif - IMPL_LIMITATION("Should have used large offset to access var"); - } - - emitMaxTmpSize = tmpSize; - emitGrowableMaxByteOffs += change; -} -#endif // LEGACY_BACKEND - /***************************************************************************** * * Start generating code to be scheduled; called once per method. @@ -982,10 +939,6 @@ void emitter::emitBegFN(bool hasFramePtr , bool chkAlign #endif -#ifdef LEGACY_BACKEND - , - unsigned lclSize -#endif // LEGACY_BACKEND , unsigned maxTmpSize) { @@ -1002,14 +955,6 @@ void emitter::emitBegFN(bool hasFramePtr emitMaxTmpSize = maxTmpSize; -#ifdef LEGACY_BACKEND - emitLclSize = lclSize; - emitGrowableMaxByteOffs = 0; -#ifdef DEBUG - emitMaxByteOffsIdNum = (unsigned)-1; -#endif // DEBUG -#endif // LEGACY_BACKEND - #ifdef DEBUG emitChkAlign = chkAlign; #endif @@ -1314,20 +1259,6 @@ void* emitter::emitAllocInstr(size_t sz, emitAttr opsz) assert(id->idCodeSize() == 0); #endif -#if HAS_TINY_DESC - /* Is the second area to be cleared actually present? */ - if (sz >= SMALL_IDSC_SIZE) - { - /* Clear the second 4 bytes, or the 'SMALL' part */ - *(int*)((BYTE*)id + (SMALL_IDSC_SIZE - sizeof(int))) = 0; - - // These fields should have been zero-ed by the above - assert(id->idIsLargeCns() == false); - assert(id->idIsLargeDsp() == false); - assert(id->idIsLargeCall() == false); - } -#endif - // Make sure that idAddrUnion is just a union of various pointer sized things C_ASSERT(sizeof(CORINFO_FIELD_HANDLE) <= sizeof(void*)); C_ASSERT(sizeof(CORINFO_METHOD_HANDLE) <= sizeof(void*)); @@ -2305,7 +2236,7 @@ bool emitter::emitNoGChelper(unsigned IHX) case CORINFO_HELP_PROF_FCN_LEAVE: case CORINFO_HELP_PROF_FCN_ENTER: -#if defined(_TARGET_AMD64_) || (defined(_TARGET_X86_) && !defined(LEGACY_BACKEND)) +#if defined(_TARGET_XARCH_) case CORINFO_HELP_PROF_FCN_TAILCALL: #endif case CORINFO_HELP_LLSH: @@ -5360,8 +5291,6 @@ UNATIVE_OFFSET emitter::emitDataConst(const void* cnsAddr, unsigned cnsSize, boo return cnum; } -#ifndef LEGACY_BACKEND - //------------------------------------------------------------------------ // emitAnyConst: Create a data section constant of arbitrary size. // @@ -5421,7 +5350,6 @@ CORINFO_FIELD_HANDLE emitter::emitFltOrDblConst(double constValue, emitAttr attr UNATIVE_OFFSET cnum = emitDataConst(cnsAddr, cnsSize, dblAlign); return emitComp->eeFindJitDataOffs(cnum); } -#endif /***************************************************************************** * @@ -6156,7 +6084,7 @@ unsigned char emitter::emitOutputSizeT(BYTE* dst, ssize_t val) // Same as wrapped function. // -#if !defined(LEGACY_BACKEND) && defined(_TARGET_X86_) +#if defined(_TARGET_X86_) unsigned char emitter::emitOutputByte(BYTE* dst, size_t val) { return emitOutputByte(dst, (ssize_t)val); @@ -6196,7 +6124,7 @@ unsigned char emitter::emitOutputSizeT(BYTE* dst, unsigned __int64 val) { return emitOutputSizeT(dst, (ssize_t)val); } -#endif // !defined(LEGACY_BACKEND) && defined(_TARGET_X86_) +#endif // defined(_TARGET_X86_) /***************************************************************************** * diff --git a/src/jit/emit.h b/src/jit/emit.h index 337ab1b665..62b6a4e1ec 100644 --- a/src/jit/emit.h +++ b/src/jit/emit.h @@ -567,24 +567,18 @@ protected: #endif // _TARGET_ARM_ -#if defined(_TARGET_X86_) && defined(LEGACY_BACKEND) -#define HAS_TINY_DESC 1 -#else -#define HAS_TINY_DESC 0 -#endif - struct instrDescCns; struct instrDesc { private: -#if (defined(_TARGET_XARCH_) || defined(_TARGET_ARM64_)) && !defined(LEGACY_BACKEND) +#if defined(_TARGET_XARCH_) || defined(_TARGET_ARM64_) // The assembly instruction instruction _idIns : 9; -#else // !(defined(_TARGET_XARCH_) || defined(_TARGET_ARM64_)) || defined(LEGACY_BACKEND) +#else // !(defined(_TARGET_XARCH_) || defined(_TARGET_ARM64_)) // The assembly instruction instruction _idIns : 8; -#endif // !(defined(_TARGET_XARCH_) || defined(_TARGET_ARM64_)) || defined(LEGACY_BACKEND) +#endif // !(defined(_TARGET_XARCH_) || defined(_TARGET_ARM64_)) // The format for the instruction insFormat _idInsFmt : 8; @@ -637,15 +631,15 @@ protected: unsigned _idCodeSize : 4; // size of instruction in bytes #endif -#if defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND) +#if defined(_TARGET_XARCH_) opSize _idOpSize : 3; // operand size: 0=1 , 1=2 , 2=4 , 3=8, 4=16, 5=32 // At this point we have fully consumed first DWORD so that next field // doesn't cross a byte boundary. #elif defined(_TARGET_ARM64_) // Moved the definition of '_idOpSize' later so that we don't cross a 32-bit boundary when laying out bitfields -#else // ARM or x86-LEGACY_BACKEND +#else // ARM opSize _idOpSize : 2; // operand size: 0=1 , 1=2 , 2=4 , 3=8 -#endif // ARM or x86-LEGACY_BACKEND +#endif // ARM // On Amd64, this is where the second DWORD begins // On System V a call could return a struct in 2 registers. The instrDescCGCA struct below has member that @@ -672,15 +666,6 @@ protected: // arm64: 31 bits CLANG_FORMAT_COMMENT_ANCHOR; -#if HAS_TINY_DESC - // - // For x86 use last two bits to differentiate if we are tiny or small - // - unsigned _idTinyDsc : 1; // is this a "tiny" descriptor? - unsigned _idSmallDsc : 1; // is this a "small" descriptor? - -#else // !HAS_TINY_DESC - // // On x86/arm platforms we have used 32 bits so far (4 bytes) // On amd64 we have used 38 bits so far (4 bytes + 6 bits) @@ -703,17 +688,17 @@ protected: unsigned _idNoGC : 1; // Some helpers don't get recorded in GC tables #ifdef _TARGET_ARM64_ - opSize _idOpSize : 3; // operand size: 0=1 , 1=2 , 2=4 , 3=8, 4=16 - insOpts _idInsOpt : 6; // options for instructions - unsigned _idLclVar : 1; // access a local on stack + opSize _idOpSize : 3; // operand size: 0=1 , 1=2 , 2=4 , 3=8, 4=16 + insOpts _idInsOpt : 6; // options for instructions + unsigned _idLclVar : 1; // access a local on stack #endif #ifdef _TARGET_ARM_ - insSize _idInsSize : 2; // size of instruction: 16, 32 or 48 bits - insFlags _idInsFlags : 1; // will this instruction set the flags - unsigned _idLclVar : 1; // access a local on stack - unsigned _idLclFPBase : 1; // access a local on stack - SP based offset - insOpts _idInsOpt : 3; // options for Load/Store instructions + insSize _idInsSize : 2; // size of instruction: 16, 32 or 48 bits + insFlags _idInsFlags : 1; // will this instruction set the flags + unsigned _idLclVar : 1; // access a local on stack + unsigned _idLclFPBase : 1; // access a local on stack - SP based offset + insOpts _idInsOpt : 3; // options for Load/Store instructions // For arm we have used 16 bits #define ID_EXTRA_BITFIELD_BITS (16) @@ -721,23 +706,19 @@ protected: #elif defined(_TARGET_ARM64_) // For Arm64, we have used 17 bits from the second DWORD. #define ID_EXTRA_BITFIELD_BITS (17) -#elif defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND) -// For xarch !LEGACY_BACKEND, we have used 14 bits from the second DWORD. +#elif defined(_TARGET_XARCH_) + // For xarch, we have used 14 bits from the second DWORD. #define ID_EXTRA_BITFIELD_BITS (14) -#elif defined(_TARGET_X86_) -// For x86, we have used 6 bits from the second DWORD. -#define ID_EXTRA_BITFIELD_BITS (6) #else #error Unsupported or unset target architecture #endif //////////////////////////////////////////////////////////////////////// // Space taken up to here: - // x86: 38 bits // if HAS_TINY_DESC is not defined (which it is) + // x86: 38 bits // amd64: 46 bits // arm: 48 bits // arm64: 49 bits - CLANG_FORMAT_COMMENT_ANCHOR; unsigned _idCnsReloc : 1; // LargeCns is an RVA and needs reloc tag unsigned _idDspReloc : 1; // LargeDsp is an RVA and needs reloc tag @@ -774,8 +755,6 @@ protected: //////////////////////////////////////////////////////////////////////// CLANG_FORMAT_COMMENT_ANCHOR; -#endif // !HAS_TINY_DESC - #ifdef DEBUG instrDescDebugInfo* _idDebugOnlyInfo; @@ -809,45 +788,6 @@ protected: // CLANG_FORMAT_COMMENT_ANCHOR; -#if HAS_TINY_DESC - - unsigned _idLargeCns : 1; // does a large constant follow? - unsigned _idLargeDsp : 1; // does a large displacement follow? - unsigned _idLargeCall : 1; // large call descriptor used - unsigned _idBound : 1; // jump target / frame offset bound - - unsigned _idCallRegPtr : 1; // IL indirect calls: addr in reg - unsigned _idCallAddr : 1; // IL indirect calls: can make a direct call to iiaAddr - unsigned _idNoGC : 1; // Some helpers don't get recorded in GC tables - -#define ID_EXTRA_BITFIELD_BITS (7) - - // - // For x86, we are using 7 bits from the second DWORD for bitfields. - // - - unsigned _idCnsReloc : 1; // LargeCns is an RVA and needs reloc tag - unsigned _idDspReloc : 1; // LargeDsp is an RVA and needs reloc tag - -#define ID_EXTRA_RELOC_BITS (2) - -#define ID_EXTRA_REG_BITS (0) - -#define ID_EXTRA_BITS (ID_EXTRA_BITFIELD_BITS + ID_EXTRA_RELOC_BITS + ID_EXTRA_REG_BITS) - -/* Use whatever bits are left over for small constants */ - -#define ID_BIT_SMALL_CNS (32 - ID_EXTRA_BITS) -#define ID_MIN_SMALL_CNS 0 -#define ID_MAX_SMALL_CNS (int)((1 << ID_BIT_SMALL_CNS) - 1U) - - // For x86 we have 23 bits remaining for the - // small constant in this extra DWORD. - - unsigned _idSmallCns : ID_BIT_SMALL_CNS; - -#endif // HAS_TINY_DESC - // // This is the end of the 'small' instrDesc which is the same on all // platforms (except 64-bit DEBUG which is a little bigger). @@ -869,18 +809,12 @@ protected: */ #if DEBUG -#define TINY_IDSC_DEBUG_EXTRA (sizeof(void*)) +#define SMALL_IDSC_DEBUG_EXTRA (sizeof(void*)) #else -#define TINY_IDSC_DEBUG_EXTRA (0) +#define SMALL_IDSC_DEBUG_EXTRA (0) #endif -#if HAS_TINY_DESC -#define TINY_IDSC_SIZE (4 + TINY_IDSC_DEBUG_EXTRA) -#define SMALL_IDSC_SIZE (8 + TINY_IDSC_DEBUG_EXTRA) -#else -#define TINY_IDSC_SIZE (8 + TINY_IDSC_DEBUG_EXTRA) -#define SMALL_IDSC_SIZE TINY_IDSC_SIZE -#endif +#define SMALL_IDSC_SIZE (8 + SMALL_IDSC_DEBUG_EXTRA) void checkSizes(); @@ -946,30 +880,6 @@ protected: /* Trivial wrappers to return properly typed enums */ public: -#if HAS_TINY_DESC - - bool idIsTiny() const - { - return (_idTinyDsc != 0); - } - void idSetIsTiny() - { - _idTinyDsc = 1; - } - -#else - - bool idIsTiny() const - { - return false; - } - void idSetIsTiny() - { - _idSmallDsc = 1; - } - -#endif // HAS_TINY_DESC - bool idIsSmallDsc() const { return (_idSmallDsc != 0); @@ -1084,13 +994,11 @@ protected: #ifdef _TARGET_ARM64_ GCtype idGCrefReg2() const { - assert(!idIsTiny()); assert(!idIsSmallDsc()); return (GCtype)idAddr()->_idGCref2; } void idGCrefReg2(GCtype gctype) { - assert(!idIsTiny()); assert(!idIsSmallDsc()); idAddr()->_idGCref2 = gctype; } @@ -1109,26 +1017,22 @@ protected: #if defined(_TARGET_XARCH_) regNumber idReg3() const { - assert(!idIsTiny()); assert(!idIsSmallDsc()); return idAddr()->_idReg3; } void idReg3(regNumber reg) { - assert(!idIsTiny()); assert(!idIsSmallDsc()); idAddr()->_idReg3 = reg; assert(reg == idAddr()->_idReg3); } regNumber idReg4() const { - assert(!idIsTiny()); assert(!idIsSmallDsc()); return idAddr()->_idReg4; } void idReg4(regNumber reg) { - assert(!idIsTiny()); assert(!idIsSmallDsc()); idAddr()->_idReg4 = reg; assert(reg == idAddr()->_idReg4); @@ -1147,26 +1051,22 @@ protected: regNumber idReg3() const { - assert(!idIsTiny()); assert(!idIsSmallDsc()); return idAddr()->_idReg3; } void idReg3(regNumber reg) { - assert(!idIsTiny()); assert(!idIsSmallDsc()); idAddr()->_idReg3 = reg; assert(reg == idAddr()->_idReg3); } regNumber idReg4() const { - assert(!idIsTiny()); assert(!idIsSmallDsc()); return idAddr()->_idReg4; } void idReg4(regNumber reg) { - assert(!idIsTiny()); assert(!idIsSmallDsc()); idAddr()->_idReg4 = reg; assert(reg == idAddr()->_idReg4); @@ -1174,13 +1074,11 @@ protected: #ifdef _TARGET_ARM64_ bool idReg3Scaled() const { - assert(!idIsTiny()); assert(!idIsSmallDsc()); return (idAddr()->_idReg3Scaled == 1); } void idReg3Scaled(bool val) { - assert(!idIsTiny()); assert(!idIsSmallDsc()); idAddr()->_idReg3Scaled = val ? 1 : 0; } @@ -1195,72 +1093,59 @@ protected: bool idIsLargeCns() const { - assert(!idIsTiny()); return _idLargeCns != 0; } void idSetIsLargeCns() { - assert(!idIsTiny()); _idLargeCns = 1; } bool idIsLargeDsp() const { - assert(!idIsTiny()); return _idLargeDsp != 0; } void idSetIsLargeDsp() { - assert(!idIsTiny()); _idLargeDsp = 1; } void idSetIsSmallDsp() { - assert(!idIsTiny()); _idLargeDsp = 0; } bool idIsLargeCall() const { - assert(!idIsTiny()); return _idLargeCall != 0; } void idSetIsLargeCall() { - assert(!idIsTiny()); _idLargeCall = 1; } bool idIsBound() const { - assert(!idIsTiny()); return _idBound != 0; } void idSetIsBound() { - assert(!idIsTiny()); _idBound = 1; } bool idIsCallRegPtr() const { - assert(!idIsTiny()); return _idCallRegPtr != 0; } void idSetIsCallRegPtr() { - assert(!idIsTiny()); _idCallRegPtr = 1; } bool idIsCallAddr() const { - assert(!idIsTiny()); return _idCallAddr != 0; } void idSetIsCallAddr() { - assert(!idIsTiny()); _idCallAddr = 1; } @@ -1269,23 +1154,20 @@ protected: // code, it is not necessary to generate GC info for a call so labeled. bool idIsNoGC() const { - assert(!idIsTiny()); return _idNoGC != 0; } void idSetIsNoGC(bool val) { - assert(!idIsTiny()); _idNoGC = val; } #ifdef _TARGET_ARMARCH_ bool idIsLclVar() const { - return !idIsTiny() && _idLclVar != 0; + return _idLclVar != 0; } void idSetIsLclVar() { - assert(!idIsTiny()); _idLclVar = 1; } #endif // _TARGET_ARMARCH_ @@ -1293,34 +1175,29 @@ protected: #if defined(_TARGET_ARM_) bool idIsLclFPBase() const { - return !idIsTiny() && _idLclFPBase != 0; + return _idLclFPBase != 0; } void idSetIsLclFPBase() { - assert(!idIsTiny()); _idLclFPBase = 1; } #endif // defined(_TARGET_ARM_) bool idIsCnsReloc() const { - assert(!idIsTiny()); return _idCnsReloc != 0; } void idSetIsCnsReloc() { - assert(!idIsTiny()); _idCnsReloc = 1; } bool idIsDspReloc() const { - assert(!idIsTiny()); return _idDspReloc != 0; } void idSetIsDspReloc(bool val = true) { - assert(!idIsTiny()); _idDspReloc = val; } bool idIsReloc() @@ -1330,25 +1207,23 @@ protected: unsigned idSmallCns() const { - assert(!idIsTiny()); return _idSmallCns; } void idSmallCns(size_t value) { - assert(!idIsTiny()); assert(fitsInSmallCns(value)); _idSmallCns = value; } inline const idAddrUnion* idAddr() const { - assert(!idIsSmallDsc() && !idIsTiny()); + assert(!idIsSmallDsc()); return &this->_idAddrUnion; } inline idAddrUnion* idAddr() { - assert(!idIsSmallDsc() && !idIsTiny()); + assert(!idIsSmallDsc()); return &this->_idAddrUnion; } }; // End of struct instrDesc @@ -1680,7 +1555,7 @@ private: unsigned char emitOutputLong(BYTE* dst, ssize_t val); unsigned char emitOutputSizeT(BYTE* dst, ssize_t val); -#if !defined(LEGACY_BACKEND) && defined(_TARGET_X86_) +#if defined(_TARGET_X86_) unsigned char emitOutputByte(BYTE* dst, size_t val); unsigned char emitOutputWord(BYTE* dst, size_t val); unsigned char emitOutputLong(BYTE* dst, size_t val); @@ -1690,7 +1565,7 @@ private: unsigned char emitOutputWord(BYTE* dst, unsigned __int64 val); unsigned char emitOutputLong(BYTE* dst, unsigned __int64 val); unsigned char emitOutputSizeT(BYTE* dst, unsigned __int64 val); -#endif // !defined(LEGACY_BACKEND) && defined(_TARGET_X86_) +#endif // defined(_TARGET_X86_) size_t emitIssue1Instr(insGroup* ig, instrDesc* id, BYTE** dp); size_t emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp); @@ -1703,15 +1578,6 @@ private: unsigned emitMaxTmpSize; -#ifdef LEGACY_BACKEND - unsigned emitLclSize; - unsigned emitGrowableMaxByteOffs; - void emitTmpSizeChanged(unsigned tmpSize); -#ifdef DEBUG - unsigned emitMaxByteOffsIdNum; -#endif // DEBUG -#endif // LEGACY_BACKEND - #ifdef DEBUG bool emitChkAlign; // perform some alignment checks #endif @@ -1722,8 +1588,6 @@ private: void emitSetMediumJump(instrDescJmp* id); UNATIVE_OFFSET emitSizeOfJump(instrDescJmp* jmp); UNATIVE_OFFSET emitInstCodeSz(instrDesc* id); - -#ifndef LEGACY_BACKEND CORINFO_FIELD_HANDLE emitAnyConst(const void* cnsAddr, unsigned cnsSize, bool dblAlign); CORINFO_FIELD_HANDLE emitFltOrDblConst(double constValue, emitAttr attr); regNumber emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, GenTree* src); @@ -1735,7 +1599,6 @@ private: insFormat emitMapFmtAtoM(insFormat fmt); void emitHandleMemOp(GenTreeIndir* indir, instrDesc* id, insFormat fmt, instruction ins); void spillIntArgRegsToShadowSlots(); -#endif // !LEGACY_BACKEND /************************************************************************/ /* The logic that creates and keeps track of instruction groups */ @@ -1749,9 +1612,9 @@ private: // and must store them all to the frame on entry. If the frame is very large, we generate // ugly code like "movw r10, 0x488; add r10, sp; vstr s0, [r10]" for each store, which // eats up our insGroup buffer. -#define SC_IG_BUFFER_SIZE (100 * sizeof(instrDesc) + 14 * TINY_IDSC_SIZE) +#define SC_IG_BUFFER_SIZE (100 * sizeof(instrDesc) + 14 * SMALL_IDSC_SIZE) #else // !_TARGET_ARMARCH_ -#define SC_IG_BUFFER_SIZE (50 * sizeof(instrDesc) + 14 * TINY_IDSC_SIZE) +#define SC_IG_BUFFER_SIZE (50 * sizeof(instrDesc) + 14 * SMALL_IDSC_SIZE) #endif // !_TARGET_ARMARCH_ size_t emitIGbuffSize; @@ -1963,7 +1826,6 @@ private: return (instrDescCGCA*)emitAllocInstr(sizeof(instrDescCGCA), attr); } - instrDesc* emitNewInstrTiny(emitAttr attr); instrDesc* emitNewInstrSmall(emitAttr attr); instrDesc* emitNewInstr(emitAttr attr = EA_4BYTE); instrDesc* emitNewInstrSC(emitAttr attr, ssize_t cns); @@ -1982,7 +1844,6 @@ private: static const unsigned emitFmtCount; #endif - bool emitIsTinyInsDsc(instrDesc* id); bool emitIsScnsInsDsc(instrDesc* id); size_t emitSizeOfInsDsc(instrDesc* id); @@ -2274,27 +2135,18 @@ public: inline void emitter::instrDesc::checkSizes() { #ifdef DEBUG -#if HAS_TINY_DESC - C_ASSERT(TINY_IDSC_SIZE == (offsetof(instrDesc, _idDebugOnlyInfo) + sizeof(instrDescDebugInfo*))); -#else // !tiny C_ASSERT(SMALL_IDSC_SIZE == (offsetof(instrDesc, _idDebugOnlyInfo) + sizeof(instrDescDebugInfo*))); -#endif #endif C_ASSERT(SMALL_IDSC_SIZE == offsetof(instrDesc, _idAddrUnion)); } /***************************************************************************** * - * Returns true if the given instruction descriptor is a "tiny" or a "small + * Returns true if the given instruction descriptor is a "small * constant" one (i.e. one of the descriptors that don't have all instrDesc * fields allocated). */ -inline bool emitter::emitIsTinyInsDsc(instrDesc* id) -{ - return id->idIsTiny(); -} - inline bool emitter::emitIsScnsInsDsc(instrDesc* id) { return id->idIsSmallDsc(); @@ -2421,16 +2273,6 @@ inline emitAttr emitActualTypeSize(T type) * Little helpers to allocate various flavors of instructions. */ -inline emitter::instrDesc* emitter::emitNewInstrTiny(emitAttr attr) -{ - instrDesc* id; - - id = (instrDesc*)emitAllocInstr(TINY_IDSC_SIZE, attr); - id->idSetIsTiny(); - - return id; -} - inline emitter::instrDesc* emitter::emitNewInstrSmall(emitAttr attr) { instrDesc* id; @@ -2536,11 +2378,6 @@ inline emitter::instrDesc* emitter::emitNewInstrCns(emitAttr attr, ssize_t cns) inline size_t emitter::emitGetInstrDescSize(const instrDesc* id) { - if (id->idIsTiny()) - { - return TINY_IDSC_SIZE; - } - if (id->idIsSmallDsc()) { return SMALL_IDSC_SIZE; diff --git a/src/jit/emitarm.cpp b/src/jit/emitarm.cpp index e5c98f9469..8363a97f66 100644 --- a/src/jit/emitarm.cpp +++ b/src/jit/emitarm.cpp @@ -86,8 +86,6 @@ const emitJumpKind emitReverseJumpKinds[] = { size_t emitter::emitSizeOfInsDsc(instrDesc* id) { - assert(!emitIsTinyInsDsc(id)); - if (emitIsScnsInsDsc(id)) return SMALL_IDSC_SIZE; @@ -1903,14 +1901,7 @@ void emitter::emitIns_R_I( } else { -#ifndef LEGACY_BACKEND assert(!"emitIns_R_I: immediate doesn't fit into the instruction"); -#else // LEGACY_BACKEND - // Load val into a register - regNumber valReg = codeGen->regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(reg)); - codeGen->instGen_Set_Reg_To_Imm(EA_PTRSIZE, valReg, (ssize_t)imm); - emitIns_R_R(ins, attr, reg, valReg, flags); -#endif // LEGACY_BACKEND return; } break; @@ -3731,11 +3722,8 @@ void emitter::emitIns_R_C(instruction ins, emitAttr attr, regNumber reg, CORINFO if (isFloatReg(regTmp)) { -#ifndef LEGACY_BACKEND assert(!"emitIns_R_C() cannot be called with floating point target"); -#else // LEGACY_BACKEND - regTmp = codeGen->regSet.rsPickFreeReg(RBM_ALLINT & ~genRegMask(reg)); -#endif // LEGACY_BACKEND + return; } // Load address of CLS_VAR into a register @@ -3754,46 +3742,7 @@ void emitter::emitIns_R_C(instruction ins, emitAttr attr, regNumber reg, CORINFO void emitter::emitIns_C_R(instruction ins, emitAttr attr, CORINFO_FIELD_HANDLE fldHnd, regNumber reg, int offs) { -#ifndef LEGACY_BACKEND - assert(!"emitIns_C_R not supported for RyuJIT backend"); -#else // LEGACY_BACKEND - if (ins == INS_mov) - { - assert(!"Please use ins_Store() to select the correct instruction"); - } - assert(emitInsIsStore(ins)); - - int doff = Compiler::eeGetJitDataOffs(fldHnd); - ssize_t addr = NULL; - - if (doff >= 0) - { - NYI_ARM("JitDataOffset static fields"); - } - else if (fldHnd == FLD_GLOBAL_FS) - { - NYI_ARM("Thread-Local-Storage static fields"); - } - else if (fldHnd == FLD_GLOBAL_DS) - { - addr = (ssize_t)offs; - offs = 0; - } - else - { - assert(!jitStaticFldIsGlobAddr(fldHnd)); - addr = (ssize_t)emitComp->info.compCompHnd->getFieldAddress(fldHnd, NULL); - if (addr == NULL) - NO_WAY("could not obtain address of static field"); - } - - regNumber regTmp = codeGen->regSet.rsPickFreeReg(RBM_ALLINT & ~genRegMask(reg)); - - // Load address of CLS_VAR into a register - codeGen->instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, regTmp, addr); - - emitIns_R_R_I(ins, attr, reg, regTmp, offs); -#endif // LEGACY_BACKEND + assert(!"emitIns_C_R not supported"); } /***************************************************************************** @@ -3831,14 +3780,7 @@ void emitter::emitIns_R_AR(instruction ins, emitAttr attr, regNumber ireg, regNu } else { -#ifndef LEGACY_BACKEND assert(!"emitIns_R_AR: immediate doesn't fit in the instruction"); -#else // LEGACY_BACKEND - // Load val into a register - regNumber immReg = codeGen->regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(ireg) & ~genRegMask(reg)); - codeGen->instGen_Set_Reg_To_Imm(EA_PTRSIZE, immReg, (ssize_t)offs); - emitIns_R_R_R(INS_add, attr, ireg, reg, immReg); -#endif // LEGACY_BACKEND } return; } @@ -3872,11 +3814,8 @@ void emitter::emitIns_R_AI(instruction ins, emitAttr attr, regNumber ireg, ssize if (isFloatReg(regTmp)) { -#ifndef LEGACY_BACKEND assert(!"emitIns_R_AI with floating point reg"); -#else // LEGACY_BACKEND - regTmp = codeGen->regSet.rsPickFreeReg(RBM_ALLINT & ~genRegMask(ireg)); -#endif // LEGACY_BACKEND + return; } codeGen->instGen_Set_Reg_To_Imm(EA_IS_RELOC(attr) ? EA_HANDLE_CNS_RELOC : EA_PTRSIZE, regTmp, disp); @@ -7557,8 +7496,6 @@ void emitter::emitDispFrameRef(int varx, int disp, int offs, bool asmfm) #endif // DEBUG -#ifndef LEGACY_BACKEND - void emitter::emitInsLoadStoreOp(instruction ins, emitAttr attr, regNumber dataReg, GenTreeIndir* indir) { // Handle unaligned floating point loads/stores @@ -7890,11 +7827,7 @@ regNumber emitter::emitInsTernary(instruction ins, emitAttr attr, GenTree* dst, jumpKind = isUnsignedOverflow ? EJ_lo : EJ_vs; if (jumpKind == EJ_lo) { - if ((dst->OperGet() != GT_SUB) && -#ifdef LEGACY_BACKEND - (dst->OperGet() != GT_ASG_SUB) && -#endif - (dst->OperGet() != GT_SUB_HI)) + if ((dst->OperGet() != GT_SUB) && (dst->OperGet() != GT_SUB_HI)) { jumpKind = EJ_hs; } @@ -7908,5 +7841,4 @@ regNumber emitter::emitInsTernary(instruction ins, emitAttr attr, GenTree* dst, return dst->gtRegNum; } -#endif // !LEGACY_BACKEND #endif // defined(_TARGET_ARM_) diff --git a/src/jit/emitarm64.cpp b/src/jit/emitarm64.cpp index 36e7719272..f58f36302a 100644 --- a/src/jit/emitarm64.cpp +++ b/src/jit/emitarm64.cpp @@ -89,8 +89,6 @@ const emitJumpKind emitReverseJumpKinds[] = { size_t emitter::emitSizeOfInsDsc(instrDesc* id) { - assert(!emitIsTinyInsDsc(id)); - if (emitIsScnsInsDsc(id)) return SMALL_IDSC_SIZE; diff --git a/src/jit/emitfmtsxarch.h b/src/jit/emitfmtsxarch.h index 4d97c4d8c5..a2814f86ce 100644 --- a/src/jit/emitfmtsxarch.h +++ b/src/jit/emitfmtsxarch.h @@ -200,55 +200,6 @@ IF_DEF(AWR_RRD_CNS, IS_AM_WR|IS_R1_RD, AMD_CNS) // write [adr], read r IF_DEF(ARW_SHF, IS_AM_RW, AMD_CNS) // shift [adr], const - - -//---------------------------------------------------------------------------- -// The following formats are used for FP coprocessor instructions -//---------------------------------------------------------------------------- -#if FEATURE_STACK_FP_X87 - -IF_DEF(FRD, IS_FP_STK, NONE) // read ST(n) -IF_DEF(FWR, IS_FP_STK, NONE) // write ST(n) -IF_DEF(FRW, IS_FP_STK, NONE) // r/w ST(n) - -IF_DEF(TRD, IS_FP_STK, NONE) // read ST(0) -IF_DEF(TWR, IS_FP_STK, NONE) // write ST(0) -IF_DEF(TRW, IS_FP_STK, NONE) // r/w ST(0) - -IF_DEF(FRD_TRD, IS_FP_STK, NONE) // read ST(n), read ST(0) -IF_DEF(FWR_TRD, IS_FP_STK, NONE) // write ST(n), read ST(0) -IF_DEF(FRW_TRD, IS_FP_STK, NONE) // r/w ST(n), read ST(0) - -IF_DEF(TRD_FRD, IS_FP_STK, NONE) // read ST(0), read ST(n) -IF_DEF(TWR_FRD, IS_FP_STK, NONE) // write ST(0), read ST(n) -IF_DEF(TRW_FRD, IS_FP_STK, NONE) // r/w ST(0), read ST(n) - -IF_DEF(TRD_SRD, IS_FP_STK|IS_SF_RD, NONE) // read ST(0), read [stk] -IF_DEF(TWR_SRD, IS_FP_STK|IS_SF_RD, NONE) // write ST(0), read [stk] -IF_DEF(TRW_SRD, IS_FP_STK|IS_SF_RD, NONE) // r/w ST(0), read [stk] - -//////(SRD_TRD, IS_FP_STK|IS_SF_RD, NONE) // read [stk], read ST(n) -IF_DEF(SWR_TRD, IS_FP_STK|IS_SF_WR, NONE) // write [stk], read ST(n) -//////(SRW_TRD, IS_FP_STK|IS_SF_RW, NONE) // r/w [stk], read ST(n) - -IF_DEF(TRD_MRD, IS_FP_STK|IS_GM_RD, NONE) // read ST(0), read [mem] -IF_DEF(TWR_MRD, IS_FP_STK|IS_GM_RD, NONE) // write ST(0), read [mem] -IF_DEF(TRW_MRD, IS_FP_STK|IS_GM_RD, NONE) // r/w ST(0), read [mem] - -//////(MRD_TRD, IS_FP_STK|IS_GM_RD, NONE) // read [mem], read ST(n) -IF_DEF(MWR_TRD, IS_FP_STK|IS_GM_WR, NONE) // write [mem], read ST(n) -//////(MRW_TRD, IS_FP_STK|IS_GM_RW, NONE) // r/w [mem], read ST(n) - -IF_DEF(TRD_ARD, IS_FP_STK|IS_AM_RD, AMD ) // read ST(0), read [adr] -IF_DEF(TWR_ARD, IS_FP_STK|IS_AM_RD, AMD ) // write ST(0), read [adr] -IF_DEF(TRW_ARD, IS_FP_STK|IS_AM_RD, AMD ) // r/w ST(0), read [adr] - -//////(ARD_TRD, IS_FP_STK|IS_AM_RD, AMD ) // read [adr], read ST(n) -IF_DEF(AWR_TRD, IS_FP_STK|IS_AM_WR, AMD ) // write [adr], read ST(n) -//////(ARW_TRD, IS_FP_STK|IS_AM_RW, AMD ) // r/w [adr], read ST(n) - -#endif // FEATURE_STACK_FP_X87 - ////////////////////////////////////////////////////////////////////////////// #undef IF_DEF diff --git a/src/jit/emitpub.h b/src/jit/emitpub.h index a2f041a5f3..61769c45b9 100644 --- a/src/jit/emitpub.h +++ b/src/jit/emitpub.h @@ -17,10 +17,6 @@ void emitBegFN(bool hasFramePtr , bool checkAlign #endif -#ifdef LEGACY_BACKEND - , - unsigned lclSize -#endif // LEGACY_BACKEND , unsigned maxTmpSize); diff --git a/src/jit/emitxarch.cpp b/src/jit/emitxarch.cpp index 5a0bdd31f3..51f7d93592 100644 --- a/src/jit/emitxarch.cpp +++ b/src/jit/emitxarch.cpp @@ -27,46 +27,29 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX bool IsSSE2Instruction(instruction ins) { - return (ins >= INS_FIRST_SSE2_INSTRUCTION && ins <= INS_LAST_SSE2_INSTRUCTION); + return (ins >= INS_FIRST_SSE2_INSTRUCTION) && (ins <= INS_LAST_SSE2_INSTRUCTION); } bool IsSSE4Instruction(instruction ins) { -#ifdef LEGACY_BACKEND - return false; -#else - return (ins >= INS_FIRST_SSE4_INSTRUCTION && ins <= INS_LAST_SSE4_INSTRUCTION); -#endif + return (ins >= INS_FIRST_SSE4_INSTRUCTION) && (ins <= INS_LAST_SSE4_INSTRUCTION); } bool IsSSEOrAVXInstruction(instruction ins) { -#ifndef LEGACY_BACKEND - return (ins >= INS_FIRST_SSE2_INSTRUCTION && ins <= INS_LAST_AVX_INSTRUCTION); -#else // !LEGACY_BACKEND - return IsSSE2Instruction(ins); -#endif // LEGACY_BACKEND + return (ins >= INS_FIRST_SSE2_INSTRUCTION) && (ins <= INS_LAST_AVX_INSTRUCTION); } bool IsAVXOnlyInstruction(instruction ins) { -#ifndef LEGACY_BACKEND - return (ins >= INS_FIRST_AVX_INSTRUCTION && ins <= INS_LAST_AVX_INSTRUCTION); -#else - return false; -#endif + return (ins >= INS_FIRST_AVX_INSTRUCTION) && (ins <= INS_LAST_AVX_INSTRUCTION); } bool emitter::IsAVXInstruction(instruction ins) { -#ifndef LEGACY_BACKEND - return (UseVEXEncoding() && IsSSEOrAVXInstruction(ins)); -#else - return false; -#endif + return UseVEXEncoding() && IsSSEOrAVXInstruction(ins); } -#ifndef LEGACY_BACKEND // Returns true if the AVX instruction is a binary operator that requires 3 operands. // When we emit an instruction with only two operands, we will duplicate the destination // as a source. @@ -270,7 +253,6 @@ bool emitter::IsDstSrcSrcAVXInstruction(instruction ins) return false; } } -#endif // !LEGACY_BACKEND // ------------------------------------------------------------------- // Is4ByteSSE4Instruction: Returns true if the SSE4 instruction @@ -283,12 +265,7 @@ bool emitter::IsDstSrcSrcAVXInstruction(instruction ins) // that use the SSE38 or SSE3A macro. bool emitter::Is4ByteSSE4Instruction(instruction ins) { -#ifdef LEGACY_BACKEND - // On legacy backend SSE4 is not enabled. - return false; -#else return UseSSE4() && IsSSE4Instruction(ins) && EncodedBySSE38orSSE3A(ins); -#endif // LEGACY_BACKEND } // ------------------------------------------------------------------------------ @@ -301,17 +278,11 @@ bool emitter::Is4ByteSSE4Instruction(instruction ins) // that use the SSE38 or SSE3A macro. bool emitter::Is4ByteSSE4OrAVXInstruction(instruction ins) { -#ifdef LEGACY_BACKEND - // On legacy backend SSE4 and AVX are not enabled. - return false; -#else return ((UseVEXEncoding() && (IsSSE4Instruction(ins) || IsAVXOnlyInstruction(ins))) || (UseSSE4() && IsSSE4Instruction(ins))) && EncodedBySSE38orSSE3A(ins); -#endif // LEGACY_BACKEND } -#ifndef LEGACY_BACKEND // Returns true if this instruction requires a VEX prefix // All AVX instructions require a VEX prefix bool emitter::TakesVexPrefix(instruction ins) @@ -382,12 +353,10 @@ emitter::code_t emitter::AddVexPrefix(instruction ins, code_t code, emitAttr att return code; } -#endif // !LEGACY_BACKEND // Returns true if this instruction, for the given EA_SIZE(attr), will require a REX.W prefix bool TakesRexWPrefix(instruction ins, emitAttr attr) { -#ifndef LEGACY_BACKEND // Because the current implementation of AVX does not have a way to distinguish between the register // size specification (128 vs. 256 bits) and the operand size specification (32 vs. 64 bits), where both are // required, the instruction must be created with the register size attribute (EA_16BYTE or EA_32BYTE), @@ -403,7 +372,7 @@ bool TakesRexWPrefix(instruction ins, emitAttr attr) default: break; } -#endif // !LEGACY_BACKEND + #ifdef _TARGET_AMD64_ // movsx should always sign extend out to 8 bytes just because we don't track // whether the dest should be 4 bytes or 8 bytes (attr indicates the size @@ -517,29 +486,19 @@ bool IsExtendedReg(regNumber reg, emitAttr attr) // Since XMM registers overlap with YMM registers, this routine // can also used to know whether a YMM register in case of AVX instructions. -// -// Legacy X86: we have XMM0-XMM7 available but this routine cannot be used to -// determine whether a reg is XMM because they share the same reg numbers -// with integer registers. Hence always return false. bool IsXMMReg(regNumber reg) { -#ifndef LEGACY_BACKEND #ifdef _TARGET_AMD64_ return (reg >= REG_XMM0) && (reg <= REG_XMM15); #else // !_TARGET_AMD64_ return (reg >= REG_XMM0) && (reg <= REG_XMM7); #endif // !_TARGET_AMD64_ -#else // LEGACY_BACKEND - return false; -#endif // LEGACY_BACKEND } // Returns bits to be encoded in instruction for the given register. unsigned RegEncoding(regNumber reg) { -#ifndef LEGACY_BACKEND static_assert((REG_XMM0 & 0x7) == 0, "bad XMMBASE"); -#endif return (unsigned)(reg & 0x7); } @@ -649,7 +608,6 @@ bool isPrefix(BYTE b) // Outputs VEX prefix (in case of AVX instructions) and REX.R/X/W/B otherwise. unsigned emitter::emitOutputRexOrVexPrefixIfNeeded(instruction ins, BYTE* dst, code_t& code) { -#ifndef LEGACY_BACKEND if (hasVexPrefix(code)) { // Only AVX instructions should have a VEX prefix @@ -747,7 +705,6 @@ unsigned emitter::emitOutputRexOrVexPrefixIfNeeded(instruction ins, BYTE* dst, c emitOutputByte(dst + 2, vexPrefix & 0xFF); return 3; } -#endif // !LEGACY_BACKEND #ifdef _TARGET_AMD64_ if (code > 0x00FFFFFFFFLL) @@ -877,7 +834,6 @@ unsigned emitter::emitGetVexPrefixSize(instruction ins, emitAttr attr) //=opcodeSize + vexPrefixAdjustedSize unsigned emitter::emitGetVexPrefixAdjustedSize(instruction ins, emitAttr attr, code_t code) { -#ifndef LEGACY_BACKEND if (IsAVXInstruction(ins)) { unsigned vexPrefixAdjustedSize = emitGetVexPrefixSize(ins, attr); @@ -913,7 +869,6 @@ unsigned emitter::emitGetVexPrefixAdjustedSize(instruction ins, emitAttr attr, c return vexPrefixAdjustedSize; } -#endif // !LEGACY_BACKEND return 0; } @@ -1047,70 +1002,17 @@ inline emitter::insFormat emitter::emitInsModeFormat(instruction ins, insFormat return (insFormat)(base + emitInsUpdateMode(ins)); } -/***************************************************************************** - * - * A version of scInsModeFormat() that handles X87 floating-point instructions. - */ - -#if FEATURE_STACK_FP_X87 -emitter::insFormat emitter::emitInsModeFormat(instruction ins, insFormat base, insFormat FPld, insFormat FPst) -{ - if (CodeGen::instIsFP(ins)) - { - assert(IF_TRD_SRD + 1 == IF_TWR_SRD); - assert(IF_TRD_SRD + 2 == IF_TRW_SRD); - - assert(IF_TRD_MRD + 1 == IF_TWR_MRD); - assert(IF_TRD_MRD + 2 == IF_TRW_MRD); - - assert(IF_TRD_ARD + 1 == IF_TWR_ARD); - assert(IF_TRD_ARD + 2 == IF_TRW_ARD); - - switch (ins) - { - case INS_fst: - case INS_fstp: - case INS_fistp: - case INS_fistpl: - return (insFormat)(FPst); - - case INS_fld: - case INS_fild: - return (insFormat)(FPld + 1); - - case INS_fcomp: - case INS_fcompp: - case INS_fcomip: - return (insFormat)(FPld); - - default: - return (insFormat)(FPld + 2); - } - } - else - { - return emitInsModeFormat(ins, base); - } -} -#endif // FEATURE_STACK_FP_X87 - // This is a helper we need due to Vs Whidbey #254016 in order to distinguish // if we can not possibly be updating an integer register. This is not the best // solution, but the other ones (see bug) are going to be much more complicated. -// The issue here is that on legacy x86, the XMM registers use the same register numbers -// as the general purpose registers, so we need to distinguish them. -// We really only need this for x86 where this issue exists. bool emitter::emitInsCanOnlyWriteSSE2OrAVXReg(instrDesc* id) { instruction ins = id->idIns(); // The following SSE2 instructions write to a general purpose integer register. - if (!IsSSEOrAVXInstruction(ins) || ins == INS_mov_xmm2i || ins == INS_cvttsd2si -#ifndef LEGACY_BACKEND - || ins == INS_cvttss2si || ins == INS_cvtsd2si || ins == INS_cvtss2si || ins == INS_pmovmskb || - ins == INS_pextrw || ins == INS_pextrb || ins == INS_pextrd || ins == INS_pextrq || ins == INS_extractps -#endif // !LEGACY_BACKEND - ) + if (!IsSSEOrAVXInstruction(ins) || ins == INS_mov_xmm2i || ins == INS_cvttsd2si || ins == INS_cvttss2si || + ins == INS_cvtsd2si || ins == INS_cvtss2si || ins == INS_pmovmskb || ins == INS_pextrw || ins == INS_pextrb || + ins == INS_pextrd || ins == INS_pextrq || ins == INS_extractps) { return false; } @@ -1373,7 +1275,6 @@ inline unsigned emitter::insEncodeReg012(instruction ins, regNumber reg, emitAtt { assert(reg < REG_STK); -#ifndef LEGACY_BACKEND #ifdef _TARGET_AMD64_ // Either code is not NULL or reg is not an extended reg. // If reg is an extended reg, instruction needs to be prefixed with 'REX' @@ -1394,12 +1295,6 @@ inline unsigned emitter::insEncodeReg012(instruction ins, regNumber reg, emitAtt unsigned regBits = RegEncoding(reg); -#else // LEGACY_BACKEND - - unsigned regBits = reg; - -#endif // LEGACY_BACKEND - assert(regBits < 8); return regBits; } @@ -1414,7 +1309,6 @@ inline unsigned emitter::insEncodeReg345(instruction ins, regNumber reg, emitAtt { assert(reg < REG_STK); -#ifndef LEGACY_BACKEND #ifdef _TARGET_AMD64_ // Either code is not NULL or reg is not an extended reg. // If reg is an extended reg, instruction needs to be prefixed with 'REX' @@ -1435,12 +1329,6 @@ inline unsigned emitter::insEncodeReg345(instruction ins, regNumber reg, emitAtt unsigned regBits = RegEncoding(reg); -#else // LEGACY_BACKEND - - unsigned regBits = reg; - -#endif // LEGACY_BACKEND - assert(regBits < 8); return (regBits << 3); } @@ -1452,7 +1340,6 @@ inline unsigned emitter::insEncodeReg345(instruction ins, regNumber reg, emitAtt */ inline emitter::code_t emitter::insEncodeReg3456(instruction ins, regNumber reg, emitAttr size, code_t code) { -#ifndef LEGACY_BACKEND assert(reg < REG_STK); assert(IsAVXInstruction(ins)); assert(hasVexPrefix(code)); @@ -1471,10 +1358,6 @@ inline emitter::code_t emitter::insEncodeReg3456(instruction ins, regNumber reg, assert(regBits <= 0xF); regBits <<= 35; return code ^ regBits; - -#else - return code; -#endif } /***************************************************************************** @@ -1805,7 +1688,6 @@ inline UNATIVE_OFFSET emitter::emitInsSizeSV(code_t code, int var, int dsp) size++; } -#ifndef LEGACY_BACKEND // The offset is already assigned. Find the temp. TempDsc* tmp = emitComp->tmpFindNum(var, Compiler::TEMP_USAGE_USED); if (tmp == nullptr) @@ -1829,23 +1711,6 @@ inline UNATIVE_OFFSET emitter::emitInsSizeSV(code_t code, int var, int dsp) // SP-based offsets must already be positive. assert((int)offs >= 0); } -#else // LEGACY_BACKEND - /* We'll have to estimate the max. possible offset of this temp */ - - // TODO: Get an estimate of the temp offset instead of assuming - // TODO: that any temp may be at the max. temp offset!!!!!!!!!! - - if (emitComp->lvaTempsHaveLargerOffsetThanVars()) - { - offs = emitLclSize + emitMaxTmpSize; - } - else - { - offs = emitMaxTmpSize; - } - - offsIsUpperBound = false; -#endif // LEGACY_BACKEND } else { @@ -1972,24 +1837,6 @@ inline UNATIVE_OFFSET emitter::emitInsSizeSV(code_t code, int var, int dsp) bool useSmallEncoding = (offs <= size_t(SCHAR_MAX)); #endif -#ifdef LEGACY_BACKEND - /* If we are using a small encoding, there is a danger that we might - end up having to use a larger encoding. Record 'offs' so that - we can detect if such a situation occurs */ - - if (useSmallEncoding && !offsIsUpperBound) - { - if (emitGrowableMaxByteOffs < offs) - { - emitGrowableMaxByteOffs = offs; -#ifdef DEBUG - // Remember which instruction this is - emitMaxByteOffsIdNum = emitInsCount; -#endif - } - } -#endif // LEGACY_BACKEND - // If it is ESP based, and the offset is zero, we will not encode the disp part. if (!EBPbased && offs == 0) { @@ -2127,13 +1974,8 @@ UNATIVE_OFFSET emitter::emitInsSizeAM(instrDesc* id, code_t code) // Most 16-bit operands will require a size prefix. // This refers to 66h size prefix override. - CLANG_FORMAT_COMMENT_ANCHOR; -#if FEATURE_STACK_FP_X87 - if ((attrSize == EA_2BYTE) && (ins != INS_fldcw) && (ins != INS_fnstcw)) -#else // FEATURE_STACK_FP_X87 if (attrSize == EA_2BYTE) -#endif // FEATURE_STACK_FP_X87 { size++; } @@ -2501,7 +2343,7 @@ void emitter::emitLoopAlign() /* Insert a pseudo-instruction to ensure that we align the next instruction properly */ - instrDesc* id = emitNewInstrTiny(EA_1BYTE); + instrDesc* id = emitNewInstrSmall(EA_1BYTE); id->idIns(INS_align); id->idCodeSize(15); // We may need to skip up to 15 bytes of code emitCurIGsize += 15; @@ -2529,18 +2371,6 @@ void emitter::emitIns_Nop(unsigned size) * * Add an instruction with no operands. */ -#ifdef DEBUG -static bool isX87InsWithNoOperands(instruction ins) -{ -#if FEATURE_STACK_FP_X87 - return (ins == INS_f2xm1 || ins == INS_fchs || ins == INS_fld1 || ins == INS_fld1 || ins == INS_fldl2e || - ins == INS_fldz || ins == INS_fprem || ins == INS_frndint || ins == INS_fscale); -#else // !FEATURE_STACK_FP_X87 - return false; -#endif // !FEATURE_STACK_FP_X87 -} -#endif // DEBUG - void emitter::emitIns(instruction ins) { UNATIVE_OFFSET sz; @@ -2548,22 +2378,15 @@ void emitter::emitIns(instruction ins) code_t code = insCodeMR(ins); #ifdef DEBUG -#if FEATURE_STACK_FP_X87 - if (ins != INS_fabs && ins != INS_fsqrt && ins != INS_fsin && ins != INS_fcos) -#endif // FEATURE_STACK_FP_X87 - { // We cannot have #ifdef inside macro expansion. - bool assertCond = (ins == INS_cdq || isX87InsWithNoOperands(ins) || ins == INS_int3 || ins == INS_lock || - ins == INS_leave || ins == INS_movsb || ins == INS_movsd || ins == INS_movsp || - ins == INS_nop || ins == INS_r_movsb || ins == INS_r_movsd || ins == INS_r_movsp || - ins == INS_r_stosb || ins == INS_r_stosd || ins == INS_r_stosp || ins == INS_ret || - ins == INS_sahf || ins == INS_stosb || ins == INS_stosd || ins == INS_stosp -#ifndef LEGACY_BACKEND - // These instructions take zero operands - || ins == INS_vzeroupper || ins == INS_lfence || ins == INS_mfence || ins == INS_sfence -#endif - ); + bool assertCond = + (ins == INS_cdq || ins == INS_int3 || ins == INS_lock || ins == INS_leave || ins == INS_movsb || + ins == INS_movsd || ins == INS_movsp || ins == INS_nop || ins == INS_r_movsb || ins == INS_r_movsd || + ins == INS_r_movsp || ins == INS_r_stosb || ins == INS_r_stosd || ins == INS_r_stosp || ins == INS_ret || + ins == INS_sahf || ins == INS_stosb || ins == INS_stosd || ins == INS_stosp + // These instructions take zero operands + || ins == INS_vzeroupper || ins == INS_lfence || ins == INS_mfence || ins == INS_sfence); assert(assertCond); } @@ -2588,20 +2411,11 @@ void emitter::emitIns(instruction ins) sz = 1; } -#ifndef LEGACY_BACKEND // vzeroupper includes its 2-byte VEX prefix in its MR code. assert((ins != INS_vzeroupper) || (sz == 3)); -#endif insFormat fmt = IF_NONE; -#if FEATURE_STACK_FP_X87 - if (CodeGen::instIsFP(ins)) - { - fmt = emitInsModeFormat(ins, IF_TRD); - } -#endif // FEATURE_STACK_FP_X87 - id->idIns(ins); id->idInsFmt(fmt); id->idCodeSize(sz); @@ -2610,7 +2424,6 @@ void emitter::emitIns(instruction ins) emitCurIGsize += sz; } -#if !defined(LEGACY_BACKEND) // Add an instruction with no operands, but whose encoding depends on the size // (Only CDQ/CQO currently) void emitter::emitIns(instruction ins, emitAttr attr) @@ -3537,50 +3350,6 @@ void emitter::emitInsRMW(instruction ins, emitAttr attr, GenTreeStoreInd* storeI emitCurIGsize += sz; } -#endif // !LEGACY_BACKEND - -#if FEATURE_STACK_FP_X87 -/***************************************************************************** - * - * Add an instruction of the form "op ST(0),ST(n)". - */ - -void emitter::emitIns_F0_F(instruction ins, unsigned fpreg) -{ - UNATIVE_OFFSET sz = 2; - instrDesc* id = emitNewInstr(); - insFormat fmt = emitInsModeFormat(ins, IF_TRD_FRD); - - id->idIns(ins); - id->idInsFmt(fmt); - id->idReg1((regNumber)fpreg); - id->idCodeSize(sz); - - dispIns(id); - emitCurIGsize += sz; -} - -/***************************************************************************** - * - * Add an instruction of the form "op ST(n),ST(0)". - */ - -void emitter::emitIns_F_F0(instruction ins, unsigned fpreg) -{ - UNATIVE_OFFSET sz = 2; - instrDesc* id = emitNewInstr(); - insFormat fmt = emitInsModeFormat(ins, IF_FRD_TRD); - - id->idIns(ins); - id->idInsFmt(fmt); - id->idReg1((regNumber)fpreg); - id->idCodeSize(sz); - - dispIns(id); - emitCurIGsize += sz; -} -#endif // FEATURE_STACK_FP_X87 - /***************************************************************************** * * Add an instruction referencing a single register. @@ -3594,7 +3363,7 @@ void emitter::emitIns_R(instruction ins, emitAttr attr, regNumber reg) noway_assert(emitVerifyEncodable(ins, size, reg)); UNATIVE_OFFSET sz; - instrDesc* id = emitNewInstrTiny(attr); + instrDesc* id = emitNewInstrSmall(attr); switch (ins) { @@ -3609,7 +3378,7 @@ void emitter::emitIns_R(instruction ins, emitAttr attr, regNumber reg) if (size == EA_1BYTE) sz = 2; // Use the long form as the small one has no 'w' bit else - sz = 1; // Use short form + sz = 1; // Use short form #endif // !_TARGET_AMD64_ @@ -3809,10 +3578,6 @@ void emitter::emitIns_R_I(instruction ins, emitAttr attr, regNumber reg, ssize_t sz += emitGetRexPrefixSize(ins); } -#if defined(_TARGET_X86_) && defined(LEGACY_BACKEND) - assert(reg < 8); -#endif - id = emitNewInstrSC(attr, val); id->idIns(ins); id->idInsFmt(fmt); @@ -3954,11 +3719,7 @@ void emitter::emitIns_C(instruction ins, emitAttr attr, CORINFO_FIELD_HANDLE fld } else { -#if FEATURE_STACK_FP_X87 - insFormat fmt = emitInsModeFormat(ins, IF_MRD, IF_TRD_MRD, IF_MWR_TRD); -#else // !FEATURE_STACK_FP_X87 insFormat fmt = emitInsModeFormat(ins, IF_MRD); -#endif // !FEATURE_STACK_FP_X87 id = emitNewInstrDsp(attr, offs); id->idIns(ins); @@ -4019,7 +3780,7 @@ void emitter::emitIns_R_R(instruction ins, emitAttr attr, regNumber reg1, regNum /* Special case: "XCHG" uses a different format */ insFormat fmt = (ins == INS_xchg) ? IF_RRW_RRW : emitInsModeFormat(ins, IF_RRD_RRD); - instrDesc* id = emitNewInstrTiny(attr); + instrDesc* id = emitNewInstrSmall(attr); id->idIns(ins); id->idInsFmt(fmt); id->idReg1(reg1); @@ -4061,13 +3822,11 @@ void emitter::emitIns_R_R_I(instruction ins, emitAttr attr, regNumber reg1, regN sz += emitGetRexPrefixSize(ins); } -#ifndef LEGACY_BACKEND if ((ins == INS_pextrq || ins == INS_pinsrq) && !UseVEXEncoding()) { assert(UseSSE4()); sz += 1; } -#endif // !LEGACY_BACKEND id->idIns(ins); id->idInsFmt(IF_RRW_RRW_CNS); @@ -4079,7 +3838,6 @@ void emitter::emitIns_R_R_I(instruction ins, emitAttr attr, regNumber reg1, regN emitCurIGsize += sz; } -#ifndef LEGACY_BACKEND void emitter::emitIns_AR(instruction ins, emitAttr attr, regNumber base, int offs) { assert(ins == INS_prefetcht0 || ins == INS_prefetcht1 || ins == INS_prefetcht2 || ins == INS_prefetchnta); @@ -4255,7 +4013,6 @@ void emitter::emitIns_R_R_A( dispIns(id); emitCurIGsize += sz; } -#endif // !LEGACY_BACKEND void emitter::emitIns_R_R_AR(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, regNumber base, int offs) { @@ -4356,7 +4113,6 @@ void emitter::emitIns_R_R_S(instruction ins, emitAttr attr, regNumber reg1, regN emitCurIGsize += sz; } -#ifndef LEGACY_BACKEND void emitter::emitIns_R_R_A_I( instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, GenTreeIndir* indir, int ival, insFormat fmt) { @@ -4402,7 +4158,6 @@ void emitter::emitIns_R_R_AR_I( dispIns(id); emitCurIGsize += sz; } -#endif // !LEGACY_BACKEND void emitter::emitIns_R_R_C_I( instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, CORINFO_FIELD_HANDLE fldHnd, int offs, int ival) @@ -4489,8 +4244,6 @@ void emitter::emitIns_R_R_S_I( emitCurIGsize += sz; } -#ifndef LEGACY_BACKEND - void emitter::emitIns_R_R_R_R( instruction ins, emitAttr attr, regNumber targetReg, regNumber reg1, regNumber reg2, regNumber reg3) { @@ -4518,8 +4271,6 @@ void emitter::emitIns_R_R_R_R( emitCurIGsize += sz; } -#endif // !LEGACY_BACKEND - /***************************************************************************** * * Add an instruction with a register + static member operands. @@ -4617,8 +4368,8 @@ void emitter::emitIns_C_R(instruction ins, emitAttr attr, CORINFO_FIELD_HANDLE f emitAttr size = EA_SIZE(attr); -#if defined(_TARGET_X86_) && !FEATURE_STACK_FP_X87 - // For x86 RyuJIT it is valid to storeind a double sized operand in an xmm reg to memory +#if defined(_TARGET_X86_) + // For x86 it is valid to storeind a double sized operand in an xmm reg to memory assert(size <= EA_8BYTE); #else assert(size <= EA_PTRSIZE); @@ -5037,11 +4788,7 @@ void emitter::emitIns_AR_R(instruction ins, emitAttr attr, regNumber ireg, regNu if (ireg == REG_NA) { -#if FEATURE_STACK_FP_X87 - fmt = emitInsModeFormat(ins, IF_ARD, IF_TRD_ARD, IF_AWR_TRD); -#else // !FEATURE_STACK_FP_X87 - fmt = emitInsModeFormat(ins, IF_ARD); -#endif // !FEATURE_STACK_FP_X87 + fmt = emitInsModeFormat(ins, IF_ARD); } else { @@ -5070,7 +4817,6 @@ void emitter::emitIns_AR_R(instruction ins, emitAttr attr, regNumber ireg, regNu emitAdjustStackDepthPushPop(ins); } -#ifndef LEGACY_BACKEND void emitter::emitIns_AR_R_I(instruction ins, emitAttr attr, regNumber base, int disp, regNumber ireg, int ival) { assert(ins == INS_vextracti128 || ins == INS_vextractf128); @@ -5093,7 +4839,6 @@ void emitter::emitIns_AR_R_I(instruction ins, emitAttr attr, regNumber base, int dispIns(id); emitCurIGsize += sz; } -#endif void emitter::emitIns_AI_R(instruction ins, emitAttr attr, regNumber ireg, ssize_t disp) { @@ -5103,11 +4848,7 @@ void emitter::emitIns_AI_R(instruction ins, emitAttr attr, regNumber ireg, ssize if (ireg == REG_NA) { -#if FEATURE_STACK_FP_X87 - fmt = emitInsModeFormat(ins, IF_ARD, IF_TRD_ARD, IF_AWR_TRD); -#else // FEATURE_STACK_FP_X87 - fmt = emitInsModeFormat(ins, IF_ARD); -#endif // FEATURE_STACK_FP_X87 + fmt = emitInsModeFormat(ins, IF_ARD); } else { @@ -5219,11 +4960,7 @@ void emitter::emitIns_ARR_R(instruction ins, emitAttr attr, regNumber ireg, regN if (ireg == REG_NA) { -#if FEATURE_STACK_FP_X87 - fmt = emitInsModeFormat(ins, IF_ARD, IF_TRD_ARD, IF_AWR_TRD); -#else // FEATURE_STACK_FP_X87 - fmt = emitInsModeFormat(ins, IF_ARD); -#endif // FEATURE_STACK_FP_X87 + fmt = emitInsModeFormat(ins, IF_ARD); } else { @@ -5340,11 +5077,7 @@ void emitter::emitIns_ARX_R( if (ireg == REG_NA) { -#if FEATURE_STACK_FP_X87 - fmt = emitInsModeFormat(ins, IF_ARD, IF_TRD_ARD, IF_AWR_TRD); -#else // !FEATURE_STACK_FP_X87 - fmt = emitInsModeFormat(ins, IF_ARD); -#endif // !FEATURE_STACK_FP_X87 + fmt = emitInsModeFormat(ins, IF_ARD); } else { @@ -5457,11 +5190,7 @@ void emitter::emitIns_AX_R(instruction ins, emitAttr attr, regNumber ireg, regNu if (ireg == REG_NA) { -#if FEATURE_STACK_FP_X87 - fmt = emitInsModeFormat(ins, IF_ARD, IF_TRD_ARD, IF_AWR_TRD); -#else // !FEATURE_STACK_FP_X87 - fmt = emitInsModeFormat(ins, IF_ARD); -#endif // !FEATURE_STACK_FP_X87 + fmt = emitInsModeFormat(ins, IF_ARD); } else { @@ -5790,13 +5519,9 @@ void emitter::emitIns_SIMD_R_R_S_I( void emitter::emitIns_S(instruction ins, emitAttr attr, int varx, int offs) { - instrDesc* id = emitNewInstr(attr); - UNATIVE_OFFSET sz = emitInsSizeSV(insCodeMR(ins), varx, offs); -#if FEATURE_STACK_FP_X87 - insFormat fmt = emitInsModeFormat(ins, IF_SRD, IF_TRD_SRD, IF_SWR_TRD); -#else // !FEATURE_STACK_FP_X87 - insFormat fmt = emitInsModeFormat(ins, IF_SRD); -#endif // !FEATURE_STACK_FP_X87 + instrDesc* id = emitNewInstr(attr); + UNATIVE_OFFSET sz = emitInsSizeSV(insCodeMR(ins), varx, offs); + insFormat fmt = emitInsModeFormat(ins, IF_SRD); // 16-bit operand instructions will need a prefix if (EA_SIZE(attr) == EA_2BYTE) @@ -6628,20 +6353,17 @@ void emitter::emitInsSanityCheck(instrDesc* id) idOp = ID_OP_CNS; } - if (!id->idIsTiny()) + if (id->idIsDspReloc()) { - if (id->idIsDspReloc()) - { - assert(idOp == ID_OP_NONE || idOp == ID_OP_AMD || idOp == ID_OP_DSP || idOp == ID_OP_DSP_CNS || - idOp == ID_OP_AMD_CNS || idOp == ID_OP_SPEC || idOp == ID_OP_CALL || idOp == ID_OP_JMP || - idOp == ID_OP_LBL); - } + assert(idOp == ID_OP_NONE || idOp == ID_OP_AMD || idOp == ID_OP_DSP || idOp == ID_OP_DSP_CNS || + idOp == ID_OP_AMD_CNS || idOp == ID_OP_SPEC || idOp == ID_OP_CALL || idOp == ID_OP_JMP || + idOp == ID_OP_LBL); + } - if (id->idIsCnsReloc()) - { - assert(idOp == ID_OP_CNS || idOp == ID_OP_AMD_CNS || idOp == ID_OP_DSP_CNS || idOp == ID_OP_SPEC || - idOp == ID_OP_CALL || idOp == ID_OP_JMP); - } + if (id->idIsCnsReloc()) + { + assert(idOp == ID_OP_CNS || idOp == ID_OP_AMD_CNS || idOp == ID_OP_DSP_CNS || idOp == ID_OP_SPEC || + idOp == ID_OP_CALL || idOp == ID_OP_JMP); } } #endif @@ -6653,11 +6375,6 @@ void emitter::emitInsSanityCheck(instrDesc* id) size_t emitter::emitSizeOfInsDsc(instrDesc* id) { - if (emitIsTinyInsDsc(id)) - { - return TINY_IDSC_SIZE; - } - if (emitIsScnsInsDsc(id)) { return SMALL_IDSC_SIZE; @@ -6853,7 +6570,6 @@ const char* emitter::emitRegName(regNumber reg, emitAttr attr, bool varName) switch (EA_SIZE(attr)) { -#ifndef LEGACY_BACKEND case EA_32BYTE: return emitYMMregName(reg); @@ -6873,10 +6589,6 @@ const char* emitter::emitRegName(regNumber reg, emitAttr attr, bool varName) return emitXMMregName(reg); } break; -#else // LEGACY_BACKEND - case EA_4BYTE: - break; -#endif // LEGACY_BACKEND case EA_2BYTE: rn++; @@ -6943,11 +6655,7 @@ const char* emitter::emitXMMregName(unsigned reg) { static const char* const regNames[] = { #define REGDEF(name, rnum, mask, sname) "x" sname, -#ifndef LEGACY_BACKEND #include "register.h" -#else // LEGACY_BACKEND -#include "registerxmm.h" -#endif // LEGACY_BACKEND }; assert(reg < REG_COUNT); @@ -6965,11 +6673,7 @@ const char* emitter::emitYMMregName(unsigned reg) { static const char* const regNames[] = { #define REGDEF(name, rnum, mask, sname) "y" sname, -#ifndef LEGACY_BACKEND #include "register.h" -#else // LEGACY_BACKEND -#include "registerxmm.h" -#endif // LEGACY_BACKEND }; assert(reg < REG_COUNT); @@ -7509,17 +7213,6 @@ void emitter::emitDispIns( case IF_MWR: case IF_MRW: -#if FEATURE_STACK_FP_X87 - - case IF_TRD_MRD: - case IF_TWR_MRD: - case IF_TRW_MRD: - - // case IF_MRD_TRD: - // case IF_MRW_TRD: - case IF_MWR_TRD: - -#endif // FEATURE_STACK_FP_X87 case IF_MRD_OFF: /* Is this actually a reference to a data section? */ @@ -7734,17 +7427,6 @@ void emitter::emitDispIns( case IF_AWR: case IF_ARW: -#if FEATURE_STACK_FP_X87 - - case IF_TRD_ARD: - case IF_TWR_ARD: - case IF_TRW_ARD: - - // case IF_ARD_TRD: - case IF_AWR_TRD: -// case IF_ARW_TRD: - -#endif // FEATURE_STACK_FP_X87 if (ins == INS_call && id->idIsCallRegPtr()) { printf("%s", emitRegName(id->idAddr()->iiaAddrMode.amBaseReg)); @@ -7914,17 +7596,6 @@ void emitter::emitDispIns( case IF_SWR: case IF_SRW: -#if FEATURE_STACK_FP_X87 - case IF_TRD_SRD: - case IF_TWR_SRD: - case IF_TRW_SRD: - - // case IF_SRD_TRD: - // case IF_SRW_TRD: - case IF_SWR_TRD: - -#endif // FEATURE_STACK_FP_X87 - printf("%s", sstr); #if !FEATURE_FIXED_OUT_ARGS @@ -8077,17 +7748,11 @@ void emitter::emitDispIns( { printf("%s, %s", emitRegName(id->idReg1(), EA_4BYTE), emitRegName(id->idReg2(), attr)); } -#ifndef LEGACY_BACKEND else if ((ins == INS_cvtsi2ss) || (ins == INS_cvtsi2sd)) { printf(" %s, %s", emitRegName(id->idReg1(), EA_16BYTE), emitRegName(id->idReg2(), attr)); } -#endif - else if ((ins == INS_cvttsd2si) -#ifndef LEGACY_BACKEND - || (ins == INS_cvtss2si) || (ins == INS_cvtsd2si) || (ins == INS_cvttss2si) -#endif - || 0) + else if ((ins == INS_cvttsd2si) || (ins == INS_cvtss2si) || (ins == INS_cvtsd2si) || (ins == INS_cvttss2si)) { printf(" %s, %s", emitRegName(id->idReg1(), attr), emitRegName(id->idReg2(), EA_16BYTE)); } @@ -8323,18 +7988,6 @@ void emitter::emitDispIns( case IF_MWR: case IF_MRW: -#if FEATURE_STACK_FP_X87 - - case IF_TRD_MRD: - case IF_TWR_MRD: - case IF_TRW_MRD: - - // case IF_MRD_TRD: - // case IF_MRW_TRD: - case IF_MWR_TRD: - -#endif // FEATURE_STACK_FP_X87 - printf("%s", sstr); offs = emitGetInsDsp(id); emitDispClsVar(id->idAddr()->iiaFieldHnd, offs, ID_INFO_DSP_RELOC); @@ -8363,32 +8016,6 @@ void emitter::emitDispIns( } break; -#if FEATURE_STACK_FP_X87 - case IF_TRD_FRD: - case IF_TWR_FRD: - case IF_TRW_FRD: - switch (ins) - { - case INS_fld: - case INS_fxch: - break; - - default: - printf("%s, ", emitFPregName(0)); - break; - } - printf("%s", emitFPregName((unsigned)id->idReg1())); - break; - - case IF_FRD_TRD: - case IF_FWR_TRD: - case IF_FRW_TRD: - printf("%s", emitFPregName((unsigned)id->idReg1())); - if (ins != INS_fst && ins != INS_fstp) - printf(", %s", emitFPregName(0)); - break; -#endif // FEATURE_STACK_FP_X87 - case IF_LABEL: case IF_RWR_LABEL: case IF_SWR_LABEL: @@ -8461,11 +8088,6 @@ void emitter::emitDispIns( break; -#if FEATURE_STACK_FP_X87 - case IF_TRD: - case IF_TWR: - case IF_TRW: -#endif // FEATURE_STACK_FP_X87 case IF_NONE: break; @@ -8820,12 +8442,7 @@ BYTE* emitter::emitOutputAM(BYTE* dst, instrDesc* id, code_t code, CnsVal* addc) } else if (CodeGen::instIsFP(ins)) { -#if FEATURE_STACK_FP_X87 - assert(size == EA_4BYTE || size == EA_8BYTE || ins == INS_fldcw || ins == INS_fnstcw); -#else // !FEATURE_STACK_FP_X87 assert(size == EA_4BYTE || size == EA_8BYTE); -#endif // ! FEATURE_STACK_FP_X87 - if (size == EA_8BYTE) { code += 4; @@ -9033,19 +8650,6 @@ GOT_DSP: break; case REG_ESP: -#ifdef LEGACY_BACKEND - // REG_ESP could be REG_R12, which applies to any instruction - // - // This assert isn't too helpful from the OptJit point of view - // - // a better question is why is it here at all - // - assert((ins == INS_lea) || (ins == INS_mov) || (ins == INS_test) || (ins == INS_cmp) || - (ins == INS_fld && dspIsZero) || (ins == INS_fstp && dspIsZero) || - (ins == INS_fistp && dspIsZero) || IsSSE2Instruction(ins) || IsAVXInstruction(ins) || - (ins == INS_or)); -#endif // LEGACY_BACKEND - if (Is4ByteSSE4OrAVXInstruction(ins)) { // Is the offset 0 or does it at least fit in a byte? @@ -10063,7 +9667,6 @@ BYTE* emitter::emitOutputCV(BYTE* dst, instrDesc* id, code_t code, CnsVal* addc) int byteSize = EA_SIZE_IN_BYTES(size); -#ifndef LEGACY_BACKEND // this instruction has a fixed size (4) src. if (ins == INS_cvttss2si || ins == INS_cvtss2sd || ins == INS_vbroadcastss) { @@ -10074,7 +9677,6 @@ BYTE* emitter::emitOutputCV(BYTE* dst, instrDesc* id, code_t code, CnsVal* addc) { byteSize = 8; } -#endif // !LEGACY_BACKEND // Check that the offset is properly aligned (i.e. the ddd in [ddd]) assert((emitChkAlign == false) || (ins == INS_lea) || (((size_t)addr & (byteSize - 1)) == 0)); @@ -10756,9 +10358,7 @@ BYTE* emitter::emitOutputRR(BYTE* dst, instrDesc* id) // If we got here, the GC-ness of the registers doesn't match, so we have to "swap" them in the GC // register pointer mask. - CLANG_FORMAT_COMMENT_ANCHOR; -#ifndef LEGACY_BACKEND GCtype gc1, gc2; gc1 = emitRegGCtype(reg1); @@ -10790,7 +10390,6 @@ BYTE* emitter::emitOutputRR(BYTE* dst, instrDesc* id) emitGCregLiveUpd(gc2, reg1, dst); } } -#endif // !LEGACY_BACKEND break; default: @@ -10933,7 +10532,6 @@ BYTE* emitter::emitOutputRI(BYTE* dst, instrDesc* id) noway_assert(emitVerifyEncodable(ins, size, reg)); -#ifndef LEGACY_BACKEND if (IsSSEOrAVXInstruction(ins)) { // Handle SSE2 instructions of the form "opcode reg, immed8" @@ -11002,7 +10600,6 @@ BYTE* emitter::emitOutputRI(BYTE* dst, instrDesc* id) return dst; } -#endif // !LEGACY_BACKEND // The 'mov' opcode is special if (ins == INS_mov) @@ -11759,7 +11356,7 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) // the loop alignment pseudo instruction if (ins == INS_align) { - sz = TINY_IDSC_SIZE; + sz = SMALL_IDSC_SIZE; dst = emitOutputNOP(dst, (-(int)(size_t)dst) & 0x0f); assert(((size_t)dst & 0x0f) == 0); break; @@ -11777,14 +11374,6 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) emitGCregDeadUpd(REG_EDX, dst); } - __fallthrough; - -#if FEATURE_STACK_FP_X87 - case IF_TRD: - case IF_TWR: - case IF_TRW: -#endif // FEATURE_STACK_FP_X87 - assert(id->idGCref() == GCT_NONE); code = insCodeMR(ins); @@ -12044,7 +11633,7 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) case IF_RWR: case IF_RRW: dst = emitOutputR(dst, id); - sz = TINY_IDSC_SIZE; + sz = SMALL_IDSC_SIZE; break; /********************************************************************/ @@ -12090,7 +11679,7 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) case IF_RRW_RRD: case IF_RRW_RRW: dst = emitOutputRR(dst, id); - sz = TINY_IDSC_SIZE; + sz = SMALL_IDSC_SIZE; break; case IF_RRD_CNS: @@ -12225,18 +11814,6 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) case IF_AWR: case IF_ARW: -#if FEATURE_STACK_FP_X87 - - case IF_TRD_ARD: - case IF_TWR_ARD: - case IF_TRW_ARD: - - // case IF_ARD_TRD: - // case IF_ARW_TRD: - case IF_AWR_TRD: - -#endif // FEATURE_STACK_FP_X87 - dst = emitCodeWithInstructionSize(dst, emitOutputAM(dst, id, insCodeMR(ins)), &callInstrSize); switch (ins) @@ -12375,18 +11952,6 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) case IF_SWR: case IF_SRW: -#if FEATURE_STACK_FP_X87 - - case IF_TRD_SRD: - case IF_TWR_SRD: - case IF_TRW_SRD: - - // case IF_SRD_TRD: - // case IF_SRW_TRD: - case IF_SWR_TRD: - -#endif // FEATURE_STACK_FP_X87 - assert(ins != INS_pop_hide); if (ins == INS_pop) { @@ -12565,18 +12130,6 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) case IF_MRW: case IF_MWR: -#if FEATURE_STACK_FP_X87 - - case IF_TRD_MRD: - case IF_TWR_MRD: - case IF_TRW_MRD: - - // case IF_MRD_TRD: - // case IF_MRW_TRD: - case IF_MWR_TRD: - -#endif // FEATURE_STACK_FP_X87 - noway_assert(ins != INS_call); dst = emitOutputCV(dst, id, insCodeMR(ins) | 0x0500); sz = emitSizeOfInsDsc(id); @@ -12762,28 +12315,6 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) sz = emitSizeOfInsDsc(id); break; -#if FEATURE_STACK_FP_X87 - - /********************************************************************/ - /* FP coprocessor stack operands */ - /********************************************************************/ - - case IF_TRD_FRD: - case IF_TWR_FRD: - case IF_TRW_FRD: - assert(id->idGCref() == GCT_NONE); - dst += emitOutputWord(dst, insCodeMR(ins) | 0xC000 | (id->idReg1() << 8)); - break; - - case IF_FRD_TRD: - case IF_FWR_TRD: - case IF_FRW_TRD: - assert(id->idGCref() == GCT_NONE); - dst += emitOutputWord(dst, insCodeMR(ins) | 0xC004 | (id->idReg1() << 8)); - break; - -#endif // FEATURE_STACK_FP_X87 - /********************************************************************/ /* oops */ /********************************************************************/ diff --git a/src/jit/emitxarch.h b/src/jit/emitxarch.h index 4cfa24fcde..f85157e1eb 100644 --- a/src/jit/emitxarch.h +++ b/src/jit/emitxarch.h @@ -30,12 +30,8 @@ inline static bool isDoubleReg(regNumber reg) // code_t is a type used to accumulate bits of opcode + prefixes. On amd64, it must be 64 bits // to support the REX prefixes. On both x86 and amd64, it must be 64 bits to support AVX, with -// its 3-byte VEX prefix. For legacy backend (which doesn't support AVX), leave it as size_t. -#if defined(LEGACY_BACKEND) -typedef size_t code_t; -#else // !defined(LEGACY_BACKEND) +// its 3-byte VEX prefix. typedef unsigned __int64 code_t; -#endif // !defined(LEGACY_BACKEND) struct CnsVal { @@ -117,8 +113,6 @@ bool hasRexPrefix(code_t code) #endif // !_TARGET_AMD64_ } -#ifndef LEGACY_BACKEND - // 3-byte VEX prefix starts with byte 0xC4 #define VEX_PREFIX_MASK_3BYTE 0xFF000000000000ULL #define VEX_PREFIX_CODE_3BYTE 0xC4000000000000ULL @@ -196,70 +190,6 @@ bool isPrefetch(instruction ins) { return (ins == INS_prefetcht0) || (ins == INS_prefetcht1) || (ins == INS_prefetcht2) || (ins == INS_prefetchnta); } -#else // LEGACY_BACKEND -bool UseVEXEncoding() -{ - return false; -} -void SetUseVEXEncoding(bool value) -{ -} -bool ContainsAVX() -{ - return false; -} -void SetContainsAVX(bool value) -{ -} -bool Contains256bitAVX() -{ - return false; -} -void SetContains256bitAVX(bool value) -{ -} -bool hasVexPrefix(code_t code) -{ - return false; -} -bool IsDstDstSrcAVXInstruction(instruction ins) -{ - return false; -} -bool IsDstSrcSrcAVXInstruction(instruction ins) -{ - return false; -} -bool IsThreeOperandAVXInstruction(instruction ins) -{ - return false; -} -bool isAvxBlendv(instruction ins) -{ - return false; -} -bool isSse41Blendv(instruction ins) -{ - return false; -} -bool TakesVexPrefix(instruction ins) -{ - return false; -} -bool isPrefetch(instruction ins) -{ - return false; -} - -code_t AddVexPrefixIfNeeded(instruction ins, code_t code, emitAttr attr) -{ - return code; -} -code_t AddVexPrefixIfNeededAndNotPresent(instruction ins, code_t code, emitAttr size) -{ - return code; -} -#endif // LEGACY_BACKEND /************************************************************************/ /* Debug-only routines to display instructions */ @@ -386,7 +316,6 @@ void emitIns_R_R(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2) void emitIns_R_R_I(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, int ival); -#ifndef LEGACY_BACKEND void emitIns_AR(instruction ins, emitAttr attr, regNumber base, int offs); void emitIns_R_A(instruction ins, emitAttr attr, regNumber reg1, GenTreeIndir* indir, insFormat fmt); @@ -400,7 +329,6 @@ void emitIns_R_C_I(instruction ins, emitAttr attr, regNumber reg1, CORINFO_FIELD void emitIns_R_S_I(instruction ins, emitAttr attr, regNumber reg1, int varx, int offs, int ival); void emitIns_R_R_A(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, GenTreeIndir* indir, insFormat fmt); -#endif // !LEGACY_BACKEND void emitIns_R_R_AR(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, regNumber base, int offs); @@ -411,13 +339,11 @@ void emitIns_R_R_S(instruction ins, emitAttr attr, regNumber reg1, regNumber reg void emitIns_R_R_R(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, regNumber reg3); -#ifndef LEGACY_BACKEND void emitIns_R_R_A_I( instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, GenTreeIndir* indir, int ival, insFormat fmt); void emitIns_R_R_AR_I( instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, regNumber base, int offs, int ival); void emitIns_AR_R_I(instruction ins, emitAttr attr, regNumber base, int disp, regNumber ireg, int ival); -#endif // !LEGACY_BACKEND void emitIns_R_R_C_I( instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, CORINFO_FIELD_HANDLE fldHnd, int offs, int ival); @@ -426,9 +352,7 @@ void emitIns_R_R_R_I(instruction ins, emitAttr attr, regNumber reg1, regNumber r void emitIns_R_R_S_I(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, int varx, int offs, int ival); -#ifndef LEGACY_BACKEND void emitIns_R_R_R_R(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, regNumber reg3, regNumber reg4); -#endif // !LEGACY_BACKEND void emitIns_S(instruction ins, emitAttr attr, int varx, int offs); @@ -502,12 +426,6 @@ void emitIns_SIMD_R_R_R_R( instruction ins, emitAttr attr, regNumber reg, regNumber reg1, regNumber reg2, regNumber reg3); #endif // FEATURE_HW_INTRINSICS -#if FEATURE_STACK_FP_X87 -void emitIns_F_F0(instruction ins, unsigned fpreg); - -void emitIns_F0_F(instruction ins, unsigned fpreg); -#endif // FEATURE_STACK_FP_X87 - enum EmitCallType { EC_FUNC_TOKEN, // Direct call to a helper/static/nonvirtual/global method diff --git a/src/jit/error.cpp b/src/jit/error.cpp index f42dcef5c6..4b4d075b9e 100644 --- a/src/jit/error.cpp +++ b/src/jit/error.cpp @@ -129,7 +129,7 @@ void noWayAssertBodyConditional( } } -#if defined(ALT_JIT) && (!defined(_TARGET_X86_) || !defined(LEGACY_BACKEND)) +#if defined(ALT_JIT) /*****************************************************************************/ void notYetImplemented(const char* msg, const char* filename, unsigned line) @@ -193,7 +193,7 @@ void notYetImplemented(const char* msg, const char* filename, unsigned line) } } -#endif // #if defined(ALT_JIT) && (!defined(_TARGET_X86_) || !defined(LEGACY_BACKEND)) +#endif // #if defined(ALT_JIT) /*****************************************************************************/ LONG __JITfilter(PEXCEPTION_POINTERS pExceptionPointers, LPVOID lpvParam) diff --git a/src/jit/error.h b/src/jit/error.h index 78f24adb38..944eaca263 100644 --- a/src/jit/error.h +++ b/src/jit/error.h @@ -150,7 +150,7 @@ extern void RecordNowayAssertGlobal(const char* filename, unsigned line, const c // limitations (that could be removed in the future) #define IMPL_LIMITATION(msg) NO_WAY(msg) -#if !defined(_TARGET_X86_) || !defined(LEGACY_BACKEND) +#if 1 // All platforms currently enable NYI; this should be a tighter condition to exclude some platforms from NYI #if defined(ALT_JIT) @@ -210,18 +210,6 @@ extern void notYetImplemented(const char* msg, const char* file, unsigned line); #endif // NYI not available -#if !defined(_TARGET_X86_) && !defined(FEATURE_STACK_FP_X87) - -#define NYI_FLAT_FP_X87(msg) NYI(msg) -#define NYI_FLAT_FP_X87_NC(msg) NYI(msg) - -#else - -#define NYI_FLAT_FP_X87(msg) do { } while (0) -#define NYI_FLAT_FP_X87_NC(msg) do { } while (0) - -#endif // !_TARGET_X86_ && !FEATURE_STACK_FP_X87 - // clang-format on #if defined(_HOST_X86_) && !defined(FEATURE_PAL) diff --git a/src/jit/flowgraph.cpp b/src/jit/flowgraph.cpp index 0c7b6cafb3..9df2dde039 100644 --- a/src/jit/flowgraph.cpp +++ b/src/jit/flowgraph.cpp @@ -17,9 +17,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #endif #include "allocacheck.h" // for alloca -#ifndef LEGACY_BACKEND -#include "lower.h" // for LowerRange() -#endif +#include "lower.h" // for LowerRange() /*****************************************************************************/ @@ -3556,21 +3554,6 @@ void Compiler::fgInitBlockVarSets() block->InitVarSets(this); } -#ifdef LEGACY_BACKEND - // QMarks are much like blocks, and need their VarSets initialized. - assert(!compIsForInlining()); - for (unsigned i = 0; i < compQMarks->Size(); i++) - { - GenTree* qmark = compQMarks->Get(i); - // Perhaps the gtOper of a QMark node was changed to something else since it was created and put on this list. - // So can't hurt to check. - if (qmark->OperGet() == GT_QMARK) - { - VarSetOps::AssignAllowUninitRhs(this, qmark->gtQmark.gtThenLiveSet, VarSetOps::UninitVal()); - VarSetOps::AssignAllowUninitRhs(this, qmark->gtQmark.gtElseLiveSet, VarSetOps::UninitVal()); - } - } -#endif // LEGACY_BACKEND fgBBVarSetsInited = true; } @@ -3887,9 +3870,6 @@ bool Compiler::fgCreateGCPoll(GCPollType pollType, BasicBlock* block) { createdPollBlocks = false; GenTreeCall* call = gtNewHelperCallNode(CORINFO_HELP_POLL_GC, TYP_VOID); -#ifdef LEGACY_BACKEND - call->gtFlags |= GTF_CALL_REG_SAVE; -#endif // LEGACY_BACKEND // for BBJ_ALWAYS I don't need to insert it before the condition. Just append it. if (block->bbJumpKind == BBJ_ALWAYS) @@ -3968,9 +3948,6 @@ bool Compiler::fgCreateGCPoll(GCPollType pollType, BasicBlock* block) // 2) Add a GC_CALL node to Poll. GenTreeCall* call = gtNewHelperCallNode(CORINFO_HELP_POLL_GC, TYP_VOID); -#ifdef LEGACY_BACKEND - call->gtFlags |= GTF_CALL_REG_SAVE; -#endif // LEGACY_BACKEND fgInsertStmtAtEnd(poll, call); // 3) Remove the last statement from Top and add it to Bottom. @@ -5428,7 +5405,6 @@ unsigned Compiler::fgMakeBasicBlocks(const BYTE* codeAddr, IL_OFFSET codeSize, B jmpKind = BBJ_SWITCH; fgHasSwitch = true; -#ifndef LEGACY_BACKEND if (opts.compProcedureSplitting) { // TODO-CQ: We might need to create a switch table; we won't know for sure until much later. @@ -5444,7 +5420,6 @@ unsigned Compiler::fgMakeBasicBlocks(const BYTE* codeAddr, IL_OFFSET codeSize, B JITDUMP("Turning off procedure splitting for this method, as it might need switch tables; " "implementation limitation.\n"); } -#endif // !LEGACY_BACKEND } goto GOT_ENDP; @@ -8769,15 +8744,13 @@ void Compiler::fgAddInternal() { noway_assert(!compIsForInlining()); -#ifndef LEGACY_BACKEND - // The RyuJIT backend requires a scratch BB into which it can safely insert a P/Invoke method prolog if one is + // The backend requires a scratch BB into which it can safely insert a P/Invoke method prolog if one is // required. Create it here. if (info.compCallUnmanaged != 0) { fgEnsureFirstBBisScratch(); fgFirstBB->bbFlags |= BBF_DONT_REMOVE; } -#endif // !LEGACY_BACKEND /* VSW441487 @@ -9568,131 +9541,104 @@ void Compiler::fgSimpleLowering() // Walk the statement trees in this basic block. compCurBB = block; // Used in fgRngChkTarget. -#ifdef LEGACY_BACKEND - for (GenTreeStmt* stmt = block->FirstNonPhiDef(); stmt; stmt = stmt->gtNextStmt) - { - for (GenTree* tree = stmt->gtStmtList; tree; tree = tree->gtNext) - { -#else - LIR::Range& range = LIR::AsRange(block); for (GenTree* tree : range) { + switch (tree->OperGet()) { -#endif - - switch (tree->OperGet()) + case GT_ARR_LENGTH: { - case GT_ARR_LENGTH: - { - GenTreeArrLen* arrLen = tree->AsArrLen(); - GenTree* arr = arrLen->gtArrLen.ArrRef(); - GenTree* add; - GenTree* con; + GenTreeArrLen* arrLen = tree->AsArrLen(); + GenTree* arr = arrLen->gtArrLen.ArrRef(); + GenTree* add; + GenTree* con; - /* Create the expression "*(array_addr + ArrLenOffs)" */ + /* Create the expression "*(array_addr + ArrLenOffs)" */ - noway_assert(arr->gtNext == tree); + noway_assert(arr->gtNext == tree); - noway_assert(arrLen->ArrLenOffset() == offsetof(CORINFO_Array, length) || - arrLen->ArrLenOffset() == offsetof(CORINFO_String, stringLen)); + noway_assert(arrLen->ArrLenOffset() == offsetof(CORINFO_Array, length) || + arrLen->ArrLenOffset() == offsetof(CORINFO_String, stringLen)); - if ((arr->gtOper == GT_CNS_INT) && (arr->gtIntCon.gtIconVal == 0)) - { - // If the array is NULL, then we should get a NULL reference - // exception when computing its length. We need to maintain - // an invariant where there is no sum of two constants node, so - // let's simply return an indirection of NULL. - - add = arr; - } - else - { - con = gtNewIconNode(arrLen->ArrLenOffset(), TYP_I_IMPL); - con->gtRsvdRegs = RBM_NONE; - - add = gtNewOperNode(GT_ADD, TYP_REF, arr, con); - add->gtRsvdRegs = arr->gtRsvdRegs; - -#ifdef LEGACY_BACKEND - con->gtCopyFPlvl(arr); - - add->gtCopyFPlvl(arr); - add->CopyCosts(arr); + if ((arr->gtOper == GT_CNS_INT) && (arr->gtIntCon.gtIconVal == 0)) + { + // If the array is NULL, then we should get a NULL reference + // exception when computing its length. We need to maintain + // an invariant where there is no sum of two constants node, so + // let's simply return an indirection of NULL. - arr->gtNext = con; - con->gtPrev = arr; + add = arr; + } + else + { + con = gtNewIconNode(arrLen->ArrLenOffset(), TYP_I_IMPL); + con->gtRsvdRegs = RBM_NONE; - con->gtNext = add; - add->gtPrev = con; + add = gtNewOperNode(GT_ADD, TYP_REF, arr, con); + add->gtRsvdRegs = arr->gtRsvdRegs; - add->gtNext = tree; - tree->gtPrev = add; -#else - range.InsertAfter(arr, con, add); -#endif - } + range.InsertAfter(arr, con, add); + } - // Change to a GT_IND. - tree->ChangeOperUnchecked(GT_IND); + // Change to a GT_IND. + tree->ChangeOperUnchecked(GT_IND); - tree->gtOp.gtOp1 = add; - break; - } + tree->gtOp.gtOp1 = add; + break; + } - case GT_ARR_BOUNDS_CHECK: + case GT_ARR_BOUNDS_CHECK: #ifdef FEATURE_SIMD - case GT_SIMD_CHK: + case GT_SIMD_CHK: #endif // FEATURE_SIMD #ifdef FEATURE_HW_INTRINSICS - case GT_HW_INTRINSIC_CHK: + case GT_HW_INTRINSIC_CHK: #endif // FEATURE_HW_INTRINSICS - { - // Add in a call to an error routine. - fgSetRngChkTarget(tree, false); - break; - } + { + // Add in a call to an error routine. + fgSetRngChkTarget(tree, false); + break; + } #if FEATURE_FIXED_OUT_ARGS - case GT_CALL: + case GT_CALL: + { + GenTreeCall* call = tree->AsCall(); + // Fast tail calls use the caller-supplied scratch + // space so have no impact on this method's outgoing arg size. + if (!call->IsFastTailCall()) { - GenTreeCall* call = tree->AsCall(); - // Fast tail calls use the caller-supplied scratch - // space so have no impact on this method's outgoing arg size. - if (!call->IsFastTailCall()) - { - // Update outgoing arg size to handle this call - const unsigned thisCallOutAreaSize = call->fgArgInfo->GetOutArgSize(); - assert(thisCallOutAreaSize >= MIN_ARG_AREA_FOR_CALL); + // Update outgoing arg size to handle this call + const unsigned thisCallOutAreaSize = call->fgArgInfo->GetOutArgSize(); + assert(thisCallOutAreaSize >= MIN_ARG_AREA_FOR_CALL); - if (thisCallOutAreaSize > outgoingArgSpaceSize) - { - outgoingArgSpaceSize = thisCallOutAreaSize; - JITDUMP("Bumping outgoingArgSpaceSize to %u for call [%06d]\n", outgoingArgSpaceSize, - dspTreeID(tree)); - } - else - { - JITDUMP("outgoingArgSpaceSize %u sufficient for call [%06d], which needs %u\n", - outgoingArgSpaceSize, dspTreeID(tree), thisCallOutAreaSize); - } + if (thisCallOutAreaSize > outgoingArgSpaceSize) + { + outgoingArgSpaceSize = thisCallOutAreaSize; + JITDUMP("Bumping outgoingArgSpaceSize to %u for call [%06d]\n", outgoingArgSpaceSize, + dspTreeID(tree)); } else { - JITDUMP("outgoingArgSpaceSize not impacted by fast tail call [%06d]\n", dspTreeID(tree)); + JITDUMP("outgoingArgSpaceSize %u sufficient for call [%06d], which needs %u\n", + outgoingArgSpaceSize, dspTreeID(tree), thisCallOutAreaSize); } - break; } -#endif // FEATURE_FIXED_OUT_ARGS - - default: + else { - // No other operators need processing. - break; + JITDUMP("outgoingArgSpaceSize not impacted by fast tail call [%06d]\n", dspTreeID(tree)); } + break; } - } // foreach gtNext - } // foreach Stmt +#endif // FEATURE_FIXED_OUT_ARGS + + default: + { + // No other operators need processing. + break; + } + } // switch on oper + } // foreach tree } // foreach BB #if FEATURE_FIXED_OUT_ARGS @@ -9796,9 +9742,7 @@ VARSET_VALRET_TP Compiler::fgGetVarBits(GenTree* tree) // For more details see Compiler::raAssignVars() method. else if (tree->gtType == TYP_STRUCT && varDsc->lvPromoted) { -#ifndef LEGACY_BACKEND assert(varDsc->lvType == TYP_STRUCT); -#endif for (unsigned i = varDsc->lvFieldLclStart; i < varDsc->lvFieldLclStart + varDsc->lvFieldCnt; ++i) { noway_assert(lvaTable[i].lvIsStructField); @@ -13954,10 +13898,8 @@ bool Compiler::fgOptimizeEmptyBlock(BasicBlock* block) if (block->IsLIR()) { LIR::AsRange(block).InsertAtEnd(nop); -#ifndef LEGACY_BACKEND LIR::ReadOnlyRange range(nop, nop); m_pLowering->LowerRange(block, range); -#endif } else { @@ -14266,7 +14208,6 @@ bool Compiler::fgOptimizeSwitchBranches(BasicBlock* block) GenTree* switchVal = switchTree->gtOp.gtOp1; noway_assert(genActualTypeIsIntOrI(switchVal->TypeGet())); -#ifndef LEGACY_BACKEND // If we are in LIR, remove the jump table from the block. if (block->IsLIR()) { @@ -14274,7 +14215,6 @@ bool Compiler::fgOptimizeSwitchBranches(BasicBlock* block) assert(jumpTable->OperGet() == GT_JMPTABLE); blockRange->Remove(jumpTable); } -#endif // Change the GT_SWITCH(switchVal) into GT_JTRUE(GT_EQ(switchVal==0)). // Also mark the node as GTF_DONT_CSE as further down JIT is not capable of handling it. @@ -14302,10 +14242,8 @@ bool Compiler::fgOptimizeSwitchBranches(BasicBlock* block) if (block->IsLIR()) { blockRange->InsertAfter(switchVal, zeroConstNode, condNode); -#ifndef LEGACY_BACKEND LIR::ReadOnlyRange range(zeroConstNode, switchTree); m_pLowering->LowerRange(block, range); -#endif // !LEGACY_BACKEND } else { @@ -16275,16 +16213,6 @@ void Compiler::fgDetermineFirstColdBlock() fgFirstColdBlock = nullptr; -#if FEATURE_STACK_FP_X87 - if (compMayHaveTransitionBlocks) - { - opts.compProcedureSplitting = false; - - // See comment above declaration of compMayHaveTransitionBlocks for comments on this - JITDUMP("Turning off procedure splitting for this method, as it may end up having FP transition blocks\n"); - } -#endif // FEATURE_STACK_FP_X87 - if (!opts.compProcedureSplitting) { JITDUMP("No procedure splitting will be done for this method\n"); @@ -19205,115 +19133,6 @@ void Compiler::fgSetBlockOrder(BasicBlock* block) } } -#ifdef LEGACY_BACKEND -//------------------------------------------------------------------------ -// fgOrderBlockOps: Get the execution order for a block assignment -// -// Arguments: -// tree - The block assignment -// reg0 - The register for the destination -// reg1 - The register for the source -// reg2 - The register for the size -// opsPtr - An array of 3 GenTree*'s, an out argument for the operands, in order -// regsPtr - An array of three regMaskTP - an out argument for the registers, in order -// -// Return Value: -// The return values go into the arrays that are passed in, and provide the -// operands and associated registers, in execution order. -// -// Notes: -// This method is somewhat convoluted in order to preserve old behavior from when -// block assignments had their dst and src in a GT_LIST as op1, and their size as op2. -// The old tree was like this: -// tree->gtOp -// / \ -// GT_LIST [size/clsHnd] -// / \ -// [dest] [val/src] -// -// The new tree looks like this: -// GT_ASG -// / \ -// blk/obj [val/src] -// / \ -// [destAddr] [*size/clsHnd] *only for GT_DYN_BLK -// -// For the (usual) case of GT_BLK or GT_OBJ, the size is always "evaluated" (i.e. -// instantiated into a register) last. In those cases, the GTF_REVERSE_OPS flag -// on the assignment works as usual. -// In order to preserve previous possible orderings, the order for evaluating -// the size of a GT_DYN_BLK node is controlled by its gtEvalSizeFirst flag. If -// that is set, the size is evaluated first, and then the src and dst are evaluated -// according to the GTF_REVERSE_OPS flag on the assignment. - -void Compiler::fgOrderBlockOps(GenTree* tree, - regMaskTP reg0, - regMaskTP reg1, - regMaskTP reg2, - GenTree** opsPtr, // OUT - regMaskTP* regsPtr) // OUT -{ - assert(tree->OperIsBlkOp()); - - GenTreeBlk* destBlk = tree->gtOp.gtOp1->AsBlk(); - GenTree* destAddr = destBlk->Addr(); - GenTree* srcPtrOrVal = tree->gtOp.gtOp2; - if (tree->OperIsCopyBlkOp()) - { - assert(srcPtrOrVal->OperIsIndir()); - srcPtrOrVal = srcPtrOrVal->AsIndir()->Addr(); - } - - assert(destAddr != nullptr); - assert(srcPtrOrVal != nullptr); - - GenTree* ops[3] = { - destAddr, // Dest address - srcPtrOrVal, // Val / Src address - nullptr // Size of block - }; - - regMaskTP regs[3] = {reg0, reg1, reg2}; - - static int blockOpsOrder[4][3] = - // destBlk->gtEvalSizeFirst | tree->gtFlags - { - // -------------------------+---------------------------- - {0, 1, 2}, // false | - - {2, 0, 1}, // true | - - {1, 0, 2}, // false | GTF_REVERSE_OPS - {2, 1, 0} // true | GTF_REVERSE_OPS - }; - - int orderNum = ((tree->gtFlags & GTF_REVERSE_OPS) == 0) ? 0 : 2; - if (destBlk->OperIs(GT_DYN_BLK)) - { - GenTreeDynBlk* const dynBlk = destBlk->AsDynBlk(); - if (dynBlk->gtEvalSizeFirst) - { - orderNum++; - } - ops[2] = dynBlk->gtDynamicSize; - } - - assert(orderNum < 4); - - int* order = blockOpsOrder[orderNum]; - - PREFIX_ASSUME(order != NULL); - - // Fill in the OUT arrays according to the order we have selected - - opsPtr[0] = ops[order[0]]; - opsPtr[1] = ops[order[1]]; - opsPtr[2] = ops[order[2]]; - - regsPtr[0] = regs[order[0]]; - regsPtr[1] = regs[order[1]]; - regsPtr[2] = regs[order[2]]; -} -#endif // LEGACY_BACKEND - //------------------------------------------------------------------------ // fgGetFirstNode: Get the first node in the tree, in execution order // @@ -20862,12 +20681,8 @@ void Compiler::fgDebugCheckBBlist(bool checkBBNum /* = false */, bool checkBBRef } else if (block->bbJumpKind == BBJ_SWITCH) { -#ifndef LEGACY_BACKEND noway_assert(block->lastNode()->gtNext == nullptr && (block->lastNode()->gtOper == GT_SWITCH || block->lastNode()->gtOper == GT_SWITCH_TABLE)); -#else // LEGACY_BACKEND - noway_assert(block->lastStmt()->gtNext == NULL && block->lastStmt()->gtStmtExpr->gtOper == GT_SWITCH); -#endif // LEGACY_BACKEND } else if (!(block->bbJumpKind == BBJ_ALWAYS || block->bbJumpKind == BBJ_RETURN)) { @@ -23667,10 +23482,6 @@ Compiler::fgWalkResult Compiler::fgChkThrowCB(GenTree** pTree, fgWalkData* data) case GT_MUL: case GT_ADD: case GT_SUB: -#ifdef LEGACY_BACKEND - case GT_ASG_ADD: - case GT_ASG_SUB: -#endif case GT_CAST: if (tree->gtOverflow()) { diff --git a/src/jit/fp.h b/src/jit/fp.h deleted file mode 100644 index f1cee9581a..0000000000 --- a/src/jit/fp.h +++ /dev/null @@ -1,73 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#ifndef _JIT_FP - -#define _JIT_FP - -// Auxiliary structures. -#if FEATURE_STACK_FP_X87 - -enum dummyFPenum -{ -#define REGDEF(name, rnum, mask, sname) dummmy_##name = rnum, -#include "registerfp.h" - - FP_VIRTUALREGISTERS, -}; - -// FlatFPStateX87 holds the state of the virtual register file. For each -// virtual register we keep track to which physical register we're -// mapping. We also keep track of the physical stack. - -#define FP_PHYSICREGISTERS FP_VIRTUALREGISTERS -#define FP_VRNOTMAPPED -1 - -struct FlatFPStateX87 -{ -public: - void Init(FlatFPStateX87* pFrom = 0); - bool Mapped(unsigned uEntry); // Is virtual register mapped - void Unmap(unsigned uEntry); // Unmaps a virtual register - void Associate(unsigned uEntry, unsigned uStack); - unsigned StackToST(unsigned uEntry); // Maps the stack to a ST(x) entry - unsigned VirtualToST(unsigned uEntry); - unsigned STToVirtual(unsigned uST); - unsigned TopIndex(); - unsigned TopVirtual(); - void Rename(unsigned uVirtualTo, unsigned uVirtualFrom); - unsigned Pop(); - void Push(unsigned uEntry); - bool IsEmpty(); - - // Debug/test methods - static bool AreEqual(FlatFPStateX87* pSrc, FlatFPStateX87* pDst); -#ifdef DEBUG - bool IsValidEntry(unsigned uEntry); - bool IsConsistent(); - void UpdateMappingFromStack(); - void Dump(); - - // In some optimizations the stack will be inconsistent in some transactions. We want to keep - // the checks for everthing else, so if have the stack in an inconsistent state, you must - // ignore it on purpose. - bool m_bIgnoreConsistencyChecks; - - inline void IgnoreConsistencyChecks(bool bIgnore) - { - m_bIgnoreConsistencyChecks = bIgnore; - } -#else - inline void IgnoreConsistencyChecks(bool bIgnore) - { - } -#endif - - unsigned m_uVirtualMap[FP_VIRTUALREGISTERS]; - unsigned m_uStack[FP_PHYSICREGISTERS]; - unsigned m_uStackSize; -}; - -#endif // FEATURE_STACK_FP_X87 -#endif diff --git a/src/jit/gcencode.cpp b/src/jit/gcencode.cpp index 8da52118ac..a05e8eb08d 100644 --- a/src/jit/gcencode.cpp +++ b/src/jit/gcencode.cpp @@ -2217,17 +2217,13 @@ size_t GCInfo::gcMakeRegPtrTable(BYTE* dest, int mask, const InfoHdr& header, un } else { -/* Stack-passed arguments which are not enregistered - * are always reported in this "untracked stack - * pointers" section of the GC info even if lvTracked==true - */ + /* Stack-passed arguments which are not enregistered + * are always reported in this "untracked stack + * pointers" section of the GC info even if lvTracked==true + */ -/* Has this argument been enregistered? */ -#ifndef LEGACY_BACKEND + /* Has this argument been enregistered? */ if (!varDsc->lvOnFrame) -#else // LEGACY_BACKEND - if (varDsc->lvRegister) -#endif // LEGACY_BACKEND { /* if a CEE_JMP has been used, then we need to report all the arguments even if they are enregistered, since we will be using this value @@ -4150,11 +4146,8 @@ void GCInfo::gcMakeRegPtrTable( GCENCODER_WITH_LOGGING(gcInfoEncoderWithLog, gcInfoEncoder); const bool noTrackedGCSlots = - (compiler->opts.MinOpts() && !compiler->opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT) -#if !defined(JIT32_GCENCODER) || !defined(LEGACY_BACKEND) - && !JitConfig.JitMinOptsTrackGCrefs() -#endif // !defined(JIT32_GCENCODER) || !defined(LEGACY_BACKEND) - ); + (compiler->opts.MinOpts() && !compiler->opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT) && + !JitConfig.JitMinOptsTrackGCrefs()); if (mode == MAKE_REG_PTR_MODE_ASSIGN_SLOTS) { @@ -4208,11 +4201,7 @@ void GCInfo::gcMakeRegPtrTable( // Has this argument been fully enregistered? CLANG_FORMAT_COMMENT_ANCHOR; -#ifndef LEGACY_BACKEND if (!varDsc->lvOnFrame) -#else // LEGACY_BACKEND - if (varDsc->lvRegister) -#endif // LEGACY_BACKEND { // If a CEE_JMP has been used, then we need to report all the arguments // even if they are enregistered, since we will be using this value diff --git a/src/jit/gcinfo.cpp b/src/jit/gcinfo.cpp index 6ed3f6d0df..36c7de0dcf 100644 --- a/src/jit/gcinfo.cpp +++ b/src/jit/gcinfo.cpp @@ -259,9 +259,7 @@ GCInfo::WriteBarrierForm GCInfo::gcIsWriteBarrierCandidate(GenTree* tgt, GenTree switch (tgt->gtOper) { -#ifndef LEGACY_BACKEND case GT_STOREIND: -#endif // !LEGACY_BACKEND case GT_IND: /* Could be the managed heap */ if (tgt->TypeGet() == TYP_BYREF) { @@ -300,39 +298,16 @@ bool GCInfo::gcIsWriteBarrierAsgNode(GenTree* op) { return gcIsWriteBarrierCandidate(op->gtOp.gtOp1, op->gtOp.gtOp2) != WBF_NoBarrier; } -#ifndef LEGACY_BACKEND else if (op->gtOper == GT_STOREIND) { return gcIsWriteBarrierCandidate(op, op->gtOp.gtOp2) != WBF_NoBarrier; } -#endif // !LEGACY_BACKEND else { return false; } } -/*****************************************************************************/ -/***************************************************************************** - * - * If the given tree value is sitting in a register, free it now. - */ - -#ifdef LEGACY_BACKEND -void GCInfo::gcMarkRegPtrVal(GenTree* tree) -{ - if (varTypeIsGC(tree->TypeGet())) - { - if (tree->gtOper == GT_LCL_VAR) - compiler->codeGen->genMarkLclVar(tree); - if (tree->InReg()) - { - gcMarkRegSetNpt(genRegMask(tree->gtRegNum)); - } - } -} -#endif // LEGACY_BACKEND - /*****************************************************************************/ /***************************************************************************** * @@ -433,11 +408,7 @@ void GCInfo::gcCountForHeader(UNALIGNED unsigned int* untrackedCount, UNALIGNED /* Has this argument been fully enregistered? */ CLANG_FORMAT_COMMENT_ANCHOR; -#ifndef LEGACY_BACKEND if (!varDsc->lvOnFrame) -#else // LEGACY_BACKEND - if (varDsc->lvRegister) -#endif // LEGACY_BACKEND { /* if a CEE_JMP has been used, then we need to report all the arguments even if they are enregistered, since we will be using this value @@ -801,7 +772,6 @@ GCInfo::WriteBarrierForm GCInfo::gcWriteBarrierFormFromTargetAddress(GenTree* tg return GCInfo::WBF_BarrierUnknown; } -#ifndef LEGACY_BACKEND //------------------------------------------------------------------------ // gcUpdateForRegVarMove: Update the masks when a variable is moved // @@ -868,7 +838,6 @@ void GCInfo::gcUpdateForRegVarMove(regMaskTP srcMask, regMaskTP dstMask, LclVarD VarSetOps::AddElemD(compiler, gcVarPtrSetCur, varDsc->lvVarIndex); } } -#endif // !LEGACY_BACKEND /*****************************************************************************/ /*****************************************************************************/ diff --git a/src/jit/gentree.cpp b/src/jit/gentree.cpp index d44312be87..8cc44645c3 100644 --- a/src/jit/gentree.cpp +++ b/src/jit/gentree.cpp @@ -25,53 +25,6 @@ const unsigned short GenTree::gtOperKindTable[] = { #include "gtlist.h" }; -#ifdef LEGACY_BACKEND -/*****************************************************************************/ -// static -genTreeOps GenTree::OpAsgToOper(genTreeOps op) -{ - // Precondition. - assert(OperIsAssignment(op) && op != GT_ASG); - switch (op) - { - case GT_ASG_ADD: - return GT_ADD; - case GT_ASG_SUB: - return GT_SUB; - case GT_ASG_MUL: - return GT_MUL; - case GT_ASG_DIV: - return GT_DIV; - case GT_ASG_MOD: - return GT_MOD; - - case GT_ASG_UDIV: - return GT_UDIV; - case GT_ASG_UMOD: - return GT_UMOD; - - case GT_ASG_OR: - return GT_OR; - case GT_ASG_XOR: - return GT_XOR; - case GT_ASG_AND: - return GT_AND; - case GT_ASG_LSH: - return GT_LSH; - case GT_ASG_RSH: - return GT_RSH; - case GT_ASG_RSZ: - return GT_RSZ; - - case GT_CHS: - return GT_NEG; - - default: - unreached(); // Precondition implies we don't get here. - } -} -#endif // LEGACY_BACKEND - /***************************************************************************** * * The types of different GenTree nodes @@ -335,7 +288,7 @@ void GenTree::InitNodeSize() // TODO-Throughput: This should not need to be a large node. The object info should be // obtained from the child node. GenTree::s_gtNodeSizes[GT_PUTARG_STK] = TREE_NODE_SZ_LARGE; -#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_) +#if defined(_TARGET_ARM_) GenTree::s_gtNodeSizes[GT_PUTARG_SPLIT] = TREE_NODE_SZ_LARGE; #endif #endif // FEATURE_PUT_STRUCT_ARG_STK @@ -353,9 +306,7 @@ void GenTree::InitNodeSize() static_assert_no_msg(sizeof(GenTreeVal) <= TREE_NODE_SZ_SMALL); static_assert_no_msg(sizeof(GenTreeIntConCommon) <= TREE_NODE_SZ_SMALL); static_assert_no_msg(sizeof(GenTreePhysReg) <= TREE_NODE_SZ_SMALL); -#ifndef LEGACY_BACKEND static_assert_no_msg(sizeof(GenTreeJumpTable) <= TREE_NODE_SZ_SMALL); -#endif // !LEGACY_BACKEND static_assert_no_msg(sizeof(GenTreeIntCon) <= TREE_NODE_SZ_SMALL); static_assert_no_msg(sizeof(GenTreeLngCon) <= TREE_NODE_SZ_SMALL); static_assert_no_msg(sizeof(GenTreeDblCon) <= TREE_NODE_SZ_SMALL); @@ -400,7 +351,7 @@ void GenTree::InitNodeSize() // TODO-Throughput: This should not need to be a large node. The object info should be // obtained from the child node. static_assert_no_msg(sizeof(GenTreePutArgStk) <= TREE_NODE_SZ_LARGE); -#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_) +#if defined(_TARGET_ARM_) static_assert_no_msg(sizeof(GenTreePutArgSplit) <= TREE_NODE_SZ_LARGE); #endif #endif // FEATURE_PUT_STRUCT_ARG_STK @@ -664,7 +615,7 @@ void Compiler::fgWalkAllTreesPre(fgWalkPreFn* visitor, void* pCallBackData) } //----------------------------------------------------------- -// CopyReg: Copy the _gtRegNum/_gtRegPair/gtRegTag fields. +// CopyReg: Copy the _gtRegNum/gtRegTag fields. // // Arguments: // from - GenTree node from which to copy @@ -673,10 +624,7 @@ void Compiler::fgWalkAllTreesPre(fgWalkPreFn* visitor, void* pCallBackData) // None void GenTree::CopyReg(GenTree* from) { - // To do the copy, use _gtRegPair, which must be bigger than _gtRegNum. Note that the values - // might be undefined (so gtRegTag == GT_REGTAG_NONE). - _gtRegPair = from->_gtRegPair; - C_ASSERT(sizeof(_gtRegPair) >= sizeof(_gtRegNum)); + _gtRegNum = from->_gtRegNum; INDEBUG(gtRegTag = from->gtRegTag;) // Also copy multi-reg state if this is a call node @@ -716,16 +664,6 @@ bool GenTree::gtHasReg() const { bool hasReg; -#if CPU_LONG_USES_REGPAIR - if (isRegPairType(TypeGet())) - { - assert(_gtRegNum != REG_NA); - INDEBUG(assert(gtRegTag == GT_REGTAG_REGPAIR)); - return (gtRegPair != REG_PAIR_NONE); - } - assert(_gtRegNum != REG_PAIR_NONE); - INDEBUG(assert(gtRegTag == GT_REGTAG_REG)); -#endif if (IsMultiRegCall()) { // Have to cast away const-ness because GetReturnTypeDesc() is a non-const method @@ -810,7 +748,7 @@ int GenTree::GetRegisterDstCount() const GenTreeCall* call = copyOrReload->gtGetOp1()->AsCall(); return call->GetReturnTypeDesc()->GetReturnRegCount(); } -#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_) +#if defined(_TARGET_ARM_) else if (OperIsPutArgSplit()) { return (const_cast(this))->AsPutArgSplit()->gtNumRegs; @@ -822,7 +760,7 @@ int GenTree::GetRegisterDstCount() const assert(OperIsMultiRegOp()); return (TypeGet() == TYP_LONG) ? 2 : 1; } -#endif // !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_) +#endif // defined(_TARGET_ARM_) assert(!"Unexpected multi-reg node"); return 0; } @@ -840,63 +778,54 @@ regMaskTP GenTree::gtGetRegMask() const { regMaskTP resultMask; -#if CPU_LONG_USES_REGPAIR - if (isRegPairType(TypeGet())) + if (IsMultiRegCall()) { - resultMask = genRegPairMask(gtRegPair); + // temporarily cast away const-ness as AsCall() method is not declared const + resultMask = genRegMask(gtRegNum); + GenTree* temp = const_cast(this); + resultMask |= temp->AsCall()->GetOtherRegMask(); } - else -#endif + else if (IsCopyOrReloadOfMultiRegCall()) { - if (IsMultiRegCall()) - { - // temporarily cast away const-ness as AsCall() method is not declared const - resultMask = genRegMask(gtRegNum); - GenTree* temp = const_cast(this); - resultMask |= temp->AsCall()->GetOtherRegMask(); - } - else if (IsCopyOrReloadOfMultiRegCall()) - { - // A multi-reg copy or reload, will have valid regs for only those - // positions that need to be copied or reloaded. Hence we need - // to consider only those registers for computing reg mask. + // A multi-reg copy or reload, will have valid regs for only those + // positions that need to be copied or reloaded. Hence we need + // to consider only those registers for computing reg mask. - GenTree* tree = const_cast(this); - GenTreeCopyOrReload* copyOrReload = tree->AsCopyOrReload(); - GenTreeCall* call = copyOrReload->gtGetOp1()->AsCall(); - unsigned regCount = call->GetReturnTypeDesc()->GetReturnRegCount(); + GenTree* tree = const_cast(this); + GenTreeCopyOrReload* copyOrReload = tree->AsCopyOrReload(); + GenTreeCall* call = copyOrReload->gtGetOp1()->AsCall(); + unsigned regCount = call->GetReturnTypeDesc()->GetReturnRegCount(); - resultMask = RBM_NONE; - for (unsigned i = 0; i < regCount; ++i) - { - regNumber reg = copyOrReload->GetRegNumByIdx(i); - if (reg != REG_NA) - { - resultMask |= genRegMask(reg); - } - } - } -#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_) - else if (OperIsPutArgSplit()) + resultMask = RBM_NONE; + for (unsigned i = 0; i < regCount; ++i) { - GenTree* tree = const_cast(this); - GenTreePutArgSplit* splitArg = tree->AsPutArgSplit(); - unsigned regCount = splitArg->gtNumRegs; - - resultMask = RBM_NONE; - for (unsigned i = 0; i < regCount; ++i) + regNumber reg = copyOrReload->GetRegNumByIdx(i); + if (reg != REG_NA) { - regNumber reg = splitArg->GetRegNumByIdx(i); - assert(reg != REG_NA); resultMask |= genRegMask(reg); } } -#endif - else + } +#if defined(_TARGET_ARM_) + else if (OperIsPutArgSplit()) + { + GenTree* tree = const_cast(this); + GenTreePutArgSplit* splitArg = tree->AsPutArgSplit(); + unsigned regCount = splitArg->gtNumRegs; + + resultMask = RBM_NONE; + for (unsigned i = 0; i < regCount; ++i) { - resultMask = genRegMask(gtRegNum); + regNumber reg = splitArg->GetRegNumByIdx(i); + assert(reg != REG_NA); + resultMask |= genRegMask(reg); } } +#endif + else + { + resultMask = genRegMask(gtRegNum); + } return resultMask; } @@ -1002,8 +931,6 @@ bool GenTreeCall::HasSideEffects(Compiler* compiler, bool ignoreExceptions, bool (!helperProperties.IsAllocator(helper) || helperProperties.MayFinalize(helper)); } -#ifndef LEGACY_BACKEND - //------------------------------------------------------------------------- // HasNonStandardAddedArgs: Return true if the method has non-standard args added to the call // argument list during argument morphing (fgMorphArgs), e.g., passed in R10 or R11 on AMD64. @@ -1061,8 +988,6 @@ int GenTreeCall::GetNonStandardAddedArgCount(Compiler* compiler) const return 0; } -#endif // !LEGACY_BACKEND - //------------------------------------------------------------------------- // TreatAsHasRetBufArg: // @@ -1193,12 +1118,12 @@ bool GenTreeCall::AreArgsComplete() const return false; } -#if !defined(FEATURE_PUT_STRUCT_ARG_STK) && !defined(LEGACY_BACKEND) +#if !defined(FEATURE_PUT_STRUCT_ARG_STK) unsigned GenTreePutArgStk::getArgSize() { return genTypeSize(genActualType(gtOp1->gtType)); } -#endif // !defined(FEATURE_PUT_STRUCT_ARG_STK) && !defined(LEGACY_BACKEND) +#endif // !defined(FEATURE_PUT_STRUCT_ARG_STK) /***************************************************************************** * @@ -2624,16 +2549,14 @@ void Compiler::lvaLclVarRefsAccumIntoRes(GenTree** findPtr, genTreeOps GenTree::ReverseRelop(genTreeOps relop) { static const genTreeOps reverseOps[] = { - GT_NE, // GT_EQ - GT_EQ, // GT_NE - GT_GE, // GT_LT - GT_GT, // GT_LE - GT_LT, // GT_GE - GT_LE, // GT_GT -#ifndef LEGACY_BACKEND + GT_NE, // GT_EQ + GT_EQ, // GT_NE + GT_GE, // GT_LT + GT_GT, // GT_LE + GT_LT, // GT_GE + GT_LE, // GT_GT GT_TEST_NE, // GT_TEST_EQ GT_TEST_EQ, // GT_TEST_NE -#endif }; assert(reverseOps[GT_EQ - GT_EQ] == GT_NE); @@ -2644,10 +2567,8 @@ genTreeOps GenTree::ReverseRelop(genTreeOps relop) assert(reverseOps[GT_GE - GT_EQ] == GT_LT); assert(reverseOps[GT_GT - GT_EQ] == GT_LE); -#ifndef LEGACY_BACKEND assert(reverseOps[GT_TEST_EQ - GT_EQ] == GT_TEST_NE); assert(reverseOps[GT_TEST_NE - GT_EQ] == GT_TEST_EQ); -#endif assert(OperIsCompare(relop)); assert(relop >= GT_EQ && (unsigned)(relop - GT_EQ) < sizeof(reverseOps)); @@ -2664,16 +2585,14 @@ genTreeOps GenTree::ReverseRelop(genTreeOps relop) genTreeOps GenTree::SwapRelop(genTreeOps relop) { static const genTreeOps swapOps[] = { - GT_EQ, // GT_EQ - GT_NE, // GT_NE - GT_GT, // GT_LT - GT_GE, // GT_LE - GT_LE, // GT_GE - GT_LT, // GT_GT -#ifndef LEGACY_BACKEND + GT_EQ, // GT_EQ + GT_NE, // GT_NE + GT_GT, // GT_LT + GT_GE, // GT_LE + GT_LE, // GT_GE + GT_LT, // GT_GT GT_TEST_EQ, // GT_TEST_EQ GT_TEST_NE, // GT_TEST_NE -#endif }; assert(swapOps[GT_EQ - GT_EQ] == GT_EQ); @@ -2684,10 +2603,8 @@ genTreeOps GenTree::SwapRelop(genTreeOps relop) assert(swapOps[GT_GE - GT_EQ] == GT_LE); assert(swapOps[GT_GT - GT_EQ] == GT_LT); -#ifndef LEGACY_BACKEND assert(swapOps[GT_TEST_EQ - GT_EQ] == GT_TEST_EQ); assert(swapOps[GT_TEST_NE - GT_EQ] == GT_TEST_NE); -#endif assert(OperIsCompare(relop)); assert(relop >= GT_EQ && (unsigned)(relop - GT_EQ) < sizeof(swapOps)); @@ -2818,12 +2735,6 @@ unsigned Compiler::gtSetListOrder(GenTree* list, bool isListCallArgs, bool callA unsigned nxtlvl = (list == nullptr) ? 0 : gtSetEvalOrder(list); while (listNodes.Height() > 0) { -#if FEATURE_STACK_FP_X87 - /* Save the current FP stack level since an argument list - * will implicitly pop the FP stack when pushing the argument */ - unsigned FPlvlSave = codeGen->genGetFPstkLevel(); -#endif // FEATURE_STACK_FP_X87 - list = listNodes.Pop(); assert(list && list->OperIsAnyList()); GenTree* next = list->gtOp.gtOp2; @@ -2854,11 +2765,6 @@ unsigned Compiler::gtSetListOrder(GenTree* list, bool isListCallArgs, bool callA GenTree* op1 = list->gtOp.gtOp1; unsigned lvl = gtSetEvalOrder(op1); -#if FEATURE_STACK_FP_X87 - // restore the FP level - codeGen->genResetFPstkLevel(FPlvlSave); -#endif // FEATURE_STACK_FP_X87 - list->gtRsvdRegs = (regMaskSmall)(ftreg | op1->gtRsvdRegs); // Swap the level counts @@ -3061,9 +2967,6 @@ GenTree* Compiler::gtWalkOpEffectiveVal(GenTree* op) void Compiler::gtPrepareCost(GenTree* tree) { -#if FEATURE_STACK_FP_X87 - codeGen->genResetFPstkLevel(); -#endif // FEATURE_STACK_FP_X87 gtSetEvalOrder(tree); } @@ -3386,13 +3289,6 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) costSz += 1; } } -#endif -#if CPU_LONG_USES_REGPAIR - if (varTypeIsLong(tree->TypeGet())) - { - costEx *= 2; // Longs are twice as expensive - costSz *= 2; - } #endif break; @@ -3428,12 +3324,6 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) costSz = 1; break; } -#if FEATURE_STACK_FP_X87 - if (isflt && (oper != GT_PHI_ARG)) - { - codeGen->genIncrementFPstkLevel(); - } -#endif // FEATURE_STACK_FP_X87 goto DONE; } @@ -3523,37 +3413,11 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) /* cast involving floats always go through memory */ costEx = IND_COST_EX * 2; costSz = 6; - -#if FEATURE_STACK_FP_X87 - if (isflt != varTypeIsFloating(op1->TypeGet())) - { - isflt ? codeGen->genIncrementFPstkLevel() // Cast from int to float - : codeGen->genDecrementFPstkLevel(); // Cast from float to int - } -#endif // FEATURE_STACK_FP_X87 } #else #error "Unknown _TARGET_" #endif -#if CPU_LONG_USES_REGPAIR - if (varTypeIsLong(tree->TypeGet())) - { - if (varTypeIsUnsigned(tree->TypeGet())) - { - /* Cast to unsigned long */ - costEx += 1; - costSz += 2; - } - else - { - /* Cast to signed long is slightly more costly */ - costEx += 2; - costSz += 3; - } - } -#endif // CPU_LONG_USES_REGPAIR - /* Overflow casts are a lot more expensive */ if (tree->gtOverflow()) { @@ -3618,14 +3482,6 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) case CORINFO_INTRINSIC_Round: costEx = 3; costSz = 4; -#if FEATURE_STACK_FP_X87 - if (tree->TypeGet() == TYP_INT) - { - // This is a special case to handle the following - // optimization: conv.i4(round.d(d)) -> round.i(d) - codeGen->genDecrementFPstkLevel(); - } -#endif // FEATURE_STACK_FP_X87 break; } level++; @@ -3646,14 +3502,6 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) case GT_ADDR: -#if FEATURE_STACK_FP_X87 - /* If the operand was floating point, pop the value from the stack */ - - if (varTypeIsFloating(op1->TypeGet())) - { - codeGen->genDecrementFPstkLevel(); - } -#endif // FEATURE_STACK_FP_X87 costEx = 0; costSz = 1; @@ -3712,10 +3560,6 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) if (isflt) { -#if FEATURE_STACK_FP_X87 - /* Indirect loads of FP values push a new value on the FP stack */ - codeGen->genIncrementFPstkLevel(); -#endif // FEATURE_STACK_FP_X87 if (tree->TypeGet() == TYP_DOUBLE) { costEx += 1; @@ -3979,7 +3823,7 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) // we have already found either a non-ADD op1 or a non-constant op2. gtWalkOp(&op1, &op2, nullptr, true); -#if defined(_TARGET_XARCH_) || defined(LEGACY_BACKEND) +#if defined(_TARGET_XARCH_) // For XARCH we will fold GT_ADDs in the op2 position into the addressing mode, so we call // gtWalkOp on both operands of the original GT_ADD. // This is not done for ARMARCH. Though the stated reason is that we don't try to create a @@ -3989,7 +3833,7 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) // into the addressing mode. // Walk op2 looking for non-overflow GT_ADDs of constants. gtWalkOp(&op2, &op1, nullptr, true); -#endif // defined(_TARGET_XARCH_) || defined(LEGACY_BACKEND) +#endif // defined(_TARGET_XARCH_) // OK we are done walking the tree // Now assert that op1 and op2 correspond with base and idx @@ -4216,10 +4060,6 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) case GT_ADD: case GT_SUB: -#ifdef LEGACY_BACKEND - case GT_ASG_ADD: - case GT_ASG_SUB: -#endif if (isflt) { /* FP instructions are a bit more expensive */ @@ -4239,7 +4079,7 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) case GT_COMMA: /* Comma tosses the result of the left operand */ - gtSetEvalOrderAndRestoreFPstkLevel(op1); + gtSetEvalOrder(op1); level = gtSetEvalOrder(op2); ftreg |= op1->gtRsvdRegs | op2->gtRsvdRegs; @@ -4252,7 +4092,7 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) case GT_COLON: - level = gtSetEvalOrderAndRestoreFPstkLevel(op1); + level = gtSetEvalOrder(op1); lvl2 = gtSetEvalOrder(op2); if (level < lvl2) @@ -4290,18 +4130,6 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) level = gtSetEvalOrder(op1); -#if FEATURE_STACK_FP_X87 - - /* If assigning an FP value, the target won't get pushed */ - - if (isflt && !tree->IsPhiDefn()) - { - op1->gtFPlvl--; - codeGen->genDecrementFPstkLevel(); - } - -#endif // FEATURE_STACK_FP_X87 - if (gtIsLikelyRegVar(op1)) { assert(lvlb == 0); @@ -4353,19 +4181,6 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) costSz += (op1->gtCostSz + op2->gtCostSz); DONE_OP1_AFTER_COST: -#if FEATURE_STACK_FP_X87 - /* - Binary FP operators pop 2 operands and produce 1 result; - FP comparisons pop 2 operands and produces 0 results. - assignments consume 1 value and don't produce anything. - */ - - if (isflt && !tree->IsPhiDefn()) - { - assert(oper != GT_COMMA); - codeGen->genDecrementFPstkLevel(); - } -#endif // FEATURE_STACK_FP_X87 bool bReverseInAssignment = false; if (GenTree::OperIsAssignment(oper)) @@ -4435,22 +4250,12 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) if (varTypeIsFloating(op1->TypeGet())) { -#if FEATURE_STACK_FP_X87 - codeGen->genDecrementFPstkLevel(2); -#endif // FEATURE_STACK_FP_X87 #ifdef _TARGET_XARCH_ ftreg |= RBM_EAX; #endif level++; lvl2++; } -#if CPU_LONG_USES_REGPAIR - if (varTypeIsLong(op1->TypeGet())) - { - costEx *= 2; // Longs are twice as expensive - costSz *= 2; - } -#endif if ((tree->gtFlags & GTF_RELOP_JMP_USED) == 0) { /* Using a setcc instruction is more expensive */ @@ -4467,11 +4272,6 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) case GT_RSZ: case GT_ROL: case GT_ROR: -#ifdef LEGACY_BACKEND - case GT_ASG_LSH: - case GT_ASG_RSH: - case GT_ASG_RSZ: -#endif /* Variable sized shifts are more expensive and use REG_SHIFT */ if (!op2->IsCnsIntOrI()) @@ -4618,12 +4418,6 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) tree->gtOp.gtOp1 = op2; tree->gtOp.gtOp2 = op1; - -#if FEATURE_STACK_FP_X87 - /* We may have to recompute FP levels */ - if (op1->gtFPlvl || op2->gtFPlvl) - gtFPstLvlRedo = true; -#endif // FEATURE_STACK_FP_X87 break; case GT_QMARK: @@ -4635,18 +4429,6 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) case GT_FIELD_LIST: break; - case GT_SUB: -#ifdef LEGACY_BACKEND - // For LSRA we require that LclVars be "evaluated" just prior to their use, - // so that if they must be reloaded, it is done at the right place. - // This means that we allow reverse evaluation for all BINOPs. - // (Note that this doesn't affect the order of the operands in the instruction). - if (!isflt) - break; -#endif // LEGACY_BACKEND - - __fallthrough; - default: /* Mark the operand's evaluation order to be swapped */ @@ -4659,12 +4441,6 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) tree->gtFlags |= GTF_REVERSE_OPS; } -#if FEATURE_STACK_FP_X87 - /* We may have to recompute FP levels */ - if (op1->gtFPlvl || op2->gtFPlvl) - gtFPstLvlRedo = true; -#endif // FEATURE_STACK_FP_X87 - break; } } @@ -4728,9 +4504,6 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) if (tree->gtCall.gtCallArgs) { -#if FEATURE_STACK_FP_X87 - unsigned FPlvlSave = codeGen->genGetFPstkLevel(); -#endif // FEATURE_STACK_FP_X87 const bool isListCallArgs = true; const bool callArgsInRegs = false; lvl2 = gtSetListOrder(tree->gtCall.gtCallArgs, isListCallArgs, callArgsInRegs); @@ -4741,9 +4514,6 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) costEx += tree->gtCall.gtCallArgs->gtCostEx; costSz += tree->gtCall.gtCallArgs->gtCostSz; ftreg |= tree->gtCall.gtCallArgs->gtRsvdRegs; -#if FEATURE_STACK_FP_X87 - codeGen->genResetFPstkLevel(FPlvlSave); -#endif // FEATURE_STACK_FP_X87 } /* Evaluate the temp register arguments list @@ -4752,9 +4522,6 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) if (tree->gtCall.gtCallLateArgs) { -#if FEATURE_STACK_FP_X87 - unsigned FPlvlSave = codeGen->genGetFPstkLevel(); -#endif // FEATURE_STACK_FP_X87 const bool isListCallArgs = true; const bool callArgsInRegs = true; lvl2 = gtSetListOrder(tree->gtCall.gtCallLateArgs, isListCallArgs, callArgsInRegs); @@ -4765,9 +4532,6 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) costEx += tree->gtCall.gtCallLateArgs->gtCostEx; costSz += tree->gtCall.gtCallLateArgs->gtCostSz; ftreg |= tree->gtCall.gtCallLateArgs->gtRsvdRegs; -#if FEATURE_STACK_FP_X87 - codeGen->genResetFPstkLevel(FPlvlSave); -#endif // FEATURE_STACK_FP_X87 } if (tree->gtCall.gtCallType == CT_INDIRECT) @@ -4837,24 +4601,9 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) #endif #endif -#ifdef LEGACY_BACKEND - // Normally function calls don't preserve caller save registers - // and thus are much more expensive. - // However a few function calls do preserve these registers - // such as the GC WriteBarrier helper calls. - - if (!(tree->gtFlags & GTF_CALL_REG_SAVE)) -#endif // LEGACY_BACKEND - { - level += 5; - costEx += 3 * IND_COST_EX; - ftreg |= RBM_CALLEE_TRASH; - } - -#if FEATURE_STACK_FP_X87 - if (isflt) - codeGen->genIncrementFPstkLevel(); -#endif // FEATURE_STACK_FP_X87 + level += 5; + costEx += 3 * IND_COST_EX; + ftreg |= RBM_CALLEE_TRASH; break; @@ -4876,10 +4625,6 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) costSz += tree->gtArrElem.gtArrInds[dim]->gtCostSz; } -#if FEATURE_STACK_FP_X87 - if (isflt) - codeGen->genIncrementFPstkLevel(); -#endif // FEATURE_STACK_FP_X87 level += tree->gtArrElem.gtArrRank; costEx += 2 + (tree->gtArrElem.gtArrRank * (IND_COST_EX + 1)); costSz += 2 + (tree->gtArrElem.gtArrRank * 2); @@ -5037,15 +4782,6 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) DONE: -#if FEATURE_STACK_FP_X87 - // printf("[FPlvl=%2u] ", genGetFPstkLevel()); gtDispTree(tree, 0, true); - noway_assert((unsigned char)codeGen->genFPstkLevel == codeGen->genFPstkLevel); - tree->gtFPlvl = (unsigned char)codeGen->genFPstkLevel; - - if (codeGen->genFPstkLevel > tmpDoubleSpillMax) - tmpDoubleSpillMax = codeGen->genFPstkLevel; -#endif // FEATURE_STACK_FP_X87 - tree->gtRsvdRegs = (regMaskSmall)ftreg; // Some path through this function must have set the costs. @@ -5060,252 +4796,6 @@ DONE: #pragma warning(pop) #endif -#if FEATURE_STACK_FP_X87 - -/*****************************************************************************/ -void Compiler::gtComputeFPlvls(GenTree* tree) -{ - genTreeOps oper; - unsigned kind; - bool isflt; - unsigned savFPstkLevel; - - noway_assert(tree); - noway_assert(tree->gtOper != GT_STMT); - - /* Figure out what kind of a node we have */ - - oper = tree->OperGet(); - kind = tree->OperKind(); - isflt = varTypeIsFloating(tree->TypeGet()) ? 1 : 0; - - /* Is this a constant or leaf node? */ - - if (kind & (GTK_CONST | GTK_LEAF)) - { - codeGen->genFPstkLevel += isflt; - goto DONE; - } - - /* Is it a 'simple' unary/binary operator? */ - - if (kind & GTK_SMPOP) - { - GenTree* op1 = tree->gtOp.gtOp1; - GenTree* op2 = tree->gtGetOp2IfPresent(); - - /* Check for some special cases */ - - switch (oper) - { - case GT_IND: - - gtComputeFPlvls(op1); - - /* Indirect loads of FP values push a new value on the FP stack */ - - codeGen->genFPstkLevel += isflt; - goto DONE; - - case GT_CAST: - - gtComputeFPlvls(op1); - - /* Casts between non-FP and FP push on / pop from the FP stack */ - - if (varTypeIsFloating(op1->TypeGet())) - { - if (isflt == false) - codeGen->genFPstkLevel--; - } - else - { - if (isflt != false) - codeGen->genFPstkLevel++; - } - - goto DONE; - - case GT_LIST: /* GT_LIST presumably part of an argument list */ - case GT_COMMA: /* Comma tosses the result of the left operand */ - - savFPstkLevel = codeGen->genFPstkLevel; - gtComputeFPlvls(op1); - codeGen->genFPstkLevel = savFPstkLevel; - - if (op2) - gtComputeFPlvls(op2); - - goto DONE; - - default: - break; - } - - if (!op1) - { - if (!op2) - goto DONE; - - gtComputeFPlvls(op2); - goto DONE; - } - - if (!op2) - { - gtComputeFPlvls(op1); - if (oper == GT_ADDR) - { - /* If the operand was floating point pop the value from the stack */ - if (varTypeIsFloating(op1->TypeGet())) - { - noway_assert(codeGen->genFPstkLevel); - codeGen->genFPstkLevel--; - } - } - - // This is a special case to handle the following - // optimization: conv.i4(round.d(d)) -> round.i(d) - - if (oper == GT_INTRINSIC && tree->gtIntrinsic.gtIntrinsicId == CORINFO_INTRINSIC_Round && - tree->TypeGet() == TYP_INT) - { - codeGen->genFPstkLevel--; - } - - goto DONE; - } - - /* FP assignments need a bit special handling */ - - if (isflt && (kind & GTK_ASGOP)) - { - /* The target of the assignment won't get pushed */ - - if (tree->gtFlags & GTF_REVERSE_OPS) - { - gtComputeFPlvls(op2); - gtComputeFPlvls(op1); - op1->gtFPlvl--; - codeGen->genFPstkLevel--; - } - else - { - gtComputeFPlvls(op1); - op1->gtFPlvl--; - codeGen->genFPstkLevel--; - gtComputeFPlvls(op2); - } - - codeGen->genFPstkLevel--; - goto DONE; - } - - /* Here we have a binary operator; visit operands in proper order */ - - if (tree->gtFlags & GTF_REVERSE_OPS) - { - gtComputeFPlvls(op2); - gtComputeFPlvls(op1); - } - else - { - gtComputeFPlvls(op1); - gtComputeFPlvls(op2); - } - - /* - Binary FP operators pop 2 operands and produce 1 result; - assignments consume 1 value and don't produce any. - */ - - if (isflt) - codeGen->genFPstkLevel--; - - /* Float compares remove both operands from the FP stack */ - - if (kind & GTK_RELOP) - { - if (varTypeIsFloating(op1->TypeGet())) - codeGen->genFPstkLevel -= 2; - } - - goto DONE; - } - - /* See what kind of a special operator we have here */ - - switch (oper) - { - case GT_FIELD: - gtComputeFPlvls(tree->gtField.gtFldObj); - codeGen->genFPstkLevel += isflt; - break; - - case GT_CALL: - - if (tree->gtCall.gtCallObjp) - gtComputeFPlvls(tree->gtCall.gtCallObjp); - - if (tree->gtCall.gtCallArgs) - { - savFPstkLevel = codeGen->genFPstkLevel; - gtComputeFPlvls(tree->gtCall.gtCallArgs); - codeGen->genFPstkLevel = savFPstkLevel; - } - - if (tree->gtCall.gtCallLateArgs) - { - savFPstkLevel = codeGen->genFPstkLevel; - gtComputeFPlvls(tree->gtCall.gtCallLateArgs); - codeGen->genFPstkLevel = savFPstkLevel; - } - - codeGen->genFPstkLevel += isflt; - break; - - case GT_ARR_ELEM: - - gtComputeFPlvls(tree->gtArrElem.gtArrObj); - - unsigned dim; - for (dim = 0; dim < tree->gtArrElem.gtArrRank; dim++) - gtComputeFPlvls(tree->gtArrElem.gtArrInds[dim]); - - /* Loads of FP values push a new value on the FP stack */ - codeGen->genFPstkLevel += isflt; - break; - - case GT_CMPXCHG: - // Evaluate the trees left to right - gtComputeFPlvls(tree->gtCmpXchg.gtOpLocation); - gtComputeFPlvls(tree->gtCmpXchg.gtOpValue); - gtComputeFPlvls(tree->gtCmpXchg.gtOpComparand); - noway_assert(!isflt); - break; - - case GT_ARR_BOUNDS_CHECK: - gtComputeFPlvls(tree->gtBoundsChk.gtIndex); - gtComputeFPlvls(tree->gtBoundsChk.gtArrLen); - noway_assert(!isflt); - break; - - default: -#ifdef DEBUG - noway_assert(!"Unhandled special operator in gtComputeFPlvls()"); -#endif - break; - } - -DONE: - - noway_assert((unsigned char)codeGen->genFPstkLevel == codeGen->genFPstkLevel); - - tree->gtFPlvl = (unsigned char)codeGen->genFPstkLevel; -} - -#endif // FEATURE_STACK_FP_X87 - /***************************************************************************** * * If the given tree is an integer constant that can be used @@ -5614,9 +5104,7 @@ bool GenTree::TryGetUse(GenTree* def, GenTree*** use) case GT_END_LFIN: #endif // !FEATURE_EH_FUNCLETS case GT_PHI_ARG: -#ifndef LEGACY_BACKEND case GT_JMPTABLE: -#endif // LEGACY_BACKEND case GT_REG_VAR: case GT_CLS_VAR: case GT_CLS_VAR_ADDR: @@ -5672,7 +5160,7 @@ bool GenTree::TryGetUse(GenTree* def, GenTree*** use) case GT_FIELD_LIST: return TryGetUseList(def, use); -#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_) +#if defined(_TARGET_ARM_) case GT_PUTARG_SPLIT: if (this->AsUnOp()->gtOp1->gtOper == GT_FIELD_LIST) { @@ -5684,7 +5172,7 @@ bool GenTree::TryGetUse(GenTree* def, GenTree*** use) return true; } return false; -#endif // !LEGACY_BACKEND && _TARGET_ARM_ +#endif // _TARGET_ARM_ #ifdef FEATURE_SIMD case GT_SIMD: @@ -6360,18 +5848,10 @@ GenTree* Compiler::gtNewQmarkNode(var_types type, GenTree* cond, GenTree* colon) GenTreeQmark::GenTreeQmark(var_types type, GenTree* cond, GenTree* colonOp, Compiler* comp) : GenTreeOp(GT_QMARK, type, cond, colonOp) -#ifdef LEGACY_BACKEND - , gtThenLiveSet(VarSetOps::UninitVal()) - , gtElseLiveSet(VarSetOps::UninitVal()) -#endif { // These must follow a specific form. assert(cond != nullptr && cond->TypeGet() == TYP_INT); assert(colonOp != nullptr && colonOp->OperGet() == GT_COLON); - -#ifdef LEGACY_BACKEND - comp->impInlineRoot()->compQMarks->Push(this); -#endif } GenTreeIntCon* Compiler::gtNewIconNode(ssize_t value, var_types type) @@ -6387,14 +5867,12 @@ GenTree* Compiler::gtNewPhysRegNode(regNumber reg, var_types type) return result; } -#ifndef LEGACY_BACKEND GenTree* Compiler::gtNewJmpTableNode() { GenTree* node = new (this, GT_JMPTABLE) GenTreeJumpTable(TYP_INT); node->gtJumpTable.gtJumpTableAddr = 0; return node; } -#endif // !LEGACY_BACKEND /***************************************************************************** * @@ -6754,10 +6232,6 @@ GenTreeCall* Compiler::gtNewCallNode( node->gtCallLateArgs = nullptr; node->gtReturnType = type; -#ifdef LEGACY_BACKEND - node->gtCallRegUsedMask = RBM_NONE; -#endif // LEGACY_BACKEND - #ifdef FEATURE_READYTORUN_COMPILER node->gtEntryPoint.addr = nullptr; node->gtEntryPoint.accessType = IAT_VALUE; @@ -6803,7 +6277,7 @@ GenTreeCall* Compiler::gtNewCallNode( // Initialize spill flags of gtOtherRegs node->ClearOtherRegFlags(); -#if (defined(_TARGET_X86_) || defined(_TARGET_ARM_)) && !defined(LEGACY_BACKEND) +#if defined(_TARGET_X86_) || defined(_TARGET_ARM_) // Initialize the multi-reg long return info if necessary if (varTypeIsLong(node)) { @@ -6817,7 +6291,7 @@ GenTreeCall* Compiler::gtNewCallNode( // must be a long returned in two registers assert(retTypeDesc->GetReturnRegCount() == 2); } -#endif // (defined(_TARGET_X86_) || defined(_TARGET_ARM_)) && !defined(_LEGACY_BACKEND_) +#endif // defined(_TARGET_X86_) || defined(_TARGET_ARM_) return node; } @@ -7235,7 +6709,6 @@ GenTree* Compiler::gtNewBlockVal(GenTree* addr, unsigned size) } else #endif // FEATURE_SIMD -#ifndef LEGACY_BACKEND if (val->TypeGet() == TYP_STRUCT) { GenTreeLclVarCommon* lcl = addr->gtGetOp1()->AsLclVarCommon(); @@ -7245,7 +6718,6 @@ GenTree* Compiler::gtNewBlockVal(GenTree* addr, unsigned size) return addr->gtGetOp1(); } } -#endif // !LEGACY_BACKEND } return new (this, GT_BLK) GenTreeBlk(GT_BLK, blkType, addr, size); } @@ -7544,7 +7016,7 @@ GenTree* Compiler::gtNewPutArgReg(var_types type, GenTree* arg, regNumber argReg assert(arg != nullptr); GenTree* node = nullptr; -#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_) +#if defined(_TARGET_ARM_) // A PUTARG_REG could be a MultiRegOp on arm since we could move a double register to two int registers. node = new (this, GT_PUTARG_REG) GenTreeMultiRegOp(GT_PUTARG_REG, type, arg, nullptr); #else @@ -7574,7 +7046,7 @@ GenTree* Compiler::gtNewBitCastNode(var_types type, GenTree* arg) assert(arg != nullptr); GenTree* node = nullptr; -#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_) +#if defined(_TARGET_ARM_) // A BITCAST could be a MultiRegOp on arm since we could move a double register to two int registers. node = new (this, GT_BITCAST) GenTreeMultiRegOp(GT_BITCAST, type, arg, nullptr); #else @@ -8005,10 +7477,6 @@ GenTree* Compiler::gtCloneExpr( case GT_QMARK: copy = new (this, GT_QMARK) GenTreeQmark(tree->TypeGet(), tree->gtOp.gtOp1, tree->gtOp.gtOp2, this); -#ifdef LEGACY_BACKEND - VarSetOps::AssignAllowUninitRhs(this, copy->gtQmark.gtThenLiveSet, tree->gtQmark.gtThenLiveSet); - VarSetOps::AssignAllowUninitRhs(this, copy->gtQmark.gtElseLiveSet, tree->gtQmark.gtElseLiveSet); -#endif break; case GT_OBJ: @@ -8162,16 +7630,6 @@ GenTree* Compiler::gtCloneExpr( copy->gtFlags |= (copy->gtGetOp2()->gtFlags & GTF_ALL_EFFECT); } -#ifdef LEGACY_BACKEND - // The early morph for TailCall creates a GT_NOP with GTF_REG_VAL flag set - // Thus we have to copy the gtRegNum/gtRegPair value if we clone it here. - // - if (tree->InReg()) - { - copy->CopyReg(tree); - } -#endif // LEGACY_BACKEND - goto DONE; } @@ -8252,10 +7710,6 @@ GenTree* Compiler::gtCloneExpr( copy->gtCall.gtReturnTypeDesc = tree->gtCall.gtReturnTypeDesc; #endif -#ifdef LEGACY_BACKEND - copy->gtCall.gtCallRegUsedMask = tree->gtCall.gtCallRegUsedMask; -#endif // LEGACY_BACKEND - #ifdef FEATURE_READYTORUN_COMPILER copy->gtCall.setEntryPoint(tree->gtCall.gtEntryPoint); #endif @@ -8357,10 +7811,6 @@ DONE: copy->gtVNPair = tree->gtVNPair; // A cloned tree gets the orginal's Value number pair - /* We assume the FP stack level will be identical */ - - copy->gtCopyFPlvl(tree); - /* Compute the flags for the copied node. Note that we can do this only if we didnt gtFoldExpr(copy) */ @@ -8809,27 +8259,6 @@ bool GenTree::gtSetFlags() const return false; } -#if defined(LEGACY_BACKEND) && !FEATURE_SET_FLAGS && defined(_TARGET_XARCH_) - // Return true if/when the codegen for this node will set the flags - // - // - if ((gtOper == GT_IND) || (gtOper == GT_MUL) || (gtOper == GT_DIV)) - { - return false; - } - else if (gtOverflowEx()) - { - return false; - } - else - { - return true; - } -#else // !(defined(LEGACY_BACKEND) && !FEATURE_SET_FLAGS && defined(_TARGET_XARCH_)) - -#if FEATURE_SET_FLAGS && defined(LEGACY_BACKEND) - assert(OperIsSimple()); -#endif if (((gtFlags & GTF_SET_FLAGS) != 0) && (gtOper != GT_IND)) { // GTF_SET_FLAGS is not valid on GT_IND and is overlaid with GTF_NONFAULTING_IND @@ -8839,7 +8268,6 @@ bool GenTree::gtSetFlags() const { return false; } -#endif // !(defined(LEGACY_BACKEND) && !FEATURE_SET_FLAGS && defined(_TARGET_XARCH_)) } bool GenTree::gtRequestSetFlags() @@ -8905,14 +8333,6 @@ void GenTree::CopyTo(class Compiler* comp, const GenTree& gt) gtRsvdRegs = gt.gtRsvdRegs; -#ifdef LEGACY_BACKEND - gtUsedRegs = gt.gtUsedRegs; -#endif // LEGACY_BACKEND - -#if FEATURE_STACK_FP_X87 - gtFPlvl = gt.gtFPlvl; -#endif // FEATURE_STACK_FP_X87 - gtNext = gt.gtNext; gtPrev = gt.gtPrev; #ifdef DEBUG @@ -9320,9 +8740,7 @@ GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node) case GT_END_LFIN: #endif // !FEATURE_EH_FUNCLETS case GT_PHI_ARG: -#ifndef LEGACY_BACKEND case GT_JMPTABLE: -#endif // LEGACY_BACKEND case GT_REG_VAR: case GT_CLS_VAR: case GT_CLS_VAR_ADDR: @@ -9360,9 +8778,9 @@ GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node) case GT_NULLCHECK: case GT_PUTARG_REG: case GT_PUTARG_STK: -#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_) +#if defined(_TARGET_ARM_) case GT_PUTARG_SPLIT: -#endif // !LEGACY_BACKEND && _TARGET_ARM_ +#endif // _TARGET_ARM_ case GT_RETURNTRAP: m_edge = &m_node->AsUnOp()->gtOp1; assert(*m_edge != nullptr); @@ -9911,11 +9329,7 @@ bool GenTree::Precedes(GenTree* other) { int charsDisplayed = 11; // 11 is the "baseline" number of flag characters displayed -#ifdef LEGACY_BACKEND - printf("%c", (flags & GTF_ASG) ? 'A' : '-'); -#else // !LEGACY_BACKEND printf("%c", (flags & GTF_ASG) ? 'A' : (IsContained(flags) ? 'c' : '-')); -#endif // LEGACY_BACKEND printf("%c", (flags & GTF_CALL) ? 'C' : '-'); printf("%c", (flags & GTF_EXCEPT) ? 'X' : '-'); printf("%c", (flags & GTF_GLOB_REF) ? 'G' : '-'); @@ -10361,7 +9775,7 @@ void Compiler::gtDispNode(GenTree* tree, IndentStack* indentStack, __in __in_z _ goto DASH; case GT_MUL: -#if !defined(_TARGET_64BIT_) && !defined(LEGACY_BACKEND) +#if !defined(_TARGET_64BIT_) case GT_MUL_LONG: #endif if (tree->gtFlags & GTF_MUL_64RSLT) @@ -10420,10 +9834,8 @@ void Compiler::gtDispNode(GenTree* tree, IndentStack* indentStack, __in __in_z _ case GT_LE: case GT_GE: case GT_GT: -#ifndef LEGACY_BACKEND case GT_TEST_EQ: case GT_TEST_NE: -#endif if (tree->gtFlags & GTF_RELOP_NAN_UN) { printf("N"); @@ -10488,34 +9900,22 @@ void Compiler::gtDispNode(GenTree* tree, IndentStack* indentStack, __in __in_z _ } msgLength -= GenTree::gtDispFlags(flags, tree->gtDebugFlags); -/* - printf("%c", (flags & GTF_ASG ) ? 'A' : '-'); - printf("%c", (flags & GTF_CALL ) ? 'C' : '-'); - printf("%c", (flags & GTF_EXCEPT ) ? 'X' : '-'); - printf("%c", (flags & GTF_GLOB_REF ) ? 'G' : '-'); - printf("%c", (flags & GTF_ORDER_SIDEEFF ) ? 'O' : '-'); - printf("%c", (flags & GTF_COLON_COND ) ? '?' : '-'); - printf("%c", (flags & GTF_DONT_CSE ) ? 'N' : // N is for No cse - (flags & GTF_MAKE_CSE ) ? 'H' : '-'); // H is for Hoist this expr - printf("%c", (flags & GTF_REVERSE_OPS ) ? 'R' : '-'); - printf("%c", (flags & GTF_UNSIGNED ) ? 'U' : - (flags & GTF_BOOLEAN ) ? 'B' : '-'); - printf("%c", (flags & GTF_SET_FLAGS ) ? 'S' : '-'); - printf("%c", (flags & GTF_SPILLED ) ? 'z' : '-'); - printf("%c", (flags & GTF_SPILL ) ? 'Z' : '-'); -*/ - -#if FEATURE_STACK_FP_X87 - BYTE fpLvl = (BYTE)tree->gtFPlvl; - if (IsUninitialized(fpLvl) || fpLvl == 0x00) - { - printf("-"); - } - else - { - printf("%1u", tree->gtFPlvl); - } -#endif // FEATURE_STACK_FP_X87 + /* + printf("%c", (flags & GTF_ASG ) ? 'A' : '-'); + printf("%c", (flags & GTF_CALL ) ? 'C' : '-'); + printf("%c", (flags & GTF_EXCEPT ) ? 'X' : '-'); + printf("%c", (flags & GTF_GLOB_REF ) ? 'G' : '-'); + printf("%c", (flags & GTF_ORDER_SIDEEFF ) ? 'O' : '-'); + printf("%c", (flags & GTF_COLON_COND ) ? '?' : '-'); + printf("%c", (flags & GTF_DONT_CSE ) ? 'N' : // N is for No cse + (flags & GTF_MAKE_CSE ) ? 'H' : '-'); // H is for Hoist this expr + printf("%c", (flags & GTF_REVERSE_OPS ) ? 'R' : '-'); + printf("%c", (flags & GTF_UNSIGNED ) ? 'U' : + (flags & GTF_BOOLEAN ) ? 'B' : '-'); + printf("%c", (flags & GTF_SET_FLAGS ) ? 'S' : '-'); + printf("%c", (flags & GTF_SPILLED ) ? 'z' : '-'); + printf("%c", (flags & GTF_SPILL ) ? 'Z' : '-'); + */ } // If we're printing a node for LIR, we use the space normally associated with the message @@ -10663,10 +10063,6 @@ void Compiler::gtDispNode(GenTree* tree, IndentStack* indentStack, __in __in_z _ { printf(" RR="); dspRegMask(tree->gtRsvdRegs); -#ifdef LEGACY_BACKEND - printf(",UR="); - dspRegMask(tree->gtUsedRegs); -#endif // LEGACY_BACKEND printf("\n"); } } @@ -10683,12 +10079,6 @@ void Compiler::gtDispRegVal(GenTree* tree) printf(" REG %s", compRegVarName(tree->gtRegNum)); break; -#if CPU_LONG_USES_REGPAIR - case GenTree::GT_REGTAG_REGPAIR: - printf(" PAIR %s", compRegPairName(tree->gtRegPair)); - break; -#endif - default: break; } @@ -10730,19 +10120,12 @@ void Compiler::gtDispRegVal(GenTree* tree) } #endif -#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_) +#if defined(_TARGET_ARM_) if (tree->OperIsMultiRegOp() && (tree->AsMultiRegOp()->gtOtherReg != REG_NA)) { printf(",%s", compRegVarName(tree->AsMultiRegOp()->gtOtherReg)); } #endif - -#ifdef LEGACY_BACKEND - if (tree->InReg()) - { - printf(" RV"); - } -#endif } // We usually/commonly don't expect to print anything longer than this string, @@ -11214,17 +10597,10 @@ void Compiler::gtDispLeaf(GenTree* tree, IndentStack* indentStack) printf(" "); varDsc->PrintVarReg(); } -#ifndef LEGACY_BACKEND else if (tree->InReg()) { -#if CPU_LONG_USES_REGPAIR - if (isRegPairType(tree->TypeGet())) - printf(" %s", compRegPairName(tree->gtRegPair)); - else -#endif - printf(" %s", compRegVarName(tree->gtRegNum)); + printf(" %s", compRegVarName(tree->gtRegNum)); } -#endif // !LEGACY_BACKEND if (varDsc->lvPromoted) { @@ -11364,9 +10740,7 @@ void Compiler::gtDispLeaf(GenTree* tree, IndentStack* indentStack) case GT_MEMORYBARRIER: case GT_ARGPLACE: case GT_PINVOKE_PROLOG: -#ifndef LEGACY_BACKEND case GT_JMPTABLE: -#endif // !LEGACY_BACKEND break; case GT_RET_EXPR: @@ -13292,9 +12666,6 @@ GenTree* Compiler::gtFoldExprSpecial(GenTree* tree) break; case GT_ADD: -#ifdef LEGACY_BACKEND - case GT_ASG_ADD: -#endif if (val == 0) { goto DONE_FOLD; @@ -13302,9 +12673,6 @@ GenTree* Compiler::gtFoldExprSpecial(GenTree* tree) break; case GT_MUL: -#ifdef LEGACY_BACKEND - case GT_ASG_MUL: -#endif if (val == 1) { goto DONE_FOLD; @@ -13326,9 +12694,6 @@ GenTree* Compiler::gtFoldExprSpecial(GenTree* tree) case GT_DIV: case GT_UDIV: -#ifdef LEGACY_BACKEND - case GT_ASG_DIV: -#endif if ((op2 == cons) && (val == 1) && !(op1->OperKind() & GTK_CONST)) { goto DONE_FOLD; @@ -13336,9 +12701,6 @@ GenTree* Compiler::gtFoldExprSpecial(GenTree* tree) break; case GT_SUB: -#ifdef LEGACY_BACKEND - case GT_ASG_SUB: -#endif if ((op2 == cons) && (val == 0) && !(op1->OperKind() & GTK_CONST)) { goto DONE_FOLD; @@ -13407,11 +12769,6 @@ GenTree* Compiler::gtFoldExprSpecial(GenTree* tree) case GT_RSZ: case GT_ROL: case GT_ROR: -#ifdef LEGACY_BACKEND - case GT_ASG_LSH: - case GT_ASG_RSH: - case GT_ASG_RSZ: -#endif if (val == 0) { if (op2 == cons) @@ -14026,9 +13383,6 @@ GenTree* Compiler::gtFoldExprConst(GenTree* tree) break; case GT_NEG: -#ifdef LEGACY_BACKEND - case GT_CHS: -#endif i1 = -i1; break; @@ -14164,9 +13518,6 @@ GenTree* Compiler::gtFoldExprConst(GenTree* tree) break; case GT_NEG: -#ifdef LEGACY_BACKEND - case GT_CHS: -#endif lval1 = -lval1; break; @@ -14271,9 +13622,6 @@ GenTree* Compiler::gtFoldExprConst(GenTree* tree) switch (tree->gtOper) { case GT_NEG: -#ifdef LEGACY_BACKEND - case GT_CHS: -#endif d1 = -d1; break; @@ -14764,20 +14112,14 @@ GenTree* Compiler::gtFoldExprConst(GenTree* tree) // update args table. For this reason this optimization is enabled only // for global morphing phase. // - // X86/Arm32 legacy codegen note: This is not an issue on x86 for the reason that - // it doesn't use arg table for calls. In addition x86/arm32 legacy codegen doesn't - // expect long constants to show up as an operand of overflow cast operation. - // // TODO-CQ: Once fgMorphArgs() is fixed this restriction could be removed. CLANG_FORMAT_COMMENT_ANCHOR; -#ifndef LEGACY_BACKEND if (!fgGlobalMorph) { assert(tree->gtOverflow()); return tree; } -#endif // !LEGACY_BACKEND op1 = gtNewLconNode(0); if (vnStore != nullptr) @@ -14787,7 +14129,6 @@ GenTree* Compiler::gtFoldExprConst(GenTree* tree) goto OVF; INT_OVF: -#ifndef LEGACY_BACKEND // Don't fold overflow operations if not global morph phase. // The reason for this is that this optimization is replacing a gentree node // with another new gentree node. Say a GT_CALL(arglist) has one 'arg' @@ -14798,10 +14139,6 @@ GenTree* Compiler::gtFoldExprConst(GenTree* tree) // update args table. For this reason this optimization is enabled only // for global morphing phase. // - // X86/Arm32 legacy codegen note: This is not an issue on x86 for the reason that - // it doesn't use arg table for calls. In addition x86/arm32 legacy codegen doesn't - // expect long constants to show up as an operand of overflow cast operation. - // // TODO-CQ: Once fgMorphArgs() is fixed this restriction could be removed. if (!fgGlobalMorph) @@ -14809,7 +14146,6 @@ GenTree* Compiler::gtFoldExprConst(GenTree* tree) assert(tree->gtOverflow()); return tree; } -#endif // !LEGACY_BACKEND op1 = gtNewIconNode(0); if (vnStore != nullptr) @@ -15225,22 +14561,6 @@ GenTree* Compiler::gtFoldExprConst(GenTree* tree) i1 = (d1 > d2); goto FOLD_COND; -#if FEATURE_STACK_FP_X87 - case GT_ADD: - d1 += d2; - break; - case GT_SUB: - d1 -= d2; - break; - case GT_MUL: - d1 *= d2; - break; - case GT_DIV: - if (!d2) - return tree; - d1 /= d2; - break; -#else //! FEATURE_STACK_FP_X87 // non-x86 arch: floating point arithmetic should be done in declared // precision while doing constant folding. For this reason though TYP_FLOAT // constants are stored as double constants, while performing float arithmetic, @@ -15308,7 +14628,6 @@ GenTree* Compiler::gtFoldExprConst(GenTree* tree) d1 /= d2; } break; -#endif //! FEATURE_STACK_FP_X87 default: return tree; @@ -15483,12 +14802,10 @@ GenTree* Compiler::gtNewTempAssign(unsigned tmp, GenTree* val) asg = gtNewAssignNode(dest, val); } -#ifndef LEGACY_BACKEND if (compRationalIRForm) { Rationalizer::RewriteAssignmentIntoStoreLcl(asg->AsOp()); } -#endif // !LEGACY_BACKEND return asg; } @@ -16714,14 +16031,6 @@ unsigned GenTree::IsLclVarUpdateTree(GenTree** pOtherTree, genTreeOps* pOper) *pOper = rhs->gtOper; } } -#ifdef LEGACY_BACKEND - else - { - lclNum = lhsLclNum; - *pOper = GenTree::OpAsgToOper(gtOper); - *pOtherTree = gtOp.gtOp2; - } -#endif } } return lclNum; @@ -16766,9 +16075,6 @@ bool GenTree::canBeContained() const // bool GenTree::isContained() const { -#ifdef LEGACY_BACKEND - return false; -#else // !LEGACY_BACKEND assert(IsLIR()); const bool isMarkedContained = ((gtFlags & GTF_CONTAINED) != 0); @@ -16801,7 +16107,6 @@ bool GenTree::isContained() const } #endif // DEBUG return isMarkedContained; -#endif // !LEGACY_BACKEND } // return true if node is contained and an indir @@ -16936,7 +16241,6 @@ bool GenTreeIntConCommon::ImmedValCanBeFolded(Compiler* comp, genTreeOps op) // be encoded as 32-bit offset relative to IP or zero. bool GenTreeIntConCommon::FitsInAddrBase(Compiler* comp) { -#ifndef LEGACY_BACKEND #ifdef DEBUG // Early out if PC-rel encoding of absolute addr is disabled. if (!comp->opts.compEnablePCRelAddr) @@ -16944,7 +16248,6 @@ bool GenTreeIntConCommon::FitsInAddrBase(Compiler* comp) return false; } #endif -#endif //! LEGACY_BACKEND if (comp->opts.compReloc) { @@ -16995,7 +16298,6 @@ bool GenTreeIntConCommon::AddrNeedsReloc(Compiler* comp) // On x86 all addresses are 4-bytes and can be directly encoded in an addr mode. bool GenTreeIntConCommon::FitsInAddrBase(Compiler* comp) { -#ifndef LEGACY_BACKEND #ifdef DEBUG // Early out if PC-rel encoding of absolute addr is disabled. if (!comp->opts.compEnablePCRelAddr) @@ -17003,7 +16305,6 @@ bool GenTreeIntConCommon::FitsInAddrBase(Compiler* comp) return false; } #endif -#endif //! LEGACY_BACKEND return IsCnsIntOrI(); } @@ -18660,8 +17961,6 @@ regMaskTP ReturnTypeDesc::GetABIReturnRegs() return resultMask; } -#ifndef LEGACY_BACKEND - //------------------------------------------------------------------------ // The following functions manage the gtRsvdRegs set of temporary registers // created by LSRA during code generation. @@ -18722,5 +18021,3 @@ regNumber GenTree::ExtractTempReg(regMaskTP mask /* = (regMaskTP)-1 */) gtRsvdRegs &= ~tempRegMask; return genRegNumFromMask(tempRegMask); } - -#endif // !LEGACY_BACKEND diff --git a/src/jit/gentree.h b/src/jit/gentree.h index 8ff97e42fe..a66f6c6d2b 100644 --- a/src/jit/gentree.h +++ b/src/jit/gentree.h @@ -107,9 +107,6 @@ enum genTreeKinds GTK_BINOP = 0x0008, // binary operator GTK_RELOP = 0x0010, // comparison operator GTK_LOGOP = 0x0020, // logical operator -#ifdef LEGACY_BACKEND - GTK_ASGOP = 0x0040, // assignment operator -#endif GTK_KINDMASK = 0x007F, // operator kind mask @@ -433,28 +430,6 @@ struct GenTree } #endif -#if FEATURE_STACK_FP_X87 - unsigned char gtFPlvl; // x87 stack depth at this node - void gtCopyFPlvl(GenTree* other) - { - gtFPlvl = other->gtFPlvl; - } - void gtSetFPlvl(unsigned level) - { - noway_assert(FitsIn(level)); - gtFPlvl = (unsigned char)level; - } -#else // FEATURE_STACK_FP_X87 - - void gtCopyFPlvl(GenTree* other) - { - } - void gtSetFPlvl(unsigned level) - { - } - -#endif // FEATURE_STACK_FP_X87 - // // Cost metrics on the node. Don't allow direct access to the variable for setting. // @@ -532,37 +507,27 @@ private: CLANG_FORMAT_COMMENT_ANCHOR; #ifdef DEBUG + public: enum genRegTag { - GT_REGTAG_NONE, // Nothing has been assigned to _gtRegNum/_gtRegPair - GT_REGTAG_REG, // _gtRegNum has been assigned -#if CPU_LONG_USES_REGPAIR - GT_REGTAG_REGPAIR // _gtRegPair has been assigned -#endif + GT_REGTAG_NONE, // Nothing has been assigned to _gtRegNum + GT_REGTAG_REG // _gtRegNum has been assigned }; genRegTag GetRegTag() const { -#if CPU_LONG_USES_REGPAIR - assert(gtRegTag == GT_REGTAG_NONE || gtRegTag == GT_REGTAG_REG || gtRegTag == GT_REGTAG_REGPAIR); -#else assert(gtRegTag == GT_REGTAG_NONE || gtRegTag == GT_REGTAG_REG); -#endif return gtRegTag; } private: - genRegTag gtRegTag; // What is in _gtRegNum/_gtRegPair? -#endif // DEBUG + genRegTag gtRegTag; // What is in _gtRegNum? + +#endif // DEBUG private: - union { - // These store the register assigned to the node. If a register is not assigned, _gtRegNum is set to REG_NA - // or _gtRegPair is set to REG_PAIR_NONE, depending on the node type. - // For the LEGACY_BACKEND,these are valid only if GTF_REG_VAL is set in gtFlags. - regNumberSmall _gtRegNum; // which register the value is in - regPairNoSmall _gtRegPair; // which register pair the value is in - }; + // This stores the register assigned to the node. If a register is not assigned, _gtRegNum is set to REG_NA. + regNumberSmall _gtRegNum; public: // The register number is stored in a small format (8 bits), but the getters return and the setters take @@ -641,40 +606,12 @@ public: void SetRegNum(regNumber reg) { assert(reg >= REG_FIRST && reg <= REG_COUNT); - // Make sure the upper bits of _gtRegPair are clear - _gtRegPair = (regPairNoSmall)0; - _gtRegNum = (regNumberSmall)reg; + _gtRegNum = (regNumberSmall)reg; INDEBUG(gtRegTag = GT_REGTAG_REG;) assert(_gtRegNum == reg); } -#if CPU_LONG_USES_REGPAIR - __declspec(property(get = GetRegPair, put = SetRegPair)) regPairNo gtRegPair; - - regPairNo GetRegPair() const - { - assert((gtRegTag == GT_REGTAG_REGPAIR) || (gtRegTag == GT_REGTAG_NONE)); // TODO-Cleanup: get rid of the NONE - // case, and fix everyplace that reads - // undefined values - regPairNo regPair = (regPairNo)_gtRegPair; - assert((gtRegTag == GT_REGTAG_NONE) || // TODO-Cleanup: get rid of the NONE case, and fix everyplace that reads - // undefined values - (regPair >= REG_PAIR_FIRST && regPair <= REG_PAIR_LAST) || - (regPair == REG_PAIR_NONE)); // allow initializing to an undefined value - return regPair; - } - - void SetRegPair(regPairNo regPair) - { - assert((regPair >= REG_PAIR_FIRST && regPair <= REG_PAIR_LAST) || - (regPair == REG_PAIR_NONE)); // allow initializing to an undefined value - _gtRegPair = (regPairNoSmall)regPair; - INDEBUG(gtRegTag = GT_REGTAG_REGPAIR;) - assert(_gtRegPair == regPair); - } -#endif - - // Copy the _gtRegNum/_gtRegPair/gtRegTag fields + // Copy the _gtRegNum/gtRegTag fields void CopyReg(GenTree* from); bool gtHasReg() const; @@ -692,15 +629,9 @@ public: regMaskSmall gtRsvdRegs; // set of fixed trashed registers -#ifndef LEGACY_BACKEND unsigned AvailableTempRegCount(regMaskTP mask = (regMaskTP)-1) const; regNumber GetSingleTempReg(regMaskTP mask = (regMaskTP)-1); regNumber ExtractTempReg(regMaskTP mask = (regMaskTP)-1); -#endif // !LEGACY_BACKEND - -#ifdef LEGACY_BACKEND - regMaskSmall gtUsedRegs; // set of used (trashed) registers -#endif // LEGACY_BACKEND void SetVNsFromNode(GenTree* tree) { @@ -791,22 +722,10 @@ public: (((flags) & (GTF_CALL | GTF_EXCEPT)) || (((flags) & (GTF_ASG | GTF_GLOB_REF)) == (GTF_ASG | GTF_GLOB_REF))) #define GTF_REVERSE_OPS 0x00000020 // operand op2 should be evaluated before op1 (normally, op1 is evaluated first and op2 is evaluated second) - -#ifdef LEGACY_BACKEND -#define GTF_REG_VAL 0x00000040 // operand is sitting in a register (or part of a TYP_LONG operand is sitting in a register) -#else // !LEGACY_BACKEND #define GTF_CONTAINED 0x00000040 // This node is contained (executed as part of its parent) -#endif // !LEGACY_BACKEND - #define GTF_SPILLED 0x00000080 // the value has been spilled -#ifdef LEGACY_BACKEND -#define GTF_SPILLED_OPER 0x00000100 // op1 has been spilled -#define GTF_SPILLED_OP2 0x00000200 // op2 has been spilled -#define GTF_ZSF_SET 0x00000400 // the zero(ZF) and sign(SF) flags set to the operand -#else // !LEGACY_BACKEND #define GTF_NOREG_AT_USE 0x00000100 // tree node is in memory at the point of use -#endif // !LEGACY_BACKEND #define GTF_SET_FLAGS 0x00000800 // Requires that codegen for this node set the flags. Use gtSetFlags() to check this flag. #define GTF_USE_FLAGS 0x00001000 // Indicates that this node uses the flags bits. @@ -818,9 +737,6 @@ public: #define GTF_NODE_MASK (GTF_COLON_COND) #define GTF_BOOLEAN 0x00040000 // value is known to be 0/1 -#if CPU_HAS_BYTE_REGS && defined(LEGACY_BACKEND) -#define GTF_SMALL_OK 0x00080000 // actual small int sufficient -#endif #define GTF_UNSIGNED 0x00100000 // With GT_CAST: the source operand is an unsigned type // With operators: the specified node is an unsigned operator @@ -864,6 +780,8 @@ public: #define GTF_LIVENESS_MASK (GTF_VAR_DEF | GTF_VAR_USEASG | GTF_REG_BIRTH | GTF_VAR_DEATH) + // For additional flags for GT_CALL node see GTF_CALL_M_* + #define GTF_CALL_UNMANAGED 0x80000000 // GT_CALL -- direct call to unmanaged code #define GTF_CALL_INLINE_CANDIDATE 0x40000000 // GT_CALL -- this call has been marked as an inline candidate @@ -875,17 +793,6 @@ public: #define GTF_CALL_NULLCHECK 0x08000000 // GT_CALL -- must check instance pointer for null #define GTF_CALL_POP_ARGS 0x04000000 // GT_CALL -- caller pop arguments? #define GTF_CALL_HOISTABLE 0x02000000 // GT_CALL -- call is hoistable -#ifdef LEGACY_BACKEND -#ifdef _TARGET_ARM_ -// The GTF_CALL_REG_SAVE flag indicates that the call preserves all integer registers. This is used for -// the PollGC helper. However, since the PollGC helper on ARM follows the standard calling convention, -// for that target we don't use this flag. -#define GTF_CALL_REG_SAVE 0x00000000 -#else -#define GTF_CALL_REG_SAVE 0x01000000 // GT_CALL -- This call preserves all integer regs -#endif // _TARGET_ARM_ -#endif // LEGACY_BACKEND - // For additional flags for GT_CALL node see GTF_CALL_M_* #define GTF_NOP_DEATH 0x40000000 // GT_NOP -- operand dies here @@ -933,12 +840,6 @@ public: #define GTF_MUL_64RSLT 0x40000000 // GT_MUL -- produce 64-bit result -#ifdef LEGACY_BACKEND -#define GTF_MOD_INT_RESULT 0x80000000 // GT_MOD, -- the real tree represented by this - // GT_UMOD node evaluates to an int even though its type is long. - // The result is placed in the low member of the reg pair. -#endif // LEGACY_BACKEND - #define GTF_RELOP_NAN_UN 0x80000000 // GT_ -- Is branch taken if ops are NaN? #define GTF_RELOP_JMP_USED 0x40000000 // GT_ -- result of compare used for jump or ?: #define GTF_RELOP_QMARK 0x20000000 // GT_ -- the node is the condition for ?: @@ -1282,7 +1183,7 @@ public: bool OperIsPutArgSplit() const { -#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_) +#if defined(_TARGET_ARM_) return gtOper == GT_PUTARG_SPLIT; #else return false; @@ -1311,7 +1212,7 @@ public: bool OperIsMultiRegOp() const { -#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_) +#if defined(_TARGET_ARM_) if ((gtOper == GT_MUL_LONG) || (gtOper == GT_PUTARG_REG) || (gtOper == GT_BITCAST)) { return true; @@ -1505,11 +1406,7 @@ public: static bool OperIsAssignment(genTreeOps gtOper) { -#ifdef LEGACY_BACKEND - return (OperKind(gtOper) & GTK_ASGOP) != 0; -#else return gtOper == GT_ASG; -#endif } bool OperIsAssignment() const @@ -1520,9 +1417,7 @@ public: static bool OperMayOverflow(genTreeOps gtOper) { return ((gtOper == GT_ADD) || (gtOper == GT_SUB) || (gtOper == GT_MUL) || (gtOper == GT_CAST) -#ifdef LEGACY_BACKEND - || (gtOper == GT_ASG_ADD) || (gtOper == GT_ASG_SUB) -#elif !defined(_TARGET_64BIT_) +#if !defined(_TARGET_64BIT_) || (gtOper == GT_ADD_HI) || (gtOper == GT_SUB_HI) #endif ); @@ -1622,7 +1517,7 @@ public: // This is here for cleaner GT_LONG #ifdefs. static bool OperIsLong(genTreeOps gtOper) { -#if defined(_TARGET_64BIT_) || defined(LEGACY_BACKEND) +#if defined(_TARGET_64BIT_) return false; #else return gtOper == GT_LONG; @@ -1670,12 +1565,6 @@ public: return OperIsBoundsCheck(OperGet()); } -#ifdef LEGACY_BACKEND - // Requires that "op" is an op= operator. Returns - // the corresponding "op". - static genTreeOps OpAsgToOper(genTreeOps op); -#endif - #ifdef DEBUG bool NullOp1Legal() const { @@ -1718,9 +1607,10 @@ public: case GT_HWIntrinsic: #endif // FEATURE_HW_INTRINSICS -#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_) +#if defined(_TARGET_ARM_) case GT_PUTARG_REG: -#endif // !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_) +#endif // defined(_TARGET_ARM_) + return true; default: return false; @@ -2007,36 +1897,11 @@ public: // sets "*pIsEntire" to true if this assignment writes the full width of the local. bool DefinesLocalAddr(Compiler* comp, unsigned width, GenTreeLclVarCommon** pLclVarTree, bool* pIsEntire); -#ifdef LEGACY_BACKEND - bool IsRegVar() const - { - return OperGet() == GT_REG_VAR ? true : false; - } - bool InReg() const - { - return (gtFlags & GTF_REG_VAL) ? true : false; - } - void SetInReg(bool value = true) - { - if (value == true) - { - gtFlags |= GTF_REG_VAL; - } - else - { - gtFlags &= ~GTF_REG_VAL; - } - } - regNumber GetReg() const - { - return InReg() ? gtRegNum : REG_NA; - } - -#else // !LEGACY_BACKEND - // For the non-legacy backend, these are only used for dumping. + // These are only used for dumping. // The gtRegNum is only valid in LIR, but the dumping methods are not easily // modified to check this. CLANG_FORMAT_COMMENT_ANCHOR; + #ifdef DEBUG bool InReg() const { @@ -2067,8 +1932,6 @@ public: ClearRegOptional(); } -#endif // !LEGACY_BACKEND - bool IsRegVarDeath() const { assert(OperGet() == GT_REG_VAR); @@ -2154,15 +2017,6 @@ public: bool gtSetFlags() const; bool gtRequestSetFlags(); -#ifdef LEGACY_BACKEND - // Returns true if the codegen of this tree node - // sets ZF and SF flags. - bool gtSetZSFlags() const - { - return (gtFlags & GTF_ZSF_SET) != 0; - } -#endif - #ifdef DEBUG bool gtIsValid64RsltMul(); static int gtDispFlags(unsigned flags, unsigned debugFlags); @@ -2628,7 +2482,6 @@ struct GenTreePhysReg : public GenTree #endif }; -#ifndef LEGACY_BACKEND // gtJumpTable - Switch Jump Table // // This node stores a DWORD constant that represents the @@ -2650,7 +2503,6 @@ struct GenTreeJumpTable : public GenTreeIntConCommon } #endif // DEBUG }; -#endif // !LEGACY_BACKEND /* gtIntCon -- integer constant (GT_CNS_INT) */ struct GenTreeIntCon : public GenTreeIntConCommon @@ -3163,11 +3015,10 @@ struct GenTreeFieldList : public GenTreeArgList if (prevList == nullptr) { gtFlags |= GTF_FIELD_LIST_HEAD; -#ifndef LEGACY_BACKEND + // A GT_FIELD_LIST head is always contained. Other nodes return false from IsValue() // and should not be marked as contained. SetContained(); -#endif } else { @@ -3369,10 +3220,6 @@ struct GenTreeCall final : public GenTree // FEATURE_FIXED_OUT_ARGS was enabled, so this makes GenTreeCall 4 bytes bigger on x86). CORINFO_SIG_INFO* callSig; // Used by tail calls and to register callsites with the EE -#ifdef LEGACY_BACKEND - regMaskTP gtCallRegUsedMask; // mask of registers used to pass parameters -#endif // LEGACY_BACKEND - #if FEATURE_MULTIREG_RET // State required to support multi-reg returning call nodes. @@ -3634,10 +3481,7 @@ struct GenTreeCall final : public GenTree #define GTF_CALL_M_NONVIRT_SAME_THIS 0x00000080 // GT_CALL -- callee "this" pointer is // equal to caller this pointer (only for GTF_CALL_NONVIRT) #define GTF_CALL_M_FRAME_VAR_DEATH 0x00000100 // GT_CALL -- the compLvFrameListRoot variable dies here (last use) - -#ifndef LEGACY_BACKEND #define GTF_CALL_M_TAILCALL_VIA_HELPER 0x00000200 // GT_CALL -- call is a tail call dispatched via tail call JIT helper. -#endif #if FEATURE_TAILCALL_OPT #define GTF_CALL_M_IMPLICIT_TAILCALL 0x00000400 // GT_CALL -- call is an opportunistic @@ -3694,10 +3538,8 @@ struct GenTreeCall final : public GenTree return (gtFlags & GTF_CALL_INLINE_CANDIDATE) != 0; } -#ifndef LEGACY_BACKEND bool HasNonStandardAddedArgs(Compiler* compiler) const; int GetNonStandardAddedArgCount(Compiler* compiler) const; -#endif // !LEGACY_BACKEND // Returns true if this call uses a retBuf argument and its calling convention bool HasRetBufArg() const @@ -3739,11 +3581,9 @@ struct GenTreeCall final : public GenTree // bool HasMultiRegRetVal() const { -#if defined(_TARGET_X86_) && !defined(LEGACY_BACKEND) - // LEGACY_BACKEND does not use multi reg returns for calls with long return types +#if defined(_TARGET_X86_) return varTypeIsLong(gtType); -#elif FEATURE_MULTIREG_RET && (defined(_TARGET_ARM_) && !defined(LEGACY_BACKEND)) - // LEGACY_BACKEND does not use multi reg returns for calls with long return types +#elif FEATURE_MULTIREG_RET && defined(_TARGET_ARM_) return varTypeIsLong(gtType) || (varTypeIsStruct(gtType) && !HasRetBufArg()); #elif FEATURE_MULTIREG_RET return varTypeIsStruct(gtType) && !HasRetBufArg(); @@ -3780,17 +3620,10 @@ struct GenTreeCall final : public GenTree return IsTailPrefixedCall() || IsImplicitTailCall(); } -#ifndef LEGACY_BACKEND bool IsTailCallViaHelper() const { return IsTailCall() && (gtCallMoreFlags & GTF_CALL_M_TAILCALL_VIA_HELPER); } -#else // LEGACY_BACKEND - bool IsTailCallViaHelper() const - { - return true; - } -#endif // LEGACY_BACKEND #if FEATURE_FASTTAILCALL bool IsFastTailCall() const @@ -3969,7 +3802,7 @@ struct GenTreeCmpXchg : public GenTree #endif }; -#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_) +#if defined(_TARGET_ARM_) struct GenTreeMultiRegOp : public GenTreeOp { regNumber gtOtherReg; @@ -4124,7 +3957,7 @@ struct GenTreeMultiRegOp : public GenTreeOp } #endif }; -#endif +#endif // defined(_TARGET_ARM_) struct GenTreeFptrVal : public GenTree { @@ -4147,12 +3980,6 @@ struct GenTreeFptrVal : public GenTree /* gtQmark */ struct GenTreeQmark : public GenTreeOp { -#ifdef LEGACY_BACKEND - // Livesets on entry to then and else subtrees - VARSET_TP gtThenLiveSet; - VARSET_TP gtElseLiveSet; -#endif - // The "Compiler*" argument is not a DEBUGARG here because we use it to keep track of the set of // (possible) QMark nodes. GenTreeQmark(var_types type, GenTree* cond, GenTree* colonOp, class Compiler* comp); @@ -4698,9 +4525,7 @@ struct GenTreeAddrMode : public GenTreeOp unsigned gtScale; // The scale factor -#ifndef LEGACY_BACKEND private: -#endif // TODO-Cleanup: gtOffset should be changed to 'int' to match the getter function and avoid accidental // zero extension to 64 bit. However, this is used by legacy code and initialized, via the offset // parameter of the constructor, by Lowering::TryCreateAddrMode & CodeGenInterface::genCreateAddrMode. @@ -5347,9 +5172,9 @@ struct GenTreePutArgStk : public GenTreeUnOp unsigned gtNumberReferenceSlots; // Number of reference slots. BYTE* gtGcPtrs; // gcPointers -#elif !defined(LEGACY_BACKEND) +#else // !FEATURE_PUT_STRUCT_ARG_STK unsigned getArgSize(); -#endif // !LEGACY_BACKEND +#endif // !FEATURE_PUT_STRUCT_ARG_STK #if defined(DEBUG) || defined(UNIX_X86_ABI) GenTreeCall* gtCall; // the call node to which this argument belongs @@ -5362,7 +5187,7 @@ struct GenTreePutArgStk : public GenTreeUnOp #endif }; -#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_) +#if defined(_TARGET_ARM_) // Represent the struct argument: split value in register(s) and stack struct GenTreePutArgSplit : public GenTreePutArgStk { @@ -5565,7 +5390,7 @@ struct GenTreePutArgSplit : public GenTreePutArgStk } #endif }; -#endif // !LEGACY_BACKEND && _TARGET_ARM_ +#endif // _TARGET_ARM_ // Represents GT_COPY or GT_RELOAD node struct GenTreeCopyOrReload : public GenTreeUnOp @@ -5794,11 +5619,7 @@ struct GenTreeCC final : public GenTree inline bool GenTree::OperIsBlkOp() { - return (((gtOper == GT_ASG) && varTypeIsStruct(gtOp.gtOp1)) -#ifndef LEGACY_BACKEND - || (OperIsBlk() && (AsBlk()->Data() != nullptr)) -#endif - ); + return ((gtOper == GT_ASG) && varTypeIsStruct(gtOp.gtOp1)) || (OperIsBlk() && (AsBlk()->Data() != nullptr)); } inline bool GenTree::OperIsDynBlkOp() @@ -5807,12 +5628,10 @@ inline bool GenTree::OperIsDynBlkOp() { return gtGetOp1()->OperGet() == GT_DYN_BLK; } -#ifndef LEGACY_BACKEND else if (gtOper == GT_STORE_DYN_BLK) { return true; } -#endif return false; } @@ -5822,7 +5641,6 @@ inline bool GenTree::OperIsInitBlkOp() { return false; } -#ifndef LEGACY_BACKEND GenTree* src; if (gtOper == GT_ASG) { @@ -5832,9 +5650,6 @@ inline bool GenTree::OperIsInitBlkOp() { src = AsBlk()->Data()->gtSkipReloadOrCopy(); } -#else // LEGACY_BACKEND - GenTree* src = gtGetOp2(); -#endif // LEGACY_BACKEND return src->OperIsInitVal() || src->OperIsConst(); } @@ -5972,13 +5787,11 @@ inline bool GenTree::IsValidCallArgument() } if (OperIsFieldList()) { -#if defined(LEGACY_BACKEND) || (!FEATURE_MULTIREG_ARGS && !FEATURE_PUT_STRUCT_ARG_STK) - // Not allowed to have a GT_FIELD_LIST for an argument - // unless we have a RyuJIT backend and FEATURE_MULTIREG_ARGS or FEATURE_PUT_STRUCT_ARG_STK +#if !FEATURE_MULTIREG_ARGS && !FEATURE_PUT_STRUCT_ARG_STK return false; -#else // we have RyuJIT backend and FEATURE_MULTIREG_ARGS or FEATURE_PUT_STRUCT_ARG_STK +#else // FEATURE_MULTIREG_ARGS or FEATURE_PUT_STRUCT_ARG_STK #ifdef UNIX_AMD64_ABI // For UNIX ABI we currently only allow a GT_FIELD_LIST of GT_LCL_FLDs nodes @@ -6006,7 +5819,7 @@ inline bool GenTree::IsValidCallArgument() // We allow this GT_FIELD_LIST as an argument return true; -#endif // FEATURE_MULTIREG_ARGS +#endif // FEATURE_MULTIREG_ARGS or FEATURE_PUT_STRUCT_ARG_STK } // We don't have either kind of list, so it satisfies the invariant. return true; @@ -6053,21 +5866,6 @@ inline bool GenTree::RequiresNonNullOp2(genTreeOps oper) case GT_ROR: case GT_INDEX: case GT_ASG: -#ifdef LEGACY_BACKEND - case GT_ASG_ADD: - case GT_ASG_SUB: - case GT_ASG_MUL: - case GT_ASG_DIV: - case GT_ASG_MOD: - case GT_ASG_UDIV: - case GT_ASG_UMOD: - case GT_ASG_OR: - case GT_ASG_XOR: - case GT_ASG_AND: - case GT_ASG_LSH: - case GT_ASG_RSH: - case GT_ASG_RSZ: -#endif case GT_EQ: case GT_NE: case GT_LT: @@ -6204,7 +6002,7 @@ inline bool GenTree::IsMultiRegNode() const return true; } -#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_) +#if defined(_TARGET_ARM_) if (OperIsMultiRegOp() || OperIsPutArgSplit() || (gtOper == GT_COPY)) { return true; @@ -6323,13 +6121,11 @@ inline bool GenTreeBlk::HasGCPtr() inline bool GenTree::isUsedFromSpillTemp() const { -#if !defined(LEGACY_BACKEND) // If spilled and no reg at use, then it is used from the spill temp location rather than being reloaded. if (((gtFlags & GTF_SPILLED) != 0) && ((gtFlags & GTF_NOREG_AT_USE) != 0)) { return true; } -#endif //! LEGACY_BACKEND return false; } diff --git a/src/jit/gtlist.h b/src/jit/gtlist.h index 13b8fb6e71..dcfe3ba09c 100644 --- a/src/jit/gtlist.h +++ b/src/jit/gtlist.h @@ -50,13 +50,7 @@ GTNODE(NEG , GenTreeOp ,0,GTK_UNOP) GTNODE(COPY , GenTreeCopyOrReload,0,GTK_UNOP) // Copies a variable from its current location to a register that satisfies // code generation constraints. The child is the actual lclVar node. GTNODE(RELOAD , GenTreeCopyOrReload,0,GTK_UNOP) -#ifdef LEGACY_BACKEND -GTNODE(CHS , GenTreeOp ,0,GTK_BINOP|GTK_ASGOP|GTK_NOTLIR) // GT_CHS is actually unary -- op2 is ignored. - // Changing to unary presently causes problems, though -- take a little work to fix. -#endif - GTNODE(ARR_LENGTH , GenTreeArrLen ,0,GTK_UNOP|GTK_EXOP) // array-length - GTNODE(INTRINSIC , GenTreeIntrinsic ,0,GTK_BINOP|GTK_EXOP) // intrinsics GTNODE(LOCKADD , GenTreeOp ,0,GTK_BINOP) @@ -66,7 +60,7 @@ GTNODE(CMPXCHG , GenTreeCmpXchg ,0,GTK_SPECIAL) GTNODE(MEMORYBARRIER , GenTree ,0,GTK_LEAF|GTK_NOVALUE) GTNODE(CAST , GenTreeCast ,0,GTK_UNOP|GTK_EXOP) // conversion to another type -#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_) +#if defined(_TARGET_ARM_) GTNODE(BITCAST , GenTreeMultiRegOp ,0,GTK_UNOP) // reinterpretation of bits as another type #else GTNODE(BITCAST , GenTreeOp ,0,GTK_UNOP) // reinterpretation of bits as another type @@ -134,33 +128,14 @@ GTNODE(MULHI , GenTreeOp ,1,GTK_BINOP) // returns high bits // the div into a MULHI + some adjustments. In codegen, we only use the // results of the high register, and we drop the low results. -#ifndef LEGACY_BACKEND GTNODE(ASG , GenTreeOp ,0,GTK_BINOP|GTK_NOTLIR) -#else -GTNODE(ASG , GenTreeOp ,0,GTK_BINOP|GTK_ASGOP|GTK_NOTLIR) -GTNODE(ASG_ADD , GenTreeOp ,0,GTK_BINOP|GTK_ASGOP|GTK_NOTLIR) -GTNODE(ASG_SUB , GenTreeOp ,0,GTK_BINOP|GTK_ASGOP|GTK_NOTLIR) -GTNODE(ASG_MUL , GenTreeOp ,0,GTK_BINOP|GTK_ASGOP|GTK_NOTLIR) -GTNODE(ASG_DIV , GenTreeOp ,0,GTK_BINOP|GTK_ASGOP|GTK_NOTLIR) -GTNODE(ASG_MOD , GenTreeOp ,0,GTK_BINOP|GTK_ASGOP|GTK_NOTLIR) - -GTNODE(ASG_UDIV , GenTreeOp ,0,GTK_BINOP|GTK_ASGOP|GTK_NOTLIR) -GTNODE(ASG_UMOD , GenTreeOp ,0,GTK_BINOP|GTK_ASGOP|GTK_NOTLIR) - -GTNODE(ASG_OR , GenTreeOp ,0,GTK_BINOP|GTK_ASGOP|GTK_NOTLIR) -GTNODE(ASG_XOR , GenTreeOp ,0,GTK_BINOP|GTK_ASGOP|GTK_NOTLIR) -GTNODE(ASG_AND , GenTreeOp ,0,GTK_BINOP|GTK_ASGOP|GTK_NOTLIR) -GTNODE(ASG_LSH , GenTreeOp ,0,GTK_BINOP|GTK_ASGOP|GTK_NOTLIR) -GTNODE(ASG_RSH , GenTreeOp ,0,GTK_BINOP|GTK_ASGOP|GTK_NOTLIR) -GTNODE(ASG_RSZ , GenTreeOp ,0,GTK_BINOP|GTK_ASGOP|GTK_NOTLIR) -#endif GTNODE(EQ , GenTreeOp ,0,GTK_BINOP|GTK_RELOP) GTNODE(NE , GenTreeOp ,0,GTK_BINOP|GTK_RELOP) GTNODE(LT , GenTreeOp ,0,GTK_BINOP|GTK_RELOP) GTNODE(LE , GenTreeOp ,0,GTK_BINOP|GTK_RELOP) GTNODE(GE , GenTreeOp ,0,GTK_BINOP|GTK_RELOP) GTNODE(GT , GenTreeOp ,0,GTK_BINOP|GTK_RELOP) -#ifndef LEGACY_BACKEND + // These are similar to GT_EQ/GT_NE but they generate "test" instead of "cmp" instructions. // Currently these are generated during lowering for code like ((x & y) eq|ne 0) only on // XArch but ARM could too use these for the same purpose as there is a "tst" instruction. @@ -170,7 +145,6 @@ GTNODE(GT , GenTreeOp ,0,GTK_BINOP|GTK_RELOP) // Because of this there is no need to also add GT_TEST_LT/LE/GE/GT opers. GTNODE(TEST_EQ , GenTreeOp ,0,GTK_BINOP|GTK_RELOP) GTNODE(TEST_NE , GenTreeOp ,0,GTK_BINOP|GTK_RELOP) -#endif GTNODE(COMMA , GenTreeOp ,0,GTK_BINOP|GTK_NOTLIR) @@ -185,7 +159,7 @@ GTNODE(MKREFANY , GenTreeOp ,0,GTK_BINOP) GTNODE(LEA , GenTreeAddrMode ,0,GTK_BINOP|GTK_EXOP) -#if !defined(LEGACY_BACKEND) && !defined(_TARGET_64BIT_) +#if !defined(_TARGET_64BIT_) // A GT_LONG node simply represents the long value produced by the concatenation // of its two (lower and upper half) operands. Some GT_LONG nodes are transient, // during the decomposing of longs; others are handled by codegen as operands of @@ -222,7 +196,7 @@ GTNODE(MUL_LONG , GenTreeMultiRegOp ,1,GTK_BINOP) // RSH_LO represents the lo operation of a 64-bit right shift by a constant int. GTNODE(LSH_HI , GenTreeOp ,0,GTK_BINOP) GTNODE(RSH_LO , GenTreeOp ,0,GTK_BINOP) -#endif // !defined(LEGACY_BACKEND) && !defined(_TARGET_64BIT_) +#endif // !defined(_TARGET_64BIT_) #ifdef FEATURE_SIMD GTNODE(SIMD , GenTreeSIMD ,0,GTK_BINOP|GTK_EXOP) // SIMD functions/operators/intrinsics @@ -298,9 +272,7 @@ GTNODE(PHI_ARG , GenTreePhiArg ,0,GTK_LEAF|GTK_LOCAL) // phi(p // Nodes used by Lower to generate a closer CPU representation of other nodes //----------------------------------------------------------------------------- -#ifndef LEGACY_BACKEND GTNODE(JMPTABLE , GenTreeJumpTable ,0, GTK_LEAF|GTK_NOCONTAIN) // Generates the jump table for switches -#endif GTNODE(SWITCH_TABLE , GenTreeOp ,0, GTK_BINOP|GTK_NOVALUE) // Jump Table based switch construct //----------------------------------------------------------------------------- @@ -316,15 +288,15 @@ GTNODE(PHYSREG , GenTreePhysReg ,0,GTK_LEAF) GTNODE(EMITNOP , GenTree ,0,GTK_LEAF|GTK_NOVALUE) // emitter-placed nop GTNODE(PINVOKE_PROLOG , GenTree ,0,GTK_LEAF|GTK_NOVALUE) // pinvoke prolog seq GTNODE(PINVOKE_EPILOG , GenTree ,0,GTK_LEAF|GTK_NOVALUE) // pinvoke epilog seq -#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_) +#if defined(_TARGET_ARM_) GTNODE(PUTARG_REG , GenTreeMultiRegOp ,0,GTK_UNOP) // operator that places outgoing arg in register #else GTNODE(PUTARG_REG , GenTreeOp ,0,GTK_UNOP) // operator that places outgoing arg in register #endif GTNODE(PUTARG_STK , GenTreePutArgStk ,0,GTK_UNOP|GTK_NOVALUE) // operator that places outgoing arg in stack -#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_) +#if defined(_TARGET_ARM_) GTNODE(PUTARG_SPLIT , GenTreePutArgSplit ,0,GTK_UNOP) // operator that places outgoing arg in registers with stack (split struct in ARM32) -#endif // !LEGACY_BACKEND && _TARGET_ARM_ +#endif // _TARGET_ARM_ GTNODE(RETURNTRAP , GenTreeOp ,0,GTK_UNOP|GTK_NOVALUE) // a conditional call to wait on gc GTNODE(SWAP , GenTreeOp ,0,GTK_BINOP|GTK_NOVALUE) // op1 and op2 swap (registers) GTNODE(IL_OFFSET , GenTreeStmt ,0,GTK_LEAF|GTK_NOVALUE) // marks an IL offset for debugging purposes diff --git a/src/jit/gtstructs.h b/src/jit/gtstructs.h index 0df4e35078..fe67a2fcd9 100644 --- a/src/jit/gtstructs.h +++ b/src/jit/gtstructs.h @@ -56,23 +56,15 @@ GTSTRUCT_2(Val , GT_END_LFIN, GT_JMP) #else GTSTRUCT_1(Val , GT_JMP) #endif -#ifndef LEGACY_BACKEND GTSTRUCT_3_SPECIAL(IntConCommon, GT_CNS_INT, GT_CNS_LNG, GT_JMPTABLE) GTSTRUCT_1(JumpTable , GT_JMPTABLE) -#else // LEGACY_BACKEND -GTSTRUCT_2_SPECIAL(IntConCommon, GT_CNS_INT, GT_CNS_LNG) -#endif// LEGACY_BACKEND GTSTRUCT_1(IntCon , GT_CNS_INT) GTSTRUCT_1(LngCon , GT_CNS_LNG) GTSTRUCT_1(DblCon , GT_CNS_DBL) GTSTRUCT_1(StrCon , GT_CNS_STR) GTSTRUCT_N(LclVarCommon, GT_LCL_VAR, GT_LCL_FLD, GT_REG_VAR, GT_PHI_ARG, GT_STORE_LCL_VAR, GT_STORE_LCL_FLD, GT_LCL_VAR_ADDR, GT_LCL_FLD_ADDR) GTSTRUCT_3(LclVar , GT_LCL_VAR, GT_LCL_VAR_ADDR, GT_STORE_LCL_VAR) -#ifndef LEGACY_BACKEND GTSTRUCT_3(LclFld , GT_LCL_FLD, GT_STORE_LCL_FLD, GT_LCL_FLD_ADDR) -#else // LEGACY_BACKEND -GTSTRUCT_1(LclFld , GT_LCL_FLD) -#endif // LEGACY_BACKEND GTSTRUCT_1(RegVar , GT_REG_VAR) GTSTRUCT_1(Cast , GT_CAST) GTSTRUCT_1(Box , GT_BOX) @@ -113,12 +105,12 @@ GTSTRUCT_1(Qmark , GT_QMARK) GTSTRUCT_1(PhiArg , GT_PHI_ARG) GTSTRUCT_1(StoreInd , GT_STOREIND) GTSTRUCT_N(Indir , GT_STOREIND, GT_IND, GT_NULLCHECK, GT_BLK, GT_STORE_BLK, GT_OBJ, GT_STORE_OBJ, GT_DYN_BLK, GT_STORE_DYN_BLK) -#if defined(LEGACY_BACKEND) || !defined(_TARGET_ARM_) -GTSTRUCT_1(PutArgStk , GT_PUTARG_STK) -#else // defined(_TARGET_ARM_) && !defined(LEGACY_BACKEND) +#if defined(_TARGET_ARM_) GTSTRUCT_2_SPECIAL(PutArgStk, GT_PUTARG_STK, GT_PUTARG_SPLIT) GTSTRUCT_1(PutArgSplit , GT_PUTARG_SPLIT) -#endif // defined(LEGACY_BACKEND) || !defined(_TARGET_ARM_) +#else // !defined(_TARGET_ARM_) +GTSTRUCT_1(PutArgStk , GT_PUTARG_STK) +#endif // !defined(_TARGET_ARM_) GTSTRUCT_1(PhysReg , GT_PHYSREG) #ifdef FEATURE_SIMD GTSTRUCT_1(SIMD , GT_SIMD) @@ -129,7 +121,7 @@ GTSTRUCT_1(HWIntrinsic , GT_HWIntrinsic) GTSTRUCT_1(AllocObj , GT_ALLOCOBJ) GTSTRUCT_1(RuntimeLookup, GT_RUNTIMELOOKUP) GTSTRUCT_2(CC , GT_JCC, GT_SETCC) -#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_) +#if defined(_TARGET_ARM_) GTSTRUCT_3(MultiRegOp , GT_MUL_LONG, GT_PUTARG_REG, GT_BITCAST) #endif /*****************************************************************************/ diff --git a/src/jit/importer.cpp b/src/jit/importer.cpp index 262bc6ee1b..3e705a8ae1 100644 --- a/src/jit/importer.cpp +++ b/src/jit/importer.cpp @@ -1111,13 +1111,10 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, if (destAddr->OperGet() == GT_ADDR) { GenTree* destNode = destAddr->gtGetOp1(); - // If the actual destination is a local (for non-LEGACY_BACKEND), or already a block node, or is a node that + // If the actual destination is a local, or already a block node, or is a node that // will be morphed, don't insert an OBJ(ADDR). - if (destNode->gtOper == GT_INDEX || destNode->OperIsBlk() -#ifndef LEGACY_BACKEND - || ((destNode->OperGet() == GT_LCL_VAR) && (destNode->TypeGet() == src->TypeGet())) -#endif // !LEGACY_BACKEND - ) + if (destNode->gtOper == GT_INDEX || destNode->OperIsBlk() || + ((destNode->OperGet() == GT_LCL_VAR) && (destNode->TypeGet() == src->TypeGet()))) { dest = destNode; } @@ -1319,13 +1316,6 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, { asgType = impNormStructType(structHnd); src->gtType = asgType; -#ifdef LEGACY_BACKEND - if (asgType == TYP_STRUCT) - { - GenTree* srcAddr = gtNewOperNode(GT_ADDR, TYP_BYREF, src); - src = gtNewOperNode(GT_IND, TYP_STRUCT, srcAddr); - } -#endif } if (dest == nullptr) { @@ -2916,12 +2906,10 @@ GenTree* Compiler::impImplicitIorI4Cast(GenTree* tree, var_types dstTyp) GenTree* Compiler::impImplicitR4orR8Cast(GenTree* tree, var_types dstTyp) { -#ifndef LEGACY_BACKEND if (varTypeIsFloating(tree) && varTypeIsFloating(dstTyp) && (dstTyp != tree->gtType)) { tree = gtNewCastNode(dstTyp, tree, false, dstTyp); } -#endif // !LEGACY_BACKEND return tree; } @@ -3618,7 +3606,6 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, // Call the regular function. break; -#ifndef LEGACY_BACKEND case CORINFO_INTRINSIC_Object_GetType: { JITDUMP("\n impIntrinsic: call to Object.GetType\n"); @@ -3715,7 +3702,6 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, break; } -#endif // Implement ByReference Ctor. This wraps the assignment of the ref into a byref-like field // in a value type. The canonical example of this is Span. In effect this is just a // substitution. The parameter byref will be assigned into the newly allocated object. @@ -3981,9 +3967,7 @@ GenTree* Compiler::impMathIntrinsic(CORINFO_METHOD_HANDLE method, op1 = nullptr; -#if defined(LEGACY_BACKEND) - if (IsTargetIntrinsic(intrinsicID)) -#elif !defined(_TARGET_X86_) +#if !defined(_TARGET_X86_) // Intrinsics that are not implemented directly by target instructions will // be re-materialized as users calls in rationalizer. For prefixed tail calls, // don't do this optimization, because @@ -4003,14 +3987,6 @@ GenTree* Compiler::impMathIntrinsic(CORINFO_METHOD_HANDLE method, case 1: op1 = impPopStack().val; -#if FEATURE_X87_DOUBLES - - // X87 stack doesn't differentiate between float/double - // so it doesn't need a cast, but everybody else does - // Just double check it is at least a FP type - noway_assert(varTypeIsFloating(op1)); - -#else // FEATURE_X87_DOUBLES assert(varTypeIsFloating(op1)); if (op1->TypeGet() != callType) @@ -4018,8 +3994,6 @@ GenTree* Compiler::impMathIntrinsic(CORINFO_METHOD_HANDLE method, op1 = gtNewCastNode(callType, op1, false, callType); } -#endif // FEATURE_X87_DOUBLES - op1 = new (this, GT_INTRINSIC) GenTreeIntrinsic(genActualType(callType), op1, intrinsicID, method); break; @@ -4027,15 +4001,6 @@ GenTree* Compiler::impMathIntrinsic(CORINFO_METHOD_HANDLE method, op2 = impPopStack().val; op1 = impPopStack().val; -#if FEATURE_X87_DOUBLES - - // X87 stack doesn't differentiate between float/double - // so it doesn't need a cast, but everybody else does - // Just double check it is at least a FP type - noway_assert(varTypeIsFloating(op2)); - noway_assert(varTypeIsFloating(op1)); - -#else // FEATURE_X87_DOUBLES assert(varTypeIsFloating(op1)); assert(varTypeIsFloating(op2)); @@ -4048,8 +4013,6 @@ GenTree* Compiler::impMathIntrinsic(CORINFO_METHOD_HANDLE method, op1 = gtNewCastNode(callType, op1, false, callType); } -#endif // FEATURE_X87_DOUBLES - op1 = new (this, GT_INTRINSIC) GenTreeIntrinsic(genActualType(callType), op1, op2, intrinsicID, method); break; @@ -4057,12 +4020,10 @@ GenTree* Compiler::impMathIntrinsic(CORINFO_METHOD_HANDLE method, NO_WAY("Unsupported number of args for Math Instrinsic"); } -#ifndef LEGACY_BACKEND if (IsIntrinsicImplementedByUserCall(intrinsicID)) { op1->gtFlags |= GTF_CALL; } -#endif } return op1; @@ -6403,13 +6364,9 @@ GenTree* Compiler::impImportStaticReadOnlyField(void* fldAddr, var_types lclTyp) break; case TYP_FLOAT: - dval = *((float*)fldAddr); - op1 = gtNewDconNode(dval); -#if !FEATURE_X87_DOUBLES - // X87 stack doesn't differentiate between float/double - // so R4 is treated as R8, but everybody else does + dval = *((float*)fldAddr); + op1 = gtNewDconNode(dval); op1->gtType = TYP_FLOAT; -#endif // FEATURE_X87_DOUBLES break; case TYP_DOUBLE: @@ -9783,15 +9740,6 @@ var_types Compiler::impGetByRefResultType(genTreeOps oper, bool fUnsigned, GenTr type = genActualType(op1->gtType); -#if FEATURE_X87_DOUBLES - - // For x87, since we only have 1 size of registers, prefer double - // For everybody else, be more precise - if (type == TYP_FLOAT) - type = TYP_DOUBLE; - -#else // !FEATURE_X87_DOUBLES - // If both operands are TYP_FLOAT, then leave it as TYP_FLOAT. // Otherwise, turn floats into doubles if ((type == TYP_FLOAT) && (genActualType(op2->gtType) != TYP_FLOAT)) @@ -9799,16 +9747,9 @@ var_types Compiler::impGetByRefResultType(genTreeOps oper, bool fUnsigned, GenTr assert(genActualType(op2->gtType) == TYP_DOUBLE); type = TYP_DOUBLE; } - -#endif // FEATURE_X87_DOUBLES } -#if FEATURE_X87_DOUBLES - assert(type == TYP_BYREF || type == TYP_DOUBLE || type == TYP_LONG || type == TYP_INT); -#else // FEATURE_X87_DOUBLES assert(type == TYP_BYREF || type == TYP_DOUBLE || type == TYP_FLOAT || type == TYP_LONG || type == TYP_INT); -#endif // FEATURE_X87_DOUBLES - return type; } @@ -10503,11 +10444,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) JITDUMP(" %#.17g", cval.dblVal); { GenTree* cnsOp = gtNewDconNode(cval.dblVal); -#if !FEATURE_X87_DOUBLES - // X87 stack doesn't differentiate between float/double - // so R4 is treated as R8, but everybody else does - cnsOp->gtType = TYP_FLOAT; -#endif // FEATURE_X87_DOUBLES + cnsOp->gtType = TYP_FLOAT; impPushOnStack(cnsOp, typeInfo(TI_DOUBLE)); } break; @@ -10797,7 +10734,6 @@ void Compiler::impImportBlockCode(BasicBlock* block) impSpillLclRefs(lclNum); -#if !FEATURE_X87_DOUBLES // We can generate an assignment to a TYP_FLOAT from a TYP_DOUBLE // We insert a cast to the dest 'op2' type // @@ -10806,7 +10742,6 @@ void Compiler::impImportBlockCode(BasicBlock* block) { op1 = gtNewCastNode(op2->TypeGet(), op1, false, op2->TypeGet()); } -#endif // !FEATURE_X87_DOUBLES if (varTypeIsStruct(lclTyp)) { @@ -11733,7 +11668,6 @@ void Compiler::impImportBlockCode(BasicBlock* block) } } -#if !FEATURE_X87_DOUBLES // We can generate a TYP_FLOAT operation that has a TYP_DOUBLE operand // if (varTypeIsFloating(type) && varTypeIsFloating(op1->gtType) && varTypeIsFloating(op2->gtType)) @@ -11749,7 +11683,6 @@ void Compiler::impImportBlockCode(BasicBlock* block) op2 = gtNewCastNode(type, op2, false, type); } } -#endif // !FEATURE_X87_DOUBLES #if SMALL_TREE_NODES if (callNode) @@ -12176,7 +12109,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) #endif break; } -#if !FEATURE_X87_DOUBLES + // We can generate an compare of different sized floating point op1 and op2 // We insert a cast // @@ -12201,7 +12134,6 @@ void Compiler::impImportBlockCode(BasicBlock* block) } } } -#endif // !FEATURE_X87_DOUBLES /* Create and append the operator */ @@ -14202,7 +14134,6 @@ void Compiler::impImportBlockCode(BasicBlock* block) } #endif -#if !FEATURE_X87_DOUBLES // We can generate an assignment to a TYP_FLOAT from a TYP_DOUBLE // We insert a cast to the dest 'op1' type // @@ -14211,7 +14142,6 @@ void Compiler::impImportBlockCode(BasicBlock* block) { op2 = gtNewCastNode(op1->TypeGet(), op2, false, op1->TypeGet()); } -#endif // !FEATURE_X87_DOUBLES op1 = gtNewAssignNode(op1, op2); @@ -16701,19 +16631,6 @@ SPILLSTACK: } #endif // _TARGET_64BIT_ -#if FEATURE_X87_DOUBLES - // X87 stack doesn't differentiate between float/double - // so promoting is no big deal. - // For everybody else keep it as float until we have a collision and then promote - // Just like for x64's TYP_INT<->TYP_I_IMPL - - if (multRef > 1 && tree->gtType == TYP_FLOAT) - { - verCurrentState.esStack[level].val = gtNewCastNode(TYP_DOUBLE, tree, TYP_DOUBLE); - } - -#else // !FEATURE_X87_DOUBLES - if (tree->gtType == TYP_DOUBLE && lvaTable[tempNum].lvType == TYP_FLOAT) { // Some other block in the spill clique set this to "float", but now we have "double". @@ -16728,8 +16645,6 @@ SPILLSTACK: verCurrentState.esStack[level].val = gtNewCastNode(TYP_DOUBLE, tree, false, TYP_DOUBLE); } -#endif // FEATURE_X87_DOUBLES - /* If addStmt has a reference to tempNum (can only happen if we are spilling to the temps already used by a previous block), we need to spill addStmt */ @@ -19173,7 +19088,7 @@ void Compiler::impMarkInlineCandidate(GenTree* callNode, bool Compiler::IsTargetIntrinsic(CorInfoIntrinsics intrinsicId) { -#if defined(_TARGET_AMD64_) || (defined(_TARGET_X86_) && !defined(LEGACY_BACKEND)) +#if defined(_TARGET_XARCH_) switch (intrinsicId) { // AMD64/x86 has SSE2 instructions to directly compute sqrt/abs and SSE4.1 @@ -19218,19 +19133,6 @@ bool Compiler::IsTargetIntrinsic(CorInfoIntrinsics intrinsicId) case CORINFO_INTRINSIC_Round: return true; - default: - return false; - } -#elif defined(_TARGET_X86_) - switch (intrinsicId) - { - case CORINFO_INTRINSIC_Sin: - case CORINFO_INTRINSIC_Cos: - case CORINFO_INTRINSIC_Sqrt: - case CORINFO_INTRINSIC_Abs: - case CORINFO_INTRINSIC_Round: - return true; - default: return false; } @@ -19239,7 +19141,7 @@ bool Compiler::IsTargetIntrinsic(CorInfoIntrinsics intrinsicId) // The reason for returning true is that on all other arch the only intrinsic // enabled are target intrinsics. return true; -#endif //_TARGET_AMD64_ +#endif } /******************************************************************************/ diff --git a/src/jit/inline.h b/src/jit/inline.h index eba9b8d52c..cedf36d0ce 100644 --- a/src/jit/inline.h +++ b/src/jit/inline.h @@ -77,13 +77,8 @@ // Implementation limits -#ifndef LEGACY_BACKEND const unsigned int MAX_INL_ARGS = 32; // does not include obj pointer const unsigned int MAX_INL_LCLS = 32; -#else // LEGACY_BACKEND -const unsigned int MAX_INL_ARGS = 10; // does not include obj pointer -const unsigned int MAX_INL_LCLS = 8; -#endif // LEGACY_BACKEND // Forward declarations diff --git a/src/jit/instr.cpp b/src/jit/instr.cpp index 1500dece90..f44074445a 100644 --- a/src/jit/instr.cpp +++ b/src/jit/instr.cpp @@ -467,70 +467,6 @@ void CodeGen::inst_IV_handle(instruction ins, int val) getEmitter()->emitIns_I(ins, EA_HANDLE_CNS_RELOC, val); } -#if FEATURE_STACK_FP_X87 -/***************************************************************************** - * - * Generate a "op ST(n), ST(0)" instruction. - */ - -void CodeGen::inst_FS(instruction ins, unsigned stk) -{ - assert(stk < 8); - -#ifdef DEBUG - - switch (ins) - { - case INS_fcompp: - assert(stk == 1); - break; // Implicit operand of compp is ST(1) - case INS_fld: - case INS_fxch: - assert(!"don't do this. Do you want to use inst_FN() instead?"); - break; - default: - break; - } - -#endif - - getEmitter()->emitIns_F_F0(ins, stk); -} - -/***************************************************************************** - * - * Generate a "op ST(0), ST(n)" instruction - */ - -void CodeGenInterface::inst_FN(instruction ins, unsigned stk) -{ - assert(stk < 8); - -#ifdef DEBUG - - switch (ins) - { - case INS_fst: - case INS_fstp: - case INS_faddp: - case INS_fsubp: - case INS_fsubrp: - case INS_fmulp: - case INS_fdivp: - case INS_fdivrp: - case INS_fcompp: - assert(!"don't do this. Do you want to use inst_FS() instead?"); - break; - default: - break; - } - -#endif // DEBUG - - getEmitter()->emitIns_F0_F(ins, stk); -} -#endif // FEATURE_STACK_FP_X87 - /***************************************************************************** * * Display a stack frame reference. @@ -570,14 +506,8 @@ void CodeGen::inst_RV_IV( } else { -#ifndef LEGACY_BACKEND // TODO-Cleanup: Add a comment about why this is unreached() for RyuJIT backend. unreached(); -#else // LEGACY_BACKEND - regNumber tmpReg = regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(reg)); - instGen_Set_Reg_To_Imm(size, tmpReg, val); - getEmitter()->emitIns_R_R(ins, size, reg, tmpReg, flags); -#endif // LEGACY_BACKEND } #elif defined(_TARGET_ARM64_) // TODO-Arm64-Bug: handle large constants! @@ -597,22 +527,7 @@ void CodeGen::inst_RV_IV( } else if (EA_SIZE(size) == EA_8BYTE && ins != INS_mov && (((int)val != val) || EA_IS_CNS_RELOC(size))) { -#ifndef LEGACY_BACKEND assert(!"Invalid immediate for inst_RV_IV"); -#else // LEGACY_BACKEND - // We can't fit the immediate into this instruction, so move it into - // a register first - regNumber tmpReg = regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(reg)); - instGen_Set_Reg_To_Imm(size, tmpReg, val); - - // We might have to switch back from 3-operand imul to two operand form - if (instrIs3opImul(ins)) - { - assert(getEmitter()->inst3opImulReg(ins) == reg); - ins = INS_imul; - } - getEmitter()->emitIns_R_R(ins, EA_TYPE(size), reg, tmpReg); -#endif // LEGACY_BACKEND } else #endif // _TARGET_AMD64_ @@ -622,1614 +537,217 @@ void CodeGen::inst_RV_IV( #endif // !_TARGET_ARM_ } -#if defined(LEGACY_BACKEND) /***************************************************************************** - * Figure out the operands to address the tree. - * 'addr' can be one of (1) a pointer to be indirected - * (2) a calculation to be done with LEA_AVAILABLE - * (3) GT_ARR_ELEM * - * On return, *baseReg, *indScale, *indReg, and *cns are set. + * Generate an instruction that has one operand given by a tree (which has + * been made addressable). */ -void CodeGen::instGetAddrMode(GenTree* addr, regNumber* baseReg, unsigned* indScale, regNumber* indReg, unsigned* cns) +void CodeGen::inst_TT(instruction ins, GenTree* tree, unsigned offs, int shfv, emitAttr size) { - if (addr->gtOper == GT_ARR_ELEM) - { - /* For GT_ARR_ELEM, the addressibility registers are marked on - gtArrObj and gtArrInds[0] */ - - assert(addr->gtArrElem.gtArrObj->InReg()); - *baseReg = addr->gtArrElem.gtArrObj->gtRegNum; - - assert(addr->gtArrElem.gtArrInds[0]->InReg()); - *indReg = addr->gtArrElem.gtArrInds[0]->gtRegNum; - - if (jitIsScaleIndexMul(addr->gtArrElem.gtArrElemSize)) - *indScale = addr->gtArrElem.gtArrElemSize; - else - *indScale = 0; + bool sizeInferred = false; - *cns = compiler->eeGetMDArrayDataOffset(addr->gtArrElem.gtArrElemType, addr->gtArrElem.gtArrRank); - } - else if (addr->gtOper == GT_LEA) - { - GenTreeAddrMode* lea = addr->AsAddrMode(); - GenTree* base = lea->Base(); - assert(!base || (base->InReg())); - GenTree* index = lea->Index(); - assert(!index || (index->InReg())); - - *baseReg = base ? base->gtRegNum : REG_NA; - *indReg = index ? index->gtRegNum : REG_NA; - *indScale = lea->gtScale; - *cns = lea->gtOffset; - return; - } - else + if (size == EA_UNKNOWN) { - /* Figure out what complex address mode to use */ - - GenTree* rv1 = NULL; - GenTree* rv2 = NULL; - bool rev = false; - - INDEBUG(bool yes =) - genCreateAddrMode(addr, -1, true, RBM_NONE, &rev, &rv1, &rv2, -#if SCALED_ADDR_MODES - indScale, -#endif - cns); - - assert(yes); // // since we have called genMakeAddressable() on addr - // Ensure that the base and index, if used, are in registers. - if (rv1 && ((rv1->gtFlags & GTF_REG_VAL) == 0)) - { - if (rv1->gtFlags & GTF_SPILLED) - { - genRecoverReg(rv1, RBM_ALLINT, RegSet::KEEP_REG); - } - else - { - genCodeForTree(rv1, RBM_NONE); - regSet.rsMarkRegUsed(rv1, addr); - } - assert(rv1->gtFlags & GTF_REG_VAL); - } - if (rv2 && !rv2->InReg()) + sizeInferred = true; + if (instIsFP(ins)) { - if (rv2->gtFlags & GTF_SPILLED) - { - genRecoverReg(rv2, ~genRegMask(rv1->gtRegNum), RegSet::KEEP_REG); - } - else - { - genCodeForTree(rv2, RBM_NONE); - regSet.rsMarkRegUsed(rv2, addr); - } - assert(rv2->InReg()); + size = EA_ATTR(genTypeSize(tree->TypeGet())); } - // If we did both, we might have spilled rv1. - if (rv1 && ((rv1->gtFlags & GTF_SPILLED) != 0)) + else { - regSet.rsLockUsedReg(genRegMask(rv2->gtRegNum)); - genRecoverReg(rv1, ~genRegMask(rv2->gtRegNum), RegSet::KEEP_REG); - regSet.rsUnlockReg(genRegMask(rv2->gtRegNum)); + size = emitTypeSize(tree->TypeGet()); } - - *baseReg = rv1 ? rv1->gtRegNum : REG_NA; - *indReg = rv2 ? rv2->gtRegNum : REG_NA; - } -} - -#if CPU_LOAD_STORE_ARCH -/***************************************************************************** - * - * Originally this was somewhat specific to the x86 instrution format. - * For a Load/Store arch we generate the 1-8 instructions necessary to - * implement the single addressing mode instruction used on x86. - * We currently don't have an instruction scheduler enabled on any target. - * - * [Schedule] an "ins reg, [r/m]" (rdst=true), or "ins [r/m], reg" (rdst=false) - * instruction (the r/m operand given by a tree). We also allow instructions - * of the form "ins [r/m], icon", these are signaled by setting 'cons' to - * true. - * - * The longest instruction sequence emitted on the ARM is as follows: - * - * - the "addr" represents an array addressing mode, - * with a baseReg, indReg with a shift and a large offset - * (Note that typically array addressing modes do NOT have a large offset) - * - "ins" is an ALU instruction, - * - cons=true, and imm is a large constant that can not be directly encoded with "ins" - * - We may need to grab upto four additional registers: regT, rtegVal, regOffs and regImm - * - * add regT, baseReg, indReg<emitInsIsLoad(ins)) - { - insType = eIT_Load; - } - else if (getEmitter()->emitInsIsStore(ins)) - { - insType = eIT_Store; } - regNumber baseReg = REG_NA; - regNumber indReg = REG_NA; - unsigned indScale = 0; +AGAIN: - regMaskTP avoidMask = RBM_NONE; + /* Is this a spilled value? */ - if (addr->InReg()) + if (tree->gtFlags & GTF_SPILLED) { - /* The address is "[reg+offs]" */ - baseReg = addr->gtRegNum; + assert(!"ISSUE: If this can happen, we need to generate 'ins [ebp+spill]'"); } - else if (addr->IsCnsIntOrI()) - { - // Do we need relocations? - if (compiler->opts.compReloc && addr->IsIconHandle()) - { - size = EA_SET_FLG(size, EA_DSP_RELOC_FLG); - // offs should be smaller than ZapperModule::FixupPlaceHolder - // so that we can uniquely identify the handle - assert(offs <= 4); - } - ssize_t disp = addr->gtIntCon.gtIconVal + offs; - if ((insType == eIT_Store) && (ireg != REG_NA)) - { - // Can't use the ireg as the baseReg when we have a store instruction - avoidMask |= genRegMask(ireg); - } - baseReg = regSet.rsPickFreeReg(RBM_ALLINT & ~avoidMask); - avoidMask |= genRegMask(baseReg); - instGen_Set_Reg_To_Imm(size, baseReg, disp); - offs = 0; - } - else + switch (tree->gtOper) { - unsigned cns = 0; - - instGetAddrMode(addr, &baseReg, &indScale, &indReg, &cns); - - /* Add the constant offset value, if present */ - - offs += cns; - -#if SCALED_ADDR_MODES - noway_assert((baseReg != REG_NA) || (indReg != REG_NA)); - if (baseReg != REG_NA) -#endif - { - avoidMask |= genRegMask(baseReg); - } - - // I don't think this is necessary even in the non-proto-jit case, but better to be - // conservative here. It is only necessary to avoid using ireg if it is used as regT, - // in which case it will be added to avoidMask below. - - if (ireg != REG_NA) - { - avoidMask |= genRegMask(ireg); - } - - if (indReg != REG_NA) - { - avoidMask |= genRegMask(indReg); - } - } - - unsigned shift = (indScale > 0) ? genLog2((unsigned)indScale) : 0; - - regNumber regT = REG_NA; // the register where the address is computed into - regNumber regOffs = REG_NA; // a temporary register to use for the offs when it can't be directly encoded - regNumber regImm = REG_NA; // a temporary register to use for the imm when it can't be directly encoded - regNumber regVal = REG_NA; // a temporary register to use when we have to do a load/modify/store operation + unsigned varNum; - // Setup regT - if (indReg == REG_NA) - { - regT = baseReg; // We can use the baseReg, regT is read-only - } - else // We have an index register (indReg != REG_NA) - { - // Check for special case that we can encode using one instruction - if ((offs == 0) && (insType != eIT_Other) && !instIsFP(ins) && baseReg != REG_NA) - { - // ins ireg, [baseReg + indReg << shift] - getEmitter()->emitIns_R_R_R_I(ins, size, ireg, baseReg, indReg, shift, flags, INS_OPTS_LSL); - return; - } + case GT_LCL_VAR: - // Otherwise setup regT, regT is written once here - // - if (insType == eIT_Lea || (insType == eIT_Load && !instIsFP(ins))) - { - assert(ireg != REG_NA); - // ireg will be written, so we can take it as our temporary register - regT = ireg; - } - else - { - // need a new temporary reg - regT = regSet.rsPickFreeReg(RBM_ALLINT & ~avoidMask); - regTracker.rsTrackRegTrash(regT); - } + inst_set_SV_var(tree); + goto LCL; -#if SCALED_ADDR_MODES - if (baseReg == REG_NA) - { - assert(shift > 0); - // LSL regT, indReg, shift. - getEmitter()->emitIns_R_R_I(INS_lsl, EA_PTRSIZE, regT, indReg, shift & ((TARGET_POINTER_SIZE * 8) - 1)); - } - else -#endif // SCALED_ADDR_MODES - { - assert(baseReg != REG_NA); - - // add regT, baseReg, indReg<emitIns_R_R_R_I(INS_add, - // The "add" operation will yield either a pointer or byref, depending on the - // type of "addr." - varTypeIsGC(addr->TypeGet()) ? EA_BYREF : EA_PTRSIZE, regT, baseReg, indReg, - shift, INS_FLAGS_NOT_SET, INS_OPTS_LSL); - } - } + case GT_LCL_FLD: - // regT is the base register for a load/store or an operand for add when insType is eIT_Lea - // - assert(regT != REG_NA); - avoidMask |= genRegMask(regT); + offs += tree->gtLclFld.gtLclOffs; + goto LCL; - if (insType != eIT_Other) - { - assert((flags != INS_FLAGS_SET) || (insType == eIT_Lea)); - if ((insType == eIT_Lea) && (offs == 0)) - { - // If we have the same register as src and dst and we do not need to set the flags - // then we can skip emitting the instruction - if ((ireg != regT) || (flags == INS_FLAGS_SET)) - { - // mov ireg, regT - getEmitter()->emitIns_R_R(INS_mov, size, ireg, regT, flags); - } - } - else if (arm_Valid_Imm_For_Instr(ins, offs, flags)) - { - // ins ireg, [regT + offs] - getEmitter()->emitIns_R_R_I(ins, size, ireg, regT, offs, flags); - } - else - { - regOffs = regSet.rsPickFreeReg(RBM_ALLINT & ~avoidMask); + LCL: + varNum = tree->gtLclVarCommon.gtLclNum; + assert(varNum < compiler->lvaCount); - // We cannot use [regT + regOffs] to load/store a floating register - if (emitter::isFloatReg(ireg)) + if (shfv) { - if (arm_Valid_Imm_For_Instr(INS_add, offs, flags)) - { - // add regOffs, regT, offs - getEmitter()->emitIns_R_R_I(INS_add, EA_4BYTE, regOffs, regT, offs, flags); - } - else - { - // movw regOffs, offs_lo16 - // movt regOffs, offs_hi16 - // add regOffs, regOffs, regT - instGen_Set_Reg_To_Imm(EA_4BYTE, regOffs, offs); - getEmitter()->emitIns_R_R_R(INS_add, EA_4BYTE, regOffs, regOffs, regT, flags); - } - // ins ireg, [regOffs] - getEmitter()->emitIns_R_R_I(ins, size, ireg, regOffs, 0, flags); - - regTracker.rsTrackRegTrash(regOffs); + getEmitter()->emitIns_S_I(ins, size, varNum, offs, shfv); } else { - // mov regOffs, offs - // ins ireg, [regT + regOffs] - instGen_Set_Reg_To_Imm(EA_4BYTE, regOffs, offs); - getEmitter()->emitIns_R_R_R(ins, size, ireg, regT, regOffs, flags); + getEmitter()->emitIns_S(ins, size, varNum, offs); } - } - } - else // (insType == eIT_Other); - { - // Setup regVal - // - regVal = regSet.rsPickFreeReg(RBM_ALLINT & ~avoidMask); - regTracker.rsTrackRegTrash(regVal); - avoidMask |= genRegMask(regVal); - var_types load_store_type; - switch (size) - { - case EA_4BYTE: - load_store_type = TYP_INT; - break; - - case EA_2BYTE: - load_store_type = TYP_SHORT; - break; - - case EA_1BYTE: - load_store_type = TYP_BYTE; - break; - - default: - assert(!"Unexpected size in sched_AM, eIT_Other"); - load_store_type = TYP_INT; - break; - } + return; - // Load the content at addr into regVal using regT + offs - if (arm_Valid_Disp_For_LdSt(offs, load_store_type)) - { - // ldrX regVal, [regT + offs] - getEmitter()->emitIns_R_R_I(ins_Load(load_store_type), size, regVal, regT, offs); - } - else - { - // mov regOffs, offs - // ldrX regVal, [regT + regOffs] - regOffs = regSet.rsPickFreeReg(RBM_ALLINT & ~avoidMask); - avoidMask |= genRegMask(regOffs); - instGen_Set_Reg_To_Imm(EA_4BYTE, regOffs, offs); - getEmitter()->emitIns_R_R_R(ins_Load(load_store_type), size, regVal, regT, regOffs); - } + case GT_CLS_VAR: + // Make sure FP instruction size matches the operand size + // (We optimized constant doubles to floats when we can, just want to + // make sure that we don't mistakenly use 8 bytes when the + // constant. + assert(!isFloatRegType(tree->gtType) || genTypeSize(tree->gtType) == EA_SIZE_IN_BYTES(size)); - if (cons) - { - if (arm_Valid_Imm_For_Instr(ins, imm, flags)) + if (shfv) { - getEmitter()->emitIns_R_I(ins, size, regVal, imm, flags); + getEmitter()->emitIns_C_I(ins, size, tree->gtClsVar.gtClsVarHnd, offs, shfv); } else { - assert(regOffs == REG_NA); - regImm = regSet.rsPickFreeReg(RBM_ALLINT & ~avoidMask); - avoidMask |= genRegMask(regImm); - instGen_Set_Reg_To_Imm(size, regImm, imm); - getEmitter()->emitIns_R_R(ins, size, regVal, regImm, flags); + getEmitter()->emitIns_C(ins, size, tree->gtClsVar.gtClsVarHnd, offs); } - } - else if (rdst) - { - getEmitter()->emitIns_R_R(ins, size, ireg, regVal, flags); - } - else + return; + + case GT_IND: + case GT_NULLCHECK: + case GT_ARR_ELEM: { - getEmitter()->emitIns_R_R(ins, size, regVal, ireg, flags); + assert(!"inst_TT not supported for GT_IND, GT_NULLCHECK or GT_ARR_ELEM"); } + break; - // If we do not have a register destination we must perform the write-back store instruction - // (unless we have an instruction like INS_cmp that does not write a destination) - // - if (!rdst && ins_Writes_Dest(ins)) - { - // Store regVal into [addr] - if (regOffs == REG_NA) - { - // strX regVal, [regT + offs] - getEmitter()->emitIns_R_R_I(ins_Store(load_store_type), size, regVal, regT, offs); - } +#ifdef _TARGET_X86_ + case GT_CNS_INT: + // We will get here for GT_MKREFANY from CodeGen::genPushArgList + assert(offs == 0); + assert(!shfv); + if (tree->IsIconHandle()) + inst_IV_handle(ins, tree->gtIntCon.gtIconVal); else - { - // strX regVal, [regT + regOffs] - getEmitter()->emitIns_R_R_R(ins_Store(load_store_type), size, regVal, regT, regOffs); - } - } + inst_IV(ins, tree->gtIntCon.gtIconVal); + break; +#endif + + case GT_COMMA: + // tree->gtOp.gtOp1 - already processed by genCreateAddrMode() + tree = tree->gtOp.gtOp2; + goto AGAIN; + + default: + assert(!"invalid address"); } } -#else // !CPU_LOAD_STORE_ARCH - /***************************************************************************** * - * This is somewhat specific to the x86 instrution format. - * We currently don't have an instruction scheduler enabled on any target. - * - * [Schedule] an "ins reg, [r/m]" (rdst=true), or "ins [r/m], reg" (rdst=false) - * instruction (the r/m operand given by a tree). We also allow instructions - * of the form "ins [r/m], icon", these are signalled by setting 'cons' to - * true. + * Generate an instruction that has one operand given by a tree (which has + * been made addressable) and another that is a register. */ -void CodeGen::sched_AM(instruction ins, - emitAttr size, - regNumber ireg, - bool rdst, - GenTree* addr, - unsigned offs, - bool cons, - int imm, - insFlags flags) +void CodeGen::inst_TT_RV(instruction ins, GenTree* tree, regNumber reg, unsigned offs, emitAttr size, insFlags flags) { -#ifdef _TARGET_XARCH_ - /* Don't use this method for issuing calls. Use instEmit_xxxCall() */ - assert(ins != INS_call); -#endif + assert(reg != REG_STK); - assert(addr); - assert(size != EA_UNKNOWN); +AGAIN: - regNumber reg; + /* Is this a spilled value? */ - /* Has the address been conveniently loaded into a register, - or is it an absolute value ? */ + if (tree->gtFlags & GTF_SPILLED) + { + assert(!"ISSUE: If this can happen, we need to generate 'ins [ebp+spill]'"); + } - if ((addr->InReg()) || (addr->IsCnsIntOrI())) + if (size == EA_UNKNOWN) { - if (addr->InReg()) + if (instIsFP(ins)) { - /* The address is "[reg+offs]" */ - - reg = addr->gtRegNum; - - if (cons) - getEmitter()->emitIns_I_AR(ins, size, imm, reg, offs); - else if (rdst) - getEmitter()->emitIns_R_AR(ins, size, ireg, reg, offs); - else - getEmitter()->emitIns_AR_R(ins, size, ireg, reg, offs); + size = EA_ATTR(genTypeSize(tree->TypeGet())); } else { - /* The address is an absolute value */ - - assert(addr->IsCnsIntOrI()); - - // Do we need relocations? - if (compiler->opts.compReloc && addr->IsIconHandle()) - { - size = EA_SET_FLG(size, EA_DSP_RELOC_FLG); - // offs should be smaller than ZapperModule::FixupPlaceHolder - // so that we can uniquely identify the handle - assert(offs <= 4); - } - - reg = REG_NA; - ssize_t disp = addr->gtIntCon.gtIconVal + offs; - - // Cross our fingers and hope the codegenerator did the right - // thing and the constant address can be RIP-relative - - if (cons) - getEmitter()->emitIns_I_AI(ins, size, imm, disp); - else if (rdst) - getEmitter()->emitIns_R_AI(ins, size, ireg, disp); - else - getEmitter()->emitIns_AI_R(ins, size, ireg, disp); - } - - return; - } - - /* Figure out what complex address mode to use */ - - regNumber baseReg, indReg; - unsigned indScale = 0, cns = 0; - - instGetAddrMode(addr, &baseReg, &indScale, &indReg, &cns); - - /* Add the constant offset value, if present */ - - offs += cns; - - /* Is there an index reg operand? */ - - if (indReg != REG_NA) - { - /* Is the index reg operand scaled? */ - - if (indScale) - { - /* Is there a base address operand? */ - - if (baseReg != REG_NA) - { - reg = baseReg; - - /* The address is "[reg + {2/4/8} * indReg + offs]" */ - - if (cons) - getEmitter()->emitIns_I_ARX(ins, size, imm, reg, indReg, indScale, offs); - else if (rdst) - getEmitter()->emitIns_R_ARX(ins, size, ireg, reg, indReg, indScale, offs); - else - getEmitter()->emitIns_ARX_R(ins, size, ireg, reg, indReg, indScale, offs); - } - else - { - /* The address is "[{2/4/8} * indReg + offs]" */ - - if (cons) - getEmitter()->emitIns_I_AX(ins, size, imm, indReg, indScale, offs); - else if (rdst) - getEmitter()->emitIns_R_AX(ins, size, ireg, indReg, indScale, offs); - else - getEmitter()->emitIns_AX_R(ins, size, ireg, indReg, indScale, offs); - } - } - else - { - assert(baseReg != REG_NA); - reg = baseReg; - - /* The address is "[reg + indReg + offs]" */ - if (cons) - getEmitter()->emitIns_I_ARR(ins, size, imm, reg, indReg, offs); - else if (rdst) - getEmitter()->emitIns_R_ARR(ins, size, ireg, reg, indReg, offs); - else - getEmitter()->emitIns_ARR_R(ins, size, ireg, reg, indReg, offs); - } - } - else - { - unsigned cpx = 0; - CORINFO_CLASS_HANDLE cls = 0; - - /* No second operand: the address is "[reg + icon]" */ - - assert(baseReg != REG_NA); - reg = baseReg; - - if (cons) - { - getEmitter()->emitIns_I_AR(ins, size, imm, reg, offs); - } - else if (rdst) - { - getEmitter()->emitIns_R_AR(ins, size, ireg, reg, offs); - } - else - { - getEmitter()->emitIns_AR_R(ins, size, ireg, reg, offs); - } - } -} - -#endif // !CPU_LOAD_STORE_ARCH - -/***************************************************************************** - * - * Emit a "call [r/m]" instruction (the r/m operand given by a tree). - */ - -// clang-format off -void CodeGen::instEmit_indCall(GenTreeCall* call, - size_t argSize, - emitAttr retSize - MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize)) -// clang-format on -{ - GenTree* addr; - - emitter::EmitCallType emitCallType; - - regNumber brg = REG_NA; - regNumber xrg = REG_NA; - unsigned mul = 0; - unsigned cns = 0; - - CORINFO_SIG_INFO* sigInfo = nullptr; - - /* Get hold of the function address */ - - assert(call->gtCallType == CT_INDIRECT); - addr = call->gtCallAddr; - assert(addr); - -#ifdef DEBUG - // Pass the call signature information from the GenTree node so the emitter can associate - // native call sites with the signatures they were generated from. - sigInfo = call->callSig; -#endif // DEBUG - -#if CPU_LOAD_STORE_ARCH - - emitCallType = emitter::EC_INDIR_R; - - if (!addr->OperIsIndir()) - { - if (!(addr->InReg()) && (addr->OperGet() == GT_CNS_INT)) - { - ssize_t funcPtr = addr->gtIntCon.gtIconVal; - - // clang-format off - getEmitter()->emitIns_Call(emitter::EC_FUNC_ADDR, - NULL, // methHnd - INDEBUG_LDISASM_COMMA(sigInfo) - (void*) funcPtr, - argSize, - retSize - MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize), - gcInfo.gcVarPtrSetCur, - gcInfo.gcRegGCrefSetCur, - gcInfo.gcRegByrefSetCur); - // clang-format on - - return; - } - } - else - { - /* Get hold of the address of the function pointer */ - - addr = addr->gtOp.gtOp1; - } - - if (addr->InReg()) - { - /* The address is "reg" */ - - brg = addr->gtRegNum; - } - else - { - // Force the address into a register - CLANG_FORMAT_COMMENT_ANCHOR; - -#ifdef LEGACY_BACKEND - genCodeForTree(addr, RBM_NONE); -#endif // LEGACY_BACKEND - assert(addr->InReg()); - brg = addr->gtRegNum; - } - -#else // CPU_LOAD_STORE_ARCH - - /* Is there an indirection? */ - - if (!addr->OperIsIndir()) - { - if (addr->InReg()) - { - emitCallType = emitter::EC_INDIR_R; - brg = addr->gtRegNum; - } - else - { - if (addr->OperGet() != GT_CNS_INT) - { - assert(addr->OperGet() == GT_LCL_VAR); - - emitCallType = emitter::EC_INDIR_SR; - cns = addr->gtLclVarCommon.gtLclNum; - } - else - { - ssize_t funcPtr = addr->gtIntCon.gtIconVal; - - // clang-format off - getEmitter()->emitIns_Call(emitter::EC_FUNC_ADDR, - nullptr, // methHnd - INDEBUG_LDISASM_COMMA(sigInfo) - (void*) funcPtr, - argSize, - retSize - MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize), - gcInfo.gcVarPtrSetCur, - gcInfo.gcRegGCrefSetCur, - gcInfo.gcRegByrefSetCur); - // clang-format on - - return; - } - } - } - else - { - /* This is an indirect call */ - - emitCallType = emitter::EC_INDIR_ARD; - - /* Get hold of the address of the function pointer */ - - addr = addr->gtOp.gtOp1; - - /* Has the address been conveniently loaded into a register? */ - - if (addr->InReg()) - { - /* The address is "reg" */ - - brg = addr->gtRegNum; - } - else - { - bool rev = false; - - GenTree* rv1 = nullptr; - GenTree* rv2 = nullptr; - - /* Figure out what complex address mode to use */ - - INDEBUG(bool yes =) - genCreateAddrMode(addr, -1, true, RBM_NONE, &rev, &rv1, &rv2, &mul, &cns); - - INDEBUG(PREFIX_ASSUME(yes)); // since we have called genMakeAddressable() on call->gtCallAddr - - /* Get the additional operands if any */ - - if (rv1) - { - assert(rv1->InReg()); - brg = rv1->gtRegNum; - } - - if (rv2) - { - assert(rv2->InReg()); - xrg = rv2->gtRegNum; - } - } - } - - assert(emitCallType == emitter::EC_INDIR_R || emitCallType == emitter::EC_INDIR_SR || - emitCallType == emitter::EC_INDIR_C || emitCallType == emitter::EC_INDIR_ARD); - -#endif // CPU_LOAD_STORE_ARCH - - // clang-format off - getEmitter()->emitIns_Call(emitCallType, - nullptr, // methHnd - INDEBUG_LDISASM_COMMA(sigInfo) - nullptr, // addr - argSize, - retSize - MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize), - gcInfo.gcVarPtrSetCur, - gcInfo.gcRegGCrefSetCur, - gcInfo.gcRegByrefSetCur, - BAD_IL_OFFSET, // ilOffset - brg, - xrg, - mul, - cns); // addressing mode values - // clang-format on -} - -/***************************************************************************** - * - * Emit an "op [r/m]" instruction (the r/m operand given by a tree). - */ - -void CodeGen::instEmit_RM(instruction ins, GenTree* tree, GenTree* addr, unsigned offs) -{ - emitAttr size; - - if (!instIsFP(ins)) - size = emitTypeSize(tree->TypeGet()); - else - size = EA_ATTR(genTypeSize(tree->TypeGet())); - - sched_AM(ins, size, REG_NA, false, addr, offs); -} - -/***************************************************************************** - * - * Emit an "op [r/m], reg" instruction (the r/m operand given by a tree). - */ - -void CodeGen::instEmit_RM_RV(instruction ins, emitAttr size, GenTree* tree, regNumber reg, unsigned offs) -{ -#ifdef _TARGET_XARCH_ - assert(instIsFP(ins) == 0); -#endif - sched_AM(ins, size, reg, false, tree, offs); -} -#endif // LEGACY_BACKEND - -/***************************************************************************** - * - * Generate an instruction that has one operand given by a tree (which has - * been made addressable). - */ - -void CodeGen::inst_TT(instruction ins, GenTree* tree, unsigned offs, int shfv, emitAttr size) -{ - bool sizeInferred = false; - - if (size == EA_UNKNOWN) - { - sizeInferred = true; - if (instIsFP(ins)) - { - size = EA_ATTR(genTypeSize(tree->TypeGet())); - } - else - { - size = emitTypeSize(tree->TypeGet()); - } - } - -AGAIN: - -#ifdef LEGACY_BACKEND - /* Is the value sitting in a register? */ - - if (tree->InReg()) - { - regNumber reg; - -#ifndef _TARGET_64BIT_ - LONGREG_TT: -#endif - -#if FEATURE_STACK_FP_X87 - - /* Is this a floating-point instruction? */ - - if (isFloatRegType(tree->gtType)) - { - reg = tree->gtRegNum; - - assert(instIsFP(ins) && ins != INS_fst && ins != INS_fstp); - assert(shfv == 0); - - inst_FS(ins, reg + genGetFPstkLevel()); - return; - } -#endif // FEATURE_STACK_FP_X87 - - assert(!instIsFP(ins)); - -#if CPU_LONG_USES_REGPAIR - if (tree->gtType == TYP_LONG) - { - if (offs) - { - assert(offs == sizeof(int)); - reg = genRegPairHi(tree->gtRegPair); - } - else - { - reg = genRegPairLo(tree->gtRegPair); - } - } - else -#endif // CPU_LONG_USES_REGPAIR - { - reg = tree->gtRegNum; - } - - /* Make sure it is not the "stack-half" of an enregistered long */ - - if (reg != REG_STK) - { - // For short types, indicate that the value is promoted to 4 bytes. - // For longs, we are only emitting half of it so again set it to 4 bytes. - // but leave the GC tracking information alone - if (sizeInferred && EA_SIZE(size) < EA_4BYTE) - { - size = EA_SET_SIZE(size, 4); - } - - if (shfv) - { - getEmitter()->emitIns_R_I(ins, size, reg, shfv); - } - else - { - inst_RV(ins, reg, tree->TypeGet(), size); - } - - return; - } - } -#endif // LEGACY_BACKEND - - /* Is this a spilled value? */ - - if (tree->gtFlags & GTF_SPILLED) - { - assert(!"ISSUE: If this can happen, we need to generate 'ins [ebp+spill]'"); - } - - switch (tree->gtOper) - { - unsigned varNum; - - case GT_LCL_VAR: - -#ifdef LEGACY_BACKEND - /* Is this an enregistered long ? */ - - if (tree->gtType == TYP_LONG && !(tree->InReg())) - { - /* Avoid infinite loop */ - - if (genMarkLclVar(tree)) - goto LONGREG_TT; - } -#endif // LEGACY_BACKEND - - inst_set_SV_var(tree); - goto LCL; - - case GT_LCL_FLD: - - offs += tree->gtLclFld.gtLclOffs; - goto LCL; - - LCL: - varNum = tree->gtLclVarCommon.gtLclNum; - assert(varNum < compiler->lvaCount); - - if (shfv) - { - getEmitter()->emitIns_S_I(ins, size, varNum, offs, shfv); - } - else - { - getEmitter()->emitIns_S(ins, size, varNum, offs); - } - - return; - - case GT_CLS_VAR: - // Make sure FP instruction size matches the operand size - // (We optimized constant doubles to floats when we can, just want to - // make sure that we don't mistakenly use 8 bytes when the - // constant. - assert(!isFloatRegType(tree->gtType) || genTypeSize(tree->gtType) == EA_SIZE_IN_BYTES(size)); - - if (shfv) - { - getEmitter()->emitIns_C_I(ins, size, tree->gtClsVar.gtClsVarHnd, offs, shfv); - } - else - { - getEmitter()->emitIns_C(ins, size, tree->gtClsVar.gtClsVarHnd, offs); - } - return; - - case GT_IND: - case GT_NULLCHECK: - case GT_ARR_ELEM: - { -#ifndef LEGACY_BACKEND - assert(!"inst_TT not supported for GT_IND, GT_NULLCHECK or GT_ARR_ELEM in !LEGACY_BACKEND"); -#else // LEGACY_BACKEND - GenTree* addr = tree->OperIsIndir() ? tree->gtOp.gtOp1 : tree; - if (shfv) - sched_AM(ins, size, REG_NA, false, addr, offs, true, shfv); - else - instEmit_RM(ins, tree, addr, offs); -#endif // LEGACY_BACKEND - } - break; - -#ifdef _TARGET_X86_ - case GT_CNS_INT: - // We will get here for GT_MKREFANY from CodeGen::genPushArgList - assert(offs == 0); - assert(!shfv); - if (tree->IsIconHandle()) - inst_IV_handle(ins, tree->gtIntCon.gtIconVal); - else - inst_IV(ins, tree->gtIntCon.gtIconVal); - break; -#endif - - case GT_COMMA: - // tree->gtOp.gtOp1 - already processed by genCreateAddrMode() - tree = tree->gtOp.gtOp2; - goto AGAIN; - - default: - assert(!"invalid address"); - } -} - -/***************************************************************************** - * - * Generate an instruction that has one operand given by a tree (which has - * been made addressable) and another that is a register. - */ - -void CodeGen::inst_TT_RV(instruction ins, GenTree* tree, regNumber reg, unsigned offs, emitAttr size, insFlags flags) -{ - assert(reg != REG_STK); - -AGAIN: - -#ifdef LEGACY_BACKEND - /* Is the value sitting in a register? */ - - if (tree->InReg()) - { - regNumber rg2; - -#ifdef _TARGET_64BIT_ - assert(!instIsFP(ins)); - - rg2 = tree->gtRegNum; - - assert(offs == 0); - assert(rg2 != REG_STK); - - if (ins != INS_mov || rg2 != reg) - { - inst_RV_RV(ins, rg2, reg, tree->TypeGet()); - } - return; - -#else // !_TARGET_64BIT_ - -#ifdef LEGACY_BACKEND - LONGREG_TT_RV: -#endif // LEGACY_BACKEND - -#ifdef _TARGET_XARCH_ - assert(!instIsFP(ins)); -#endif - -#if CPU_LONG_USES_REGPAIR - if (tree->gtType == TYP_LONG) - { - if (offs) - { - assert(offs == sizeof(int)); - rg2 = genRegPairHi(tree->gtRegPair); - } - else - { - rg2 = genRegPairLo(tree->gtRegPair); - } - } - else -#endif // CPU_LONG_USES_REGPAIR - { - rg2 = tree->gtRegNum; - } - - if (rg2 != REG_STK) - { - if (ins != INS_mov || rg2 != reg) - inst_RV_RV(ins, rg2, reg, tree->TypeGet(), size, flags); - return; - } - -#endif // _TARGET_64BIT_ - } -#endif // LEGACY_BACKEND - - /* Is this a spilled value? */ - - if (tree->gtFlags & GTF_SPILLED) - { - assert(!"ISSUE: If this can happen, we need to generate 'ins [ebp+spill]'"); - } - - if (size == EA_UNKNOWN) - { - if (instIsFP(ins)) - { - size = EA_ATTR(genTypeSize(tree->TypeGet())); - } - else - { - size = emitTypeSize(tree->TypeGet()); - } - } - - switch (tree->gtOper) - { - unsigned varNum; - - case GT_LCL_VAR: - -#ifdef LEGACY_BACKEND - if (tree->gtType == TYP_LONG && !(tree->InReg())) - { - /* Avoid infinite loop */ - - if (genMarkLclVar(tree)) - goto LONGREG_TT_RV; - } -#endif // LEGACY_BACKEND - - inst_set_SV_var(tree); - goto LCL; - - case GT_LCL_FLD: - case GT_STORE_LCL_FLD: - offs += tree->gtLclFld.gtLclOffs; - goto LCL; - - LCL: - - varNum = tree->gtLclVarCommon.gtLclNum; - assert(varNum < compiler->lvaCount); - -#if CPU_LOAD_STORE_ARCH - if (!getEmitter()->emitInsIsStore(ins)) - { -#ifndef LEGACY_BACKEND - // TODO-LdStArch-Bug: Should regTmp be a dst on the node or an internal reg? - // Either way, it is not currently being handled by Lowering. - regNumber regTmp = tree->gtRegNum; - assert(regTmp != REG_NA); -#else // LEGACY_BACKEND - regNumber regTmp = regSet.rsPickFreeReg(RBM_ALLINT & ~genRegMask(reg)); -#endif // LEGACY_BACKEND - getEmitter()->emitIns_R_S(ins_Load(tree->TypeGet()), size, regTmp, varNum, offs); - getEmitter()->emitIns_R_R(ins, size, regTmp, reg, flags); - getEmitter()->emitIns_S_R(ins_Store(tree->TypeGet()), size, regTmp, varNum, offs); - - regTracker.rsTrackRegTrash(regTmp); - } - else -#endif - { - // ins is a Store instruction - // - getEmitter()->emitIns_S_R(ins, size, reg, varNum, offs); -#ifdef _TARGET_ARM_ - // If we need to set the flags then add an extra movs reg,reg instruction - if (flags == INS_FLAGS_SET) - getEmitter()->emitIns_R_R(INS_mov, size, reg, reg, INS_FLAGS_SET); -#endif - } - return; - - case GT_CLS_VAR: - // Make sure FP instruction size matches the operand size - // (We optimized constant doubles to floats when we can, just want to - // make sure that we don't mistakenly use 8 bytes when the - // constant). - assert(!isFloatRegType(tree->gtType) || genTypeSize(tree->gtType) == EA_SIZE_IN_BYTES(size)); - -#if CPU_LOAD_STORE_ARCH - if (!getEmitter()->emitInsIsStore(ins)) - { -#ifndef LEGACY_BACKEND - NYI("Store of GT_CLS_VAR not supported for ARM RyuJIT Backend"); -#else // LEGACY_BACKEND - regNumber regTmpAddr = regSet.rsPickFreeReg(RBM_ALLINT & ~genRegMask(reg)); - regNumber regTmpArith = regSet.rsPickFreeReg(RBM_ALLINT & ~genRegMask(reg) & ~genRegMask(regTmpAddr)); - - getEmitter()->emitIns_R_C(INS_lea, EA_PTRSIZE, regTmpAddr, tree->gtClsVar.gtClsVarHnd, offs); - getEmitter()->emitIns_R_R(ins_Load(tree->TypeGet()), size, regTmpArith, regTmpAddr); - getEmitter()->emitIns_R_R(ins, size, regTmpArith, reg, flags); - getEmitter()->emitIns_R_R(ins_Store(tree->TypeGet()), size, regTmpArith, regTmpAddr); - - regTracker.rsTrackRegTrash(regTmpAddr); - regTracker.rsTrackRegTrash(regTmpArith); -#endif // LEGACY_BACKEND - } - else -#endif // CPU_LOAD_STORE_ARCH - { - getEmitter()->emitIns_C_R(ins, size, tree->gtClsVar.gtClsVarHnd, reg, offs); - } - return; - - case GT_IND: - case GT_NULLCHECK: - case GT_ARR_ELEM: - { -#ifndef LEGACY_BACKEND - assert(!"inst_TT_RV not supported for GT_IND, GT_NULLCHECK or GT_ARR_ELEM in RyuJIT Backend"); -#else // LEGACY_BACKEND - GenTree* addr = tree->OperIsIndir() ? tree->gtOp.gtOp1 : tree; - sched_AM(ins, size, reg, false, addr, offs, false, 0, flags); -#endif // LEGACY_BACKEND - } - break; - - case GT_COMMA: - // tree->gtOp.gtOp1 - already processed by genCreateAddrMode() - tree = tree->gtOp.gtOp2; - goto AGAIN; - - default: - assert(!"invalid address"); - } -} - -#ifdef LEGACY_BACKEND -regNumber CodeGen::genGetZeroRegister() -{ - // Is the constant already in some register? - - regNumber zeroReg = regTracker.rsIconIsInReg(0); - - if (zeroReg == REG_NA) - { - regMaskTP freeMask = regSet.rsRegMaskFree(); - - if ((freeMask != 0) && (compiler->compCodeOpt() != Compiler::FAST_CODE)) - { - // For SMALL_CODE and BLENDED_CODE, - // we try to generate: - // - // xor reg, reg - // mov dest, reg - // - // When selecting a register to xor we try to avoid REG_TMP_0 - // when we have another CALLEE_TRASH register available. - // This will often let us reuse the zeroed register in - // several back-to-back assignments - // - if ((freeMask & RBM_CALLEE_TRASH) != RBM_TMP_0) - freeMask &= ~RBM_TMP_0; - zeroReg = regSet.rsGrabReg(freeMask); // PickReg in stress will pick 'random' registers - // We want one in the freeMask set, so just use GrabReg - genSetRegToIcon(zeroReg, 0, TYP_INT); - } - } - - return zeroReg; -} - -/***************************************************************************** - * - * Generate an instruction that has one operand given by a tree (which has - * been made addressable) and another that is an integer constant. - */ -void CodeGen::inst_TT_IV(instruction ins, GenTree* tree, ssize_t val, unsigned offs, emitAttr size, insFlags flags) -{ - bool sizeInferred = false; - - if (size == EA_UNKNOWN) - { - sizeInferred = true; - if (instIsFP(ins)) - size = EA_ATTR(genTypeSize(tree->TypeGet())); - else - size = emitTypeSize(tree->TypeGet()); - } - -AGAIN: - - /* Is the value sitting in a register? */ - - if (tree->InReg()) - { -#ifndef _TARGET_64BIT_ - LONGREG_TT_IV: -#endif - regNumber reg; - - assert(instIsFP(ins) == 0); - -#if CPU_LONG_USES_REGPAIR - if (tree->gtType == TYP_LONG) - { - if (offs == 0) - { - reg = genRegPairLo(tree->gtRegPair); - } - else // offs == 4 - { - assert(offs == sizeof(int)); - reg = genRegPairHi(tree->gtRegPair); - } -#if CPU_LOAD_STORE_ARCH - if (reg == REG_STK && !getEmitter()->emitInsIsLoadOrStore(ins)) - { - reg = regSet.rsPickFreeReg(); - inst_RV_TT(INS_mov, reg, tree, offs, EA_4BYTE, flags); - regTracker.rsTrackRegTrash(reg); - } -#endif - } - else -#endif // CPU_LONG_USES_REGPAIR - { - reg = tree->gtRegNum; - } - - if (reg != REG_STK) - { - // We always widen as part of enregistering, - // so a smaller tree in a register can be - // treated as 4 bytes - if (sizeInferred && (size < EA_4BYTE)) - { - size = EA_SET_SIZE(size, EA_4BYTE); - } - - if ((ins == INS_mov) && !EA_IS_CNS_RELOC(size)) - { - genSetRegToIcon(reg, val, tree->TypeGet(), flags); - } - else - { -#if defined(_TARGET_XARCH_) - inst_RV_IV(ins, reg, val, size); -#elif defined(_TARGET_ARM_) - if (!EA_IS_CNS_RELOC(size) && arm_Valid_Imm_For_Instr(ins, val, flags)) - { - getEmitter()->emitIns_R_I(ins, size, reg, val, flags); - } - else // We need a scratch register - { - // Load imm into a register - regMaskTP usedMask; - if (tree->gtType == TYP_LONG) - { - usedMask = genRegPairMask(tree->gtRegPair); -#if CPU_LOAD_STORE_ARCH - // In gtRegPair, this part of the long may have been on the stack - // in which case, the code above would have loaded it into 'reg' - // and so we need to also include 'reg' in the set of registers - // that are already in use. - usedMask |= genRegMask(reg); -#endif // CPU_LOAD_STORE_ARCH - } - else - { - usedMask = genRegMask(tree->gtRegNum); - } - regNumber immReg = regSet.rsGrabReg(RBM_ALLINT & ~usedMask); - noway_assert(reg != immReg); - instGen_Set_Reg_To_Imm(size, immReg, val); - if (getEmitter()->emitInsIsStore(ins)) - ins = INS_mov; - getEmitter()->emitIns_R_R(ins, size, reg, immReg, flags); - } -#else - NYI("inst_TT_IV - unknown target"); -#endif - } - return; - } - } - -#ifdef _TARGET_XARCH_ - /* Are we storing a zero? */ - - if ((ins == INS_mov) && (val == 0) && - ((genTypeSize(tree->gtType) == sizeof(int)) || (genTypeSize(tree->gtType) == REGSIZE_BYTES))) - { - regNumber zeroReg; - - zeroReg = genGetZeroRegister(); - - if (zeroReg != REG_NA) - { - inst_TT_RV(INS_mov, tree, zeroReg, offs); - return; - } - } -#endif - -#if CPU_LOAD_STORE_ARCH - /* Are we storing/comparing with a constant? */ - - if (getEmitter()->emitInsIsStore(ins) || getEmitter()->emitInsIsCompare(ins)) - { - // Load val into a register - - regNumber valReg; - valReg = regSet.rsGrabReg(RBM_ALLINT); - instGen_Set_Reg_To_Imm(EA_PTRSIZE, valReg, val); - inst_TT_RV(ins, tree, valReg, offs, size, flags); - return; - } - else if (ins == INS_mov) - { - assert(!"Please call ins_Store(type) to get the store instruction"); - } - assert(!getEmitter()->emitInsIsLoad(ins)); -#endif // CPU_LOAD_STORE_ARCH - - /* Is this a spilled value? */ - - if (tree->gtFlags & GTF_SPILLED) - { - assert(!"ISSUE: If this can happen, we need to generate 'ins [ebp+spill], icon'"); - } - -#ifdef _TARGET_AMD64_ - if ((EA_SIZE(size) == EA_8BYTE) && (((int)val != (ssize_t)val) || EA_IS_CNS_RELOC(size))) - { - // Load imm into a register - regNumber immReg = regSet.rsGrabReg(RBM_ALLINT); - instGen_Set_Reg_To_Imm(size, immReg, val); - inst_TT_RV(ins, tree, immReg, offs); - return; - } -#endif // _TARGET_AMD64_ - - int ival = (int)val; + size = emitTypeSize(tree->TypeGet()); + } + } switch (tree->gtOper) { - unsigned varNum; - LclVarDsc* varDsc; - - case GT_LCL_FLD: - - varNum = tree->gtLclVarCommon.gtLclNum; - assert(varNum < compiler->lvaCount); - offs += tree->gtLclFld.gtLclOffs; - - goto LCL; + unsigned varNum; case GT_LCL_VAR: -#ifndef _TARGET_64BIT_ - /* Is this an enregistered long ? */ - CLANG_FORMAT_COMMENT_ANCHOR; -#ifdef LEGACY_BACKEND - if (tree->gtType == TYP_LONG && !(tree->InReg())) -#else // !LEGACY_BACKEND - if (tree->gtType == TYP_LONG) -#endif // !LEGACY_BACKEND - { - /* Avoid infinite loop */ - - if (genMarkLclVar(tree)) - goto LONGREG_TT_IV; - } -#endif // !_TARGET_64BIT_ - inst_set_SV_var(tree); + goto LCL; - varNum = tree->gtLclVarCommon.gtLclNum; - assert(varNum < compiler->lvaCount); - varDsc = &compiler->lvaTable[varNum]; - - // Fix the immediate by sign extending if needed - if (size < EA_4BYTE && !varTypeIsUnsigned(varDsc->TypeGet())) - { - if (size == EA_1BYTE) - { - if ((ival & 0x7f) != ival) - ival = ival | 0xffffff00; - } - else - { - assert(size == EA_2BYTE); - if ((ival & 0x7fff) != ival) - ival = ival | 0xffff0000; - } - } - - // A local stack slot is at least 4 bytes in size, regardles of - // what the local var is typed as, so auto-promote it here - // unless the codegenerator told us a size, or it is a field - // of a promoted struct - if (sizeInferred && (size < EA_4BYTE) && !varDsc->lvIsStructField) - { - size = EA_SET_SIZE(size, EA_4BYTE); - } + case GT_LCL_FLD: + case GT_STORE_LCL_FLD: + offs += tree->gtLclFld.gtLclOffs; + goto LCL; LCL: - /* Integer instructions never operate on more than EA_PTRSIZE */ - - assert(instIsFP(ins) == false); + varNum = tree->gtLclVarCommon.gtLclNum; + assert(varNum < compiler->lvaCount); #if CPU_LOAD_STORE_ARCH if (!getEmitter()->emitInsIsStore(ins)) { - regNumber regTmp = regSet.rsPickFreeReg(RBM_ALLINT); + // TODO-LdStArch-Bug: Should regTmp be a dst on the node or an internal reg? + // Either way, it is not currently being handled by Lowering. + regNumber regTmp = tree->gtRegNum; + assert(regTmp != REG_NA); getEmitter()->emitIns_R_S(ins_Load(tree->TypeGet()), size, regTmp, varNum, offs); - regTracker.rsTrackRegTrash(regTmp); - - if (arm_Valid_Imm_For_Instr(ins, val, flags)) - { - getEmitter()->emitIns_R_I(ins, size, regTmp, ival, flags); - } - else // We need a scratch register - { - // Load imm into a register - regNumber regImm = regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(regTmp)); - - instGen_Set_Reg_To_Imm(size, regImm, val); - getEmitter()->emitIns_R_R(ins, size, regTmp, regImm, flags); - } + getEmitter()->emitIns_R_R(ins, size, regTmp, reg, flags); getEmitter()->emitIns_S_R(ins_Store(tree->TypeGet()), size, regTmp, varNum, offs); + + regTracker.rsTrackRegTrash(regTmp); } else #endif { - getEmitter()->emitIns_S_I(ins, size, varNum, offs, ival); + // ins is a Store instruction + // + getEmitter()->emitIns_S_R(ins, size, reg, varNum, offs); +#ifdef _TARGET_ARM_ + // If we need to set the flags then add an extra movs reg,reg instruction + if (flags == INS_FLAGS_SET) + getEmitter()->emitIns_R_R(INS_mov, size, reg, reg, INS_FLAGS_SET); +#endif } return; case GT_CLS_VAR: // Make sure FP instruction size matches the operand size - // (We optimize constant doubles to floats when we can) - // We just want to make sure that we don't mistakenly - // use 8 bytes when the constant is smaller. - // + // (We optimized constant doubles to floats when we can, just want to + // make sure that we don't mistakenly use 8 bytes when the + // constant). assert(!isFloatRegType(tree->gtType) || genTypeSize(tree->gtType) == EA_SIZE_IN_BYTES(size)); #if CPU_LOAD_STORE_ARCH - regNumber regTmpAddr; - regTmpAddr = regSet.rsPickFreeReg(RBM_ALLINT); - - getEmitter()->emitIns_R_C(INS_lea, EA_PTRSIZE, regTmpAddr, tree->gtClsVar.gtClsVarHnd, offs); - regTracker.rsTrackRegTrash(regTmpAddr); - if (!getEmitter()->emitInsIsStore(ins)) { - regNumber regTmpArith = regSet.rsPickFreeReg(RBM_ALLINT & ~genRegMask(regTmpAddr)); - - getEmitter()->emitIns_R_R(ins_Load(tree->TypeGet()), size, regTmpArith, regTmpAddr); - - if (arm_Valid_Imm_For_Instr(ins, ival, flags)) - { - getEmitter()->emitIns_R_R_I(ins, size, regTmpArith, regTmpArith, ival, flags); - } - else - { - regNumber regTmpImm = - regSet.rsPickFreeReg(RBM_ALLINT & ~genRegMask(regTmpAddr) & ~genRegMask(regTmpArith)); - instGen_Set_Reg_To_Imm(EA_4BYTE, regTmpImm, (ssize_t)ival); - getEmitter()->emitIns_R_R(ins, size, regTmpArith, regTmpImm, flags); - } - regTracker.rsTrackRegTrash(regTmpArith); - - getEmitter()->emitIns_R_R(ins_Store(tree->TypeGet()), size, regTmpArith, regTmpAddr); + NYI("Store of GT_CLS_VAR not supported for ARM"); } else +#endif // CPU_LOAD_STORE_ARCH { - regNumber regTmpImm = regSet.rsPickFreeReg(RBM_ALLINT & ~genRegMask(regTmpAddr)); - - instGen_Set_Reg_To_Imm(EA_4BYTE, regTmpImm, (ssize_t)ival, flags); - getEmitter()->emitIns_R_R(ins_Store(tree->TypeGet()), size, regTmpImm, regTmpAddr); + getEmitter()->emitIns_C_R(ins, size, tree->gtClsVar.gtClsVarHnd, reg, offs); } -#else // !CPU_LOAD_STORE_ARCH - getEmitter()->emitIns_C_I(ins, size, tree->gtClsVar.gtClsVarHnd, offs, ival); -#endif return; case GT_IND: case GT_NULLCHECK: case GT_ARR_ELEM: { - GenTree* addr = tree->OperIsIndir() ? tree->gtOp.gtOp1 : tree; - sched_AM(ins, size, REG_NA, false, addr, offs, true, ival, flags); + assert(!"inst_TT_RV not supported for GT_IND, GT_NULLCHECK or GT_ARR_ELEM"); } - return; + break; case GT_COMMA: // tree->gtOp.gtOp1 - already processed by genCreateAddrMode() @@ -2241,50 +759,6 @@ AGAIN: } } -/***************************************************************************** - * - * Generate an instruction that has one operand given by a register and the - * other one by an indirection tree (which has been made addressable). - */ - -void CodeGen::inst_RV_AT( - instruction ins, emitAttr size, var_types type, regNumber reg, GenTree* tree, unsigned offs, insFlags flags) -{ -#ifdef _TARGET_XARCH_ -#ifdef DEBUG - // If it is a GC type and the result is not, then either - // 1) it is an LEA - // 2) optOptimizeBools() optimized if (ref != 0 && ref != 0) to if (ref & ref) - // 3) optOptimizeBools() optimized if (ref == 0 || ref == 0) to if (ref | ref) - // 4) byref - byref = int - if (type == TYP_REF && !EA_IS_GCREF(size)) - assert((EA_IS_BYREF(size) && ins == INS_add) || (ins == INS_lea || ins == INS_and || ins == INS_or)); - if (type == TYP_BYREF && !EA_IS_BYREF(size)) - assert(ins == INS_lea || ins == INS_and || ins == INS_or || ins == INS_sub); - assert(!instIsFP(ins)); -#endif -#endif - - // Integer instructions never operate on more than EA_PTRSIZE. - if (EA_SIZE(size) > EA_PTRSIZE && !instIsFP(ins)) - EA_SET_SIZE(size, EA_PTRSIZE); - - GenTree* addr = tree; - sched_AM(ins, size, reg, true, addr, offs, false, 0, flags); -} - -/***************************************************************************** - * - * Generate an instruction that has one operand given by an indirection tree - * (which has been made addressable) and an integer constant. - */ - -void CodeGen::inst_AT_IV(instruction ins, emitAttr size, GenTree* baseTree, int icon, unsigned offs) -{ - sched_AM(ins, size, REG_NA, false, baseTree, offs, true, icon); -} -#endif // LEGACY_BACKEND - /***************************************************************************** * * Generate an instruction that has one operand given by a register and the @@ -2333,20 +807,7 @@ void CodeGen::inst_RV_TT(instruction ins, #if CPU_LOAD_STORE_ARCH if (ins == INS_mov) { -#if defined(_TARGET_ARM_) && CPU_LONG_USES_REGPAIR - if (tree->TypeGet() != TYP_LONG) - { - ins = ins_Move_Extend(tree->TypeGet(), tree->InReg()); - } - else if (offs == 0) - { - ins = ins_Move_Extend(TYP_INT, tree->InReg() && genRegPairLo(tree->gtRegPair) != REG_STK); - } - else - { - ins = ins_Move_Extend(TYP_INT, tree->InReg() && genRegPairHi(tree->gtRegPair) != REG_STK); - } -#elif defined(_TARGET_ARM64_) || defined(_TARGET_ARM64_) +#if defined(_TARGET_ARM64_) || defined(_TARGET_ARM64_) ins = ins_Move_Extend(tree->TypeGet(), false); #else NYI("CodeGen::inst_RV_TT with INS_mov"); @@ -2356,82 +817,6 @@ void CodeGen::inst_RV_TT(instruction ins, AGAIN: -#ifdef LEGACY_BACKEND - /* Is the value sitting in a register? */ - - if (tree->InReg()) - { -#ifdef _TARGET_64BIT_ - assert(instIsFP(ins) == 0); - - regNumber rg2 = tree->gtRegNum; - - assert(offs == 0); - assert(rg2 != REG_STK); - - if ((ins != INS_mov) || (rg2 != reg)) - { - inst_RV_RV(ins, reg, rg2, tree->TypeGet(), size); - } - return; - -#else // !_TARGET_64BIT_ - -#ifdef LEGACY_BACKEND - LONGREG_RVTT: -#endif // LEGACY_BACKEND - -#ifdef _TARGET_XARCH_ - assert(instIsFP(ins) == 0); -#endif - - regNumber rg2; - -#if CPU_LONG_USES_REGPAIR - if (tree->gtType == TYP_LONG) - { - if (offs) - { - assert(offs == sizeof(int)); - - rg2 = genRegPairHi(tree->gtRegPair); - } - else - { - rg2 = genRegPairLo(tree->gtRegPair); - } - } - else -#endif // LEGACY_BACKEND - { - rg2 = tree->gtRegNum; - } - - if (rg2 != REG_STK) - { -#ifdef _TARGET_ARM_ - if (getEmitter()->emitInsIsLoad(ins) || (ins == INS_lea)) - { - ins = ins_Copy(tree->TypeGet()); - } -#endif - - bool isMoveIns = (ins == INS_mov); -#ifdef _TARGET_ARM_ - if (ins == INS_vmov) - isMoveIns = true; -#endif - if (!isMoveIns || (rg2 != reg)) - { - inst_RV_RV(ins, reg, rg2, tree->TypeGet(), size, flags); - } - return; - } - -#endif // _TARGET_64BIT_ - } -#endif // LEGACY_BACKEND - /* Is this a spilled value? */ if (tree->gtFlags & GTF_SPILLED) @@ -2446,19 +831,6 @@ AGAIN: case GT_LCL_VAR: case GT_LCL_VAR_ADDR: -#ifdef LEGACY_BACKEND - /* Is this an enregistered long ? */ - - if (tree->gtType == TYP_LONG && !(tree->InReg())) - { - - /* Avoid infinite loop */ - - if (genMarkLclVar(tree)) - goto LONGREG_RVTT; - } -#endif // LEGACY_BACKEND - inst_set_SV_var(tree); goto LCL; @@ -2491,40 +863,7 @@ AGAIN: default: regNumber regTmp; -#ifndef LEGACY_BACKEND -#if CPU_LONG_USES_REGPAIR - if (tree->TypeGet() == TYP_LONG) - regTmp = (offs == 0) ? genRegPairLo(tree->gtRegPair) : genRegPairHi(tree->gtRegPair); - else -#endif // CPU_LONG_USES_REGPAIR - regTmp = tree->gtRegNum; -#else // LEGACY_BACKEND - if (varTypeIsFloating(tree)) - { - regTmp = regSet.PickRegFloat(tree->TypeGet()); - } - else - { - // Lock the destination register to ensure that rsPickReg does not choose it. - const regMaskTP regMask = genRegMask(reg); - if ((regMask & regSet.rsMaskUsed) == 0) - { - regSet.rsLockReg(regMask); - regTmp = regSet.rsPickReg(RBM_ALLINT); - regSet.rsUnlockReg(regMask); - } - else if ((regMask & regSet.rsMaskLock) == 0) - { - regSet.rsLockUsedReg(regMask); - regTmp = regSet.rsPickReg(RBM_ALLINT); - regSet.rsUnlockUsedReg(regMask); - } - else - { - regTmp = regSet.rsPickReg(RBM_ALLINT); - } - } -#endif // LEGACY_BACKEND + regTmp = tree->gtRegNum; getEmitter()->emitIns_R_S(ins_Load(tree->TypeGet()), size, regTmp, varNum, offs); getEmitter()->emitIns_R_R(ins, size, reg, regTmp, flags); @@ -2545,36 +884,7 @@ AGAIN: assert(!isFloatRegType(tree->gtType) || genTypeSize(tree->gtType) == EA_SIZE_IN_BYTES(size)); #if CPU_LOAD_STORE_ARCH -#ifndef LEGACY_BACKEND - assert(!"GT_CLS_VAR not supported in ARM RyuJIT backend"); -#else // LEGACY_BACKEND - switch (ins) - { - case INS_mov: - ins = ins_Load(tree->TypeGet()); - - __fallthrough; - - case INS_lea: - case INS_ldr: - case INS_ldrh: - case INS_ldrb: - case INS_ldrsh: - case INS_ldrsb: - case INS_vldr: - assert(flags != INS_FLAGS_SET); - getEmitter()->emitIns_R_C(ins, size, reg, tree->gtClsVar.gtClsVarHnd, offs); - return; - - default: - regNumber regTmp = regSet.rsPickFreeReg(RBM_ALLINT & ~genRegMask(reg)); - getEmitter()->emitIns_R_C(ins_Load(tree->TypeGet()), size, regTmp, tree->gtClsVar.gtClsVarHnd, - offs); - getEmitter()->emitIns_R_R(ins, size, reg, regTmp, flags); - regTracker.rsTrackRegTrash(regTmp); - return; - } -#endif // LEGACY_BACKEND + assert(!"GT_CLS_VAR not supported in ARM backend"); #else // CPU_LOAD_STORE_ARCH getEmitter()->emitIns_R_C(ins, size, reg, tree->gtClsVar.gtClsVarHnd, offs); #endif // CPU_LOAD_STORE_ARCH @@ -2585,12 +895,7 @@ AGAIN: case GT_ARR_ELEM: case GT_LEA: { -#ifndef LEGACY_BACKEND - assert(!"inst_RV_TT not supported for GT_IND, GT_NULLCHECK, GT_ARR_ELEM or GT_LEA in !LEGACY_BACKEND"); -#else // LEGACY_BACKEND - GenTree* addr = tree->OperIsIndir() ? tree->gtOp.gtOp1 : tree; - inst_RV_AT(ins, size, tree->TypeGet(), reg, addr, offs, flags); -#endif // LEGACY_BACKEND + assert(!"inst_RV_TT not supported for GT_IND, GT_NULLCHECK, GT_ARR_ELEM or GT_LEA"); } break; @@ -2634,32 +939,6 @@ AGAIN: } } -/***************************************************************************** - * - * Generate the 3-operand imul instruction "imul reg, [tree], icon" - * which is reg=[tree]*icon - */ -#ifdef LEGACY_BACKEND -void CodeGen::inst_RV_TT_IV(instruction ins, regNumber reg, GenTree* tree, int val) -{ - assert(tree->gtType <= TYP_I_IMPL); - -#ifdef _TARGET_XARCH_ - /* Only 'imul' uses this instruction format. Since we don't represent - three operands for an instruction, we encode the target register as - an implicit operand */ - - assert(ins == INS_imul); - ins = getEmitter()->inst3opImulForReg(reg); - - genUpdateLife(tree); - inst_TT_IV(ins, tree, val); -#else - NYI("inst_RV_TT_IV - unknown target"); -#endif -} -#endif // LEGACY_BACKEND - /***************************************************************************** * * Generate a "shift reg, icon" instruction. @@ -2749,13 +1028,9 @@ void CodeGen::inst_TT_CL(instruction ins, GenTree* tree, unsigned offs) #if defined(_TARGET_XARCH_) void CodeGen::inst_RV_RV_IV(instruction ins, emitAttr size, regNumber reg1, regNumber reg2, unsigned ival) { -#if defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND) assert(ins == INS_shld || ins == INS_shrd || ins == INS_shufps || ins == INS_shufpd || ins == INS_pshufd || ins == INS_cmpps || ins == INS_cmppd || ins == INS_dppd || ins == INS_dpps || ins == INS_insertps || ins == INS_roundps || ins == INS_roundss || ins == INS_roundpd || ins == INS_roundsd); -#else // !_TARGET_XARCH_ - assert(ins == INS_shld || ins == INS_shrd); -#endif // !_TARGET_XARCH_ getEmitter()->emitIns_R_R_I(ins, size, reg1, reg2, ival); } @@ -2825,17 +1100,7 @@ void CodeGen::inst_RV_ST(instruction ins, emitAttr size, regNumber reg, GenTree* { assert(size == EA_1BYTE || size == EA_2BYTE); -#ifdef LEGACY_BACKEND - if (tree->InReg()) - { - /* "movsx erx, rl" must be handled as a special case */ - inst_RV_RR(ins, size, reg, tree->gtRegNum); - } - else -#endif // LEGACY_BACKEND - { - inst_RV_TT(ins, reg, tree, 0, size); - } + inst_RV_TT(ins, reg, tree, 0, size); } void CodeGen::inst_RV_ST(instruction ins, regNumber reg, TempDsc* tmp, unsigned ofs, var_types type, emitAttr size) @@ -2864,22 +1129,7 @@ void CodeGen::inst_RV_ST(instruction ins, regNumber reg, TempDsc* tmp, unsigned break; default: -#ifndef LEGACY_BACKEND - assert(!"Default inst_RV_ST case not supported for Arm !LEGACY_BACKEND"); -#else // LEGACY_BACKEND - regNumber regTmp; - if (varTypeIsFloating(type)) - { - regTmp = regSet.PickRegFloat(type); - } - else - { - regTmp = regSet.rsPickFreeReg(RBM_ALLINT & ~genRegMask(reg)); - } - getEmitter()->emitIns_R_S(ins_Load(type), size, regTmp, tmp->tdTempNum(), ofs); - regTracker.rsTrackRegTrash(regTmp); - getEmitter()->emitIns_R_R(ins, size, reg, regTmp); -#endif // LEGACY_BACKEND + assert(!"Default inst_RV_ST case not supported for Arm"); break; } #else // !_TARGET_ARM_ @@ -2891,29 +1141,14 @@ void CodeGen::inst_mov_RV_ST(regNumber reg, GenTree* tree) { /* Figure out the size of the value being loaded */ - emitAttr size = EA_ATTR(genTypeSize(tree->gtType)); -#ifdef LEGACY_BACKEND - instruction loadIns = ins_Move_Extend(tree->TypeGet(), tree->InReg()); -#else // !LEGACY_BACKEND + emitAttr size = EA_ATTR(genTypeSize(tree->gtType)); instruction loadIns = ins_Move_Extend(tree->TypeGet(), false); -#endif // !LEGACY_BACKEND if (size < EA_4BYTE) { -#if CPU_HAS_BYTE_REGS && defined(LEGACY_BACKEND) - if ((tree->gtFlags & GTF_SMALL_OK) && (size == EA_1BYTE) && (genRegMask(reg) & RBM_BYTE_REGS)) - { - /* We only need to load the actual size */ - - inst_RV_TT(INS_mov, reg, tree, 0, EA_1BYTE); - } - else -#endif // CPU_HAS_BYTE_REGS && defined(LEGACY_BACKEND) - { - /* Generate the "movsx/movzx" opcode */ + /* Generate the "movsx/movzx" opcode */ - inst_RV_ST(loadIns, size, reg, tree); - } + inst_RV_ST(loadIns, size, reg, tree); } else { @@ -3112,7 +1347,7 @@ instruction CodeGen::ins_Move_Extend(var_types srcType, bool srcInReg) if (varTypeIsSIMD(srcType)) { -#if defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND) +#if defined(_TARGET_XARCH_) // SSE2/AVX requires destination to be a reg always. // If src is in reg means, it is a reg-reg move. // @@ -3125,12 +1360,12 @@ instruction CodeGen::ins_Move_Extend(var_types srcType, bool srcInReg) return (srcInReg) ? INS_movaps : INS_movups; #elif defined(_TARGET_ARM64_) return (srcInReg) ? INS_mov : ins_Load(srcType); -#else // !defined(_TARGET_ARM64_) && !(defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND)) +#else // !defined(_TARGET_ARM64_) && !defined(_TARGET_XARCH_) assert(!"unhandled SIMD type"); -#endif // !defined(_TARGET_ARM64_) && !(defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND)) +#endif // !defined(_TARGET_ARM64_) && !defined(_TARGET_XARCH_) } -#if defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND) +#if defined(_TARGET_XARCH_) if (varTypeIsFloating(srcType)) { if (srcType == TYP_DOUBLE) @@ -3267,7 +1502,7 @@ instruction CodeGenInterface::ins_Load(var_types srcType, bool aligned /*=false* if (varTypeIsSIMD(srcType)) { -#if defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND) +#if defined(_TARGET_XARCH_) #ifdef FEATURE_SIMD if (srcType == TYP_SIMD8) { @@ -3295,7 +1530,7 @@ instruction CodeGenInterface::ins_Load(var_types srcType, bool aligned /*=false* if (varTypeIsFloating(srcType)) { -#if defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND) +#if defined(_TARGET_XARCH_) if (srcType == TYP_DOUBLE) { return INS_movsdsse2; @@ -3376,7 +1611,7 @@ instruction CodeGenInterface::ins_Load(var_types srcType, bool aligned /*=false* */ instruction CodeGen::ins_Copy(var_types dstType) { -#if defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND) +#if defined(_TARGET_XARCH_) if (varTypeIsSIMD(dstType)) { return INS_movaps; @@ -3430,7 +1665,7 @@ instruction CodeGenInterface::ins_Store(var_types dstType, bool aligned /*=false { instruction ins = INS_invalid; -#if defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND) +#if defined(_TARGET_XARCH_) if (varTypeIsSIMD(dstType)) { #ifdef FEATURE_SIMD @@ -3501,7 +1736,7 @@ instruction CodeGenInterface::ins_Store(var_types dstType, bool aligned /*=false return ins; } -#if defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND) +#if defined(_TARGET_XARCH_) bool CodeGen::isMoveIns(instruction ins) { @@ -3563,24 +1798,12 @@ instruction CodeGen::ins_MathOp(genTreeOps oper, var_types type) switch (oper) { case GT_ADD: -#ifdef LEGACY_BACKEND - case GT_ASG_ADD: -#endif return type == TYP_DOUBLE ? INS_addsd : INS_addss; case GT_SUB: -#ifdef LEGACY_BACKEND - case GT_ASG_SUB: -#endif return type == TYP_DOUBLE ? INS_subsd : INS_subss; case GT_MUL: -#ifdef LEGACY_BACKEND - case GT_ASG_MUL: -#endif return type == TYP_DOUBLE ? INS_mulsd : INS_mulss; case GT_DIV: -#ifdef LEGACY_BACKEND - case GT_ASG_DIV: -#endif return type == TYP_DOUBLE ? INS_divsd : INS_divss; case GT_AND: return type == TYP_DOUBLE ? INS_andpd : INS_andps; @@ -3742,25 +1965,12 @@ instruction CodeGen::ins_MathOp(genTreeOps oper, var_types type) switch (oper) { case GT_ADD: -#ifdef LEGACY_BACKEND - case GT_ASG_ADD: -#endif return INS_vadd; case GT_SUB: -#ifdef LEGACY_BACKEND - case GT_ASG_SUB: -#endif return INS_vsub; case GT_MUL: -#ifdef LEGACY_BACKEND - case GT_ASG_MUL: -#endif return INS_vmul; - break; case GT_DIV: -#ifdef LEGACY_BACKEND - case GT_ASG_DIV: -#endif return INS_vdiv; case GT_NEG: return INS_vneg; @@ -3926,73 +2136,6 @@ void CodeGen::instGen_Set_Reg_To_Zero(emitAttr size, regNumber reg, insFlags fla regTracker.rsTrackRegIntCns(reg, 0); } -#ifdef LEGACY_BACKEND -/***************************************************************************** - * - * Machine independent way to move an immediate value into a register - */ -void CodeGen::instGen_Set_Reg_To_Imm(emitAttr size, regNumber reg, ssize_t imm, insFlags flags) -{ - if (!compiler->opts.compReloc) - { - size = EA_SIZE(size); // Strip any Reloc flags from size if we aren't doing relocs - } - - if ((imm == 0) && !EA_IS_RELOC(size)) - { - instGen_Set_Reg_To_Zero(size, reg, flags); - } - else - { -#if defined(_TARGET_XARCH_) - getEmitter()->emitIns_R_I(INS_mov, size, reg, imm); -#elif defined(_TARGET_ARM_) - - if (EA_IS_RELOC(size)) - { - genMov32RelocatableImmediate(size, imm, reg); - } - else if (arm_Valid_Imm_For_Mov(imm)) - { - getEmitter()->emitIns_R_I(INS_mov, size, reg, imm, flags); - } - else // We have to use a movw/movt pair of instructions - { - ssize_t imm_lo16 = (imm & 0xffff); - ssize_t imm_hi16 = (imm >> 16) & 0xffff; - - assert(arm_Valid_Imm_For_Mov(imm_lo16)); - assert(imm_hi16 != 0); - - getEmitter()->emitIns_R_I(INS_movw, size, reg, imm_lo16); - - // If we've got a low register, the high word is all bits set, - // and the high bit of the low word is set, we can sign extend - // halfword and save two bytes of encoding. This can happen for - // small magnitude negative numbers 'n' for -32768 <= n <= -1. - - if (getEmitter()->isLowRegister(reg) && (imm_hi16 == 0xffff) && ((imm_lo16 & 0x8000) == 0x8000)) - { - getEmitter()->emitIns_R_R(INS_sxth, EA_2BYTE, reg, reg); - } - else - { - getEmitter()->emitIns_R_I(INS_movt, size, reg, imm_hi16); - } - - if (flags == INS_FLAGS_SET) - getEmitter()->emitIns_R_R(INS_mov, size, reg, reg, INS_FLAGS_SET); - } -#elif defined(_TARGET_ARM64_) - NYI_ARM64("instGen_Set_Reg_To_Imm"); -#else -#error "Unknown _TARGET_" -#endif - } - regTracker.rsTrackRegIntCns(reg, imm); -} -#endif // LEGACY_BACKEND - /***************************************************************************** * * Machine independent way to set the flags based on @@ -4040,14 +2183,7 @@ void CodeGen::instGen_Compare_Reg_To_Imm(emitAttr size, regNumber reg, ssize_t i #if defined(_TARGET_AMD64_) if ((EA_SIZE(size) == EA_8BYTE) && (((int)imm != (ssize_t)imm) || EA_IS_CNS_RELOC(size))) { -#ifndef LEGACY_BACKEND assert(!"Invalid immediate for instGen_Compare_Reg_To_Imm"); -#else // LEGACY_BACKEND - // Load imm into a register - regNumber immReg = regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(reg)); - instGen_Set_Reg_To_Imm(size, immReg, (ssize_t)imm); - getEmitter()->emitIns_R_R(INS_cmp, EA_TYPE(size), reg, immReg); -#endif // LEGACY_BACKEND } else #endif // _TARGET_AMD64_ @@ -4061,14 +2197,7 @@ void CodeGen::instGen_Compare_Reg_To_Imm(emitAttr size, regNumber reg, ssize_t i } else // We need a scratch register { -#ifndef LEGACY_BACKEND assert(!"Invalid immediate for instGen_Compare_Reg_To_Imm"); -#else // LEGACY_BACKEND - // Load imm into a register - regNumber immReg = regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(reg)); - instGen_Set_Reg_To_Imm(size, immReg, (ssize_t)imm); - getEmitter()->emitIns_R_R(INS_cmp, size, reg, immReg); -#endif // !LEGACY_BACKEND } #elif defined(_TARGET_ARM64_) if (true) // TODO-ARM64-NYI: arm_Valid_Imm_For_Alu(imm) || arm_Valid_Imm_For_Alu(-imm)) @@ -4127,21 +2256,15 @@ void CodeGen::instGen_Store_Imm_Into_Lcl( } #elif defined(_TARGET_ARMARCH_) // Load imm into a register - CLANG_FORMAT_COMMENT_ANCHOR; - -#ifndef LEGACY_BACKEND regNumber immReg = regToUse; assert(regToUse != REG_NA); -#else // LEGACY_BACKEND - regNumber immReg = (regToUse == REG_NA) ? regSet.rsGrabReg(RBM_ALLINT) : regToUse; -#endif // LEGACY_BACKEND instGen_Set_Reg_To_Imm(sizeAttr, immReg, (ssize_t)imm); instGen_Store_Reg_Into_Lcl(dstType, immReg, varNum, offs); if (EA_IS_RELOC(sizeAttr)) { regTracker.rsTrackRegTrash(immReg); } -#else // _TARGET_* +#else // _TARGET_* #error "Unknown _TARGET_" #endif // _TARGET_* } diff --git a/src/jit/instrsxarch.h b/src/jit/instrsxarch.h index 8d210d9f2e..fa93ad217c 100644 --- a/src/jit/instrsxarch.h +++ b/src/jit/instrsxarch.h @@ -194,7 +194,6 @@ INST3( xorps, "xorps" , 0, IUM_WR, 0, 0, BAD_CODE, BAD_CODE, PCK INST3( cvttsd2si, "cvttsd2si" , 0, IUM_WR, 0, 0, BAD_CODE, BAD_CODE, SSEDBL(0x2C)) // cvt with trunc scalar double to signed DWORDs -#ifndef LEGACY_BACKEND INST3( movntdq, "movntdq" , 0, IUM_WR, 0, 0, PCKDBL(0xE7), BAD_CODE, BAD_CODE) INST3( movnti, "movnti" , 0, IUM_WR, 0, 0, PCKFLT(0xC3), BAD_CODE, BAD_CODE) INST3( movntpd, "movntpd" , 0, IUM_WR, 0, 0, PCKDBL(0x2B), BAD_CODE, BAD_CODE) @@ -387,11 +386,11 @@ INST3( unpcklpd, "unpcklpd" , 0, IUM_WR, 0, 0, BAD_CODE, BAD_CODE, INST3( packssdw, "packssdw" , 0, IUM_WR, 0, 0, BAD_CODE, BAD_CODE, PCKDBL(0x6B)) // Pack (narrow) int to short with saturation INST3( packsswb, "packsswb" , 0, IUM_WR, 0, 0, BAD_CODE, BAD_CODE, PCKDBL(0x63)) // Pack (narrow) short to byte with saturation INST3( packuswb, "packuswb" , 0, IUM_WR, 0, 0, BAD_CODE, BAD_CODE, PCKDBL(0x67)) // Pack (narrow) short to unsigned byte with saturation -#endif // !LEGACY_BACKEND + INST3(LAST_SSE2_INSTRUCTION, "LAST_SSE2_INSTRUCTION", 0, IUM_WR, 0, 0, BAD_CODE, BAD_CODE, BAD_CODE) -#ifndef LEGACY_BACKEND INST3(FIRST_SSE4_INSTRUCTION, "FIRST_SSE4_INSTRUCTION", 0, IUM_WR, 0, 0, BAD_CODE, BAD_CODE, BAD_CODE) + // enum name FP updmode rf wf MR MI RM INST3( dpps, "dpps" , 0, IUM_WR, 0, 0, BAD_CODE, BAD_CODE, SSE3A(0x40)) // Packed dot product of two float vector regs INST3( dppd, "dppd" , 0, IUM_WR, 0, 0, BAD_CODE, BAD_CODE, SSE3A(0x41)) // Packed dot product of two double vector regs @@ -510,7 +509,7 @@ INST3( lzcnt, "lzcnt" , 0, IUM_WR, 0, 0, BAD_CODE, BAD_CODE, SS // POPCNT INST3( popcnt, "popcnt" , 0, IUM_WR, 0, 0, BAD_CODE, BAD_CODE, SSEFLT(0xB8)) -#endif // !LEGACY_BACKEND + // enum name FP updmode rf wf R/M,R/M[reg] R/M,icon INST2(ret , "ret" , 0, IUM_RD, 0, 0, 0x0000C3, 0x0000C2) @@ -545,25 +544,25 @@ INST2(sar_N , "sar" , 0, IUM_RW, 0, 1, 0x0038C0, 0x0038C0) INST1(r_movsb, "rep movsb" , 0, IUM_RD, 0, 0, 0x00A4F3) INST1(r_movsd, "rep movsd" , 0, IUM_RD, 0, 0, 0x00A5F3) -#if !defined(LEGACY_BACKEND) && defined(_TARGET_AMD64_) +#if defined(_TARGET_AMD64_) INST1(r_movsq, "rep movsq" , 0, IUM_RD, 0, 0, 0xF3A548) -#endif // !LEGACY_BACKEND || !defined(_TARGET_AMD64_) +#endif // defined(_TARGET_AMD64_) INST1(movsb , "movsb" , 0, IUM_RD, 0, 0, 0x0000A4) INST1(movsd , "movsd" , 0, IUM_RD, 0, 0, 0x0000A5) -#if !defined(LEGACY_BACKEND) && defined(_TARGET_AMD64_) +#if defined(_TARGET_AMD64_) INST1(movsq, "movsq" , 0, IUM_RD, 0, 0, 0x00A548) -#endif // !LEGACY_BACKEND || !defined(_TARGET_AMD64_) +#endif // defined(_TARGET_AMD64_) INST1(r_stosb, "rep stosb" , 0, IUM_RD, 0, 0, 0x00AAF3) INST1(r_stosd, "rep stosd" , 0, IUM_RD, 0, 0, 0x00ABF3) -#if !defined(LEGACY_BACKEND) && defined(_TARGET_AMD64_) +#if defined(_TARGET_AMD64_) INST1(r_stosq, "rep stosq" , 0, IUM_RD, 0, 0, 0xF3AB48) -#endif // !LEGACY_BACKEND || !defined(_TARGET_AMD64_) +#endif // defined(_TARGET_AMD64_) INST1(stosb, "stosb" , 0, IUM_RD, 0, 0, 0x0000AA) INST1(stosd, "stosd" , 0, IUM_RD, 0, 0, 0x0000AB) -#if !defined(LEGACY_BACKEND) && defined(_TARGET_AMD64_) +#if defined(_TARGET_AMD64_) INST1(stosq, "stosq" , 0, IUM_RD, 0, 0, 0x00AB48) -#endif // !LEGACY_BACKEND || !defined(_TARGET_AMD64_) +#endif // defined(_TARGET_AMD64_) INST1(int3 , "int3" , 0, IUM_RD, 0, 0, 0x0000CC) INST1(nop , "nop" , 0, IUM_RD, 0, 0, 0x000090) @@ -596,53 +595,6 @@ INST1(fld , "fld" , 1, IUM_WR, 0, 0, 0x0000D9) INST1(fstp , "fstp" , 1, IUM_WR, 0, 0, 0x0018D9) #endif // _TARGET_X86 -#if FEATURE_STACK_FP_X87 -INST1(fnstsw , "fnstsw" , 1, IUM_WR, 1, 0, 0x0020DF) -INST1(fcom , "fcom" , 1, IUM_RD, 0, 1, 0x0010D8) -INST1(fcomp , "fcomp" , 1, IUM_RD, 0, 1, 0x0018D8) -INST1(fcompp , "fcompp" , 1, IUM_RD, 0, 1, 0x00D9DE) -INST1(fcomi , "fcomi" , 1, IUM_RD, 0, 1, 0x00F0DB) -INST1(fcomip , "fcomip" , 1, IUM_RD, 0, 1, 0x00F0DF) - -INST1(fchs , "fchs" , 1, IUM_RW, 0, 1, 0x00E0D9) -INST1(fabs , "fabs" , 1, IUM_RW, 0, 1, 0x00E1D9) -INST1(fsin , "fsin" , 1, IUM_RW, 0, 1, 0x00FED9) -INST1(fcos , "fcos" , 1, IUM_RW, 0, 1, 0x00FFD9) -INST1(fsqrt , "fsqrt" , 1, IUM_RW, 0, 1, 0x00FAD9) -INST1(fldl2e , "fldl2e" , 1, IUM_RW, 0, 1, 0x00EAD9) -INST1(frndint, "frndint" , 1, IUM_RW, 0, 1, 0x00FCD9) -INST1(f2xm1 , "f2xm1" , 1, IUM_RW, 0, 1, 0x00F0D9) -INST1(fscale , "fscale" , 1, IUM_RW, 0, 1, 0x00FDD9) - -INST1(fld1 , "fld1" , 1, IUM_WR, 0, 0, 0x00E8D9) -INST1(fldz , "fldz" , 1, IUM_WR, 0, 0, 0x00EED9) -INST1(fst , "fst" , 1, IUM_WR, 0, 0, 0x0010D9) - -INST1(fadd , "fadd" , 1, IUM_RW, 0, 0, 0x0000D8) -INST1(faddp , "faddp" , 1, IUM_RW, 0, 0, 0x0000DA) -INST1(fsub , "fsub" , 1, IUM_RW, 0, 0, 0x0020D8) -INST1(fsubp , "fsubp" , 1, IUM_RW, 0, 0, 0x0028DA) -INST1(fsubr , "fsubr" , 1, IUM_RW, 0, 0, 0x0028D8) -INST1(fsubrp , "fsubrp" , 1, IUM_RW, 0, 0, 0x0020DA) -INST1(fmul , "fmul" , 1, IUM_RW, 0, 0, 0x0008D8) -INST1(fmulp , "fmulp" , 1, IUM_RW, 0, 0, 0x0008DA) -INST1(fdiv , "fdiv" , 1, IUM_RW, 0, 0, 0x0030D8) -INST1(fdivp , "fdivp" , 1, IUM_RW, 0, 0, 0x0038DA) -INST1(fdivr , "fdivr" , 1, IUM_RW, 0, 0, 0x0038D8) -INST1(fdivrp , "fdivrp" , 1, IUM_RW, 0, 0, 0x0030DA) - -INST1(fxch , "fxch" , 1, IUM_RW, 0, 0, 0x00C8D9) -INST1(fprem , "fprem" , 0, IUM_RW, 0, 1, 0x00F8D9) - -INST1(fild , "fild" , 1, IUM_RD, 0, 0, 0x0000DB) -INST1(fildl , "fild" , 1, IUM_RD, 0, 0, 0x0028DB) -INST1(fistp , "fistp" , 1, IUM_WR, 0, 0, 0x0018DB) -INST1(fistpl , "fistp" , 1, IUM_WR, 0, 0, 0x0038DB) - -INST1(fldcw , "fldcw" , 1, IUM_RD, 0, 0, 0x0028D9) -INST1(fnstcw , "fnstcw" , 1, IUM_WR, 0, 0, 0x0038D9) -#endif // FEATURE_STACK_FP_X87 - INST1(seto , "seto" , 0, IUM_WR, 1, 0, 0x0F0090) INST1(setno , "setno" , 0, IUM_WR, 1, 0, 0x0F0091) INST1(setb , "setb" , 0, IUM_WR, 1, 0, 0x0F0092) diff --git a/src/jit/jit.h b/src/jit/jit.h index b077c4d8f8..2cc070b797 100644 --- a/src/jit/jit.h +++ b/src/jit/jit.h @@ -272,14 +272,14 @@ #define UNIX_AMD64_ABI_ONLY(x) #endif // defined(UNIX_AMD64_ABI) -#if defined(UNIX_AMD64_ABI) || (!defined(_TARGET_64BIT_) && !defined(LEGACY_BACKEND)) +#if defined(UNIX_AMD64_ABI) || !defined(_TARGET_64BIT_) #define FEATURE_PUT_STRUCT_ARG_STK 1 #define PUT_STRUCT_ARG_STK_ONLY_ARG(x) , x #define PUT_STRUCT_ARG_STK_ONLY(x) x -#else // !(defined(UNIX_AMD64_ABI)|| (!defined(_TARGET_64BIT_) && !defined(LEGACY_BACKEND))) +#else // !(defined(UNIX_AMD64_ABI) || !defined(_TARGET_64BIT_)) #define PUT_STRUCT_ARG_STK_ONLY_ARG(x) #define PUT_STRUCT_ARG_STK_ONLY(x) -#endif // !(defined(UNIX_AMD64_ABI)|| (!defined(_TARGET_64BIT_) && !defined(LEGACY_BACKEND))) +#endif // !(defined(UNIX_AMD64_ABI)|| !defined(_TARGET_64BIT_)) #if defined(UNIX_AMD64_ABI) #define UNIX_AMD64_ABI_ONLY_ARG(x) , x diff --git a/src/jit/jit.settings.targets b/src/jit/jit.settings.targets index 467930e051..58ca992d69 100644 --- a/src/jit/jit.settings.targets +++ b/src/jit/jit.settings.targets @@ -88,28 +88,25 @@ - - - - - + + + + - - - - - - - - - + + + + + + + + - @@ -124,18 +121,16 @@ - - - - - - - - + + + + + + + - diff --git a/src/jit/jitconfigvalues.h b/src/jit/jitconfigvalues.h index 35398775f3..9c20cd3da5 100644 --- a/src/jit/jitconfigvalues.h +++ b/src/jit/jitconfigvalues.h @@ -263,10 +263,9 @@ CONFIG_INTEGER(JitEnableNoWayAssert, W("JitEnableNoWayAssert"), 1) #endif // !defined(DEBUG) && !defined(_DEBUG) // It was originally intended that JitMinOptsTrackGCrefs only be enabled for amd64 on CoreCLR. A mistake was -// made, and it was enabled for x86 as well. However, it doesn't currently work with x86 legacy back-end, so -// disable it for that. Whether it should continue to be enabled for x86 non-legacy-backend should be investigated. +// made, and it was enabled for x86 as well. Whether it should continue to be enabled for x86 should be investigated. // This is tracked by issue https://github.com/dotnet/coreclr/issues/12415. -#if (defined(_TARGET_AMD64_) && defined(FEATURE_CORECLR)) || (defined(_TARGET_X86_) && !defined(LEGACY_BACKEND)) +#if (defined(_TARGET_AMD64_) && defined(FEATURE_CORECLR)) || defined(_TARGET_X86_) #define JitMinOptsTrackGCrefs_Default 0 // Not tracking GC refs in MinOpts is new behavior #else #define JitMinOptsTrackGCrefs_Default 1 @@ -303,8 +302,7 @@ CONFIG_METHODSET(JitOptRepeat, W("JitOptRepeat")) // Runs optimizer m CONFIG_INTEGER(JitOptRepeatCount, W("JitOptRepeatCount"), 2) // Number of times to repeat opts when repeating #endif // defined(OPT_CONFIG) -CONFIG_INTEGER(JitRegisterFP, W("JitRegisterFP"), 3) // Control FP enregistration -CONFIG_INTEGER(JitTelemetry, W("JitTelemetry"), 1) // If non-zero, gather JIT telemetry data +CONFIG_INTEGER(JitTelemetry, W("JitTelemetry"), 1) // If non-zero, gather JIT telemetry data // Max # of MapSelect's considered for a particular top-level invocation. CONFIG_INTEGER(JitVNMapSelBudget, W("JitVNMapSelBudget"), DEFAULT_MAP_SELECT_BUDGET) diff --git a/src/jit/jiteh.cpp b/src/jit/jiteh.cpp index 2d0eee366f..67abd878f0 100644 --- a/src/jit/jiteh.cpp +++ b/src/jit/jiteh.cpp @@ -2199,9 +2199,6 @@ bool Compiler::fgNormalizeEHCase1() newHndStart->bbCodeOffs = handlerStart->bbCodeOffs; newHndStart->bbCodeOffsEnd = newHndStart->bbCodeOffs; // code size = 0. TODO: use BAD_IL_OFFSET instead? newHndStart->inheritWeight(handlerStart); -#if FEATURE_STACK_FP_X87 - newHndStart->bbFPStateX87 = codeGen->FlatFPAllocFPState(handlerStart->bbFPStateX87); -#endif // FEATURE_STACK_FP_X87 newHndStart->bbFlags |= (BBF_DONT_REMOVE | BBF_INTERNAL | BBF_HAS_LABEL); modified = true; @@ -2360,9 +2357,6 @@ bool Compiler::fgNormalizeEHCase2() newTryStart->bbCodeOffsEnd = newTryStart->bbCodeOffs; // code size = 0. TODO: use BAD_IL_OFFSET instead? newTryStart->inheritWeight(tryStart); -#if FEATURE_STACK_FP_X87 - newTryStart->bbFPStateX87 = codeGen->FlatFPAllocFPState(tryStart->bbFPStateX87); -#endif // FEATURE_STACK_FP_X87 // Note that we don't need to clear any flags on the old try start, since it is still a 'try' // start. @@ -2765,10 +2759,6 @@ bool Compiler::fgNormalizeEHCase3() newLast->bbCodeOffs = insertAfterBlk->bbCodeOffsEnd; newLast->bbCodeOffsEnd = newLast->bbCodeOffs; // code size = 0. TODO: use BAD_IL_OFFSET instead? newLast->inheritWeight(insertAfterBlk); -#if FEATURE_STACK_FP_X87 - newLast->bbFPStateX87 = codeGen->FlatFPAllocFPState(insertAfterBlk->bbFPStateX87); -#endif // FEATURE_STACK_FP_X87 - newLast->bbFlags |= BBF_INTERNAL; // The new block (a fall-through block) is a new predecessor. diff --git a/src/jit/jitgcinfo.h b/src/jit/jitgcinfo.h index 2c79783d3a..dddde20e92 100644 --- a/src/jit/jitgcinfo.h +++ b/src/jit/jitgcinfo.h @@ -386,11 +386,9 @@ private: #endif // JIT32_GCENCODER #endif // DUMP_GC_TABLES -#ifndef LEGACY_BACKEND - // This method updates the appropriate reg masks when a variable is moved. public: + // This method updates the appropriate reg masks when a variable is moved. void gcUpdateForRegVarMove(regMaskTP srcMask, regMaskTP dstMask, LclVarDsc* varDsc); -#endif // !LEGACY_BACKEND private: ReturnKind getReturnKind(); diff --git a/src/jit/lclvars.cpp b/src/jit/lclvars.cpp index 5886179888..19dadfca09 100644 --- a/src/jit/lclvars.cpp +++ b/src/jit/lclvars.cpp @@ -355,10 +355,8 @@ void Compiler::lvaInitArgs(InitVarDscInfo* varDscInfo) noway_assert(varDscInfo->varNum == info.compArgsCount); assert(varDscInfo->intRegArgNum <= MAX_REG_ARG); - codeGen->intRegState.rsCalleeRegArgCount = varDscInfo->intRegArgNum; -#if !FEATURE_STACK_FP_X87 + codeGen->intRegState.rsCalleeRegArgCount = varDscInfo->intRegArgNum; codeGen->floatRegState.rsCalleeRegArgCount = varDscInfo->floatRegArgNum; -#endif // FEATURE_STACK_FP_X87 #if FEATURE_FASTTAILCALL // Save the stack usage information @@ -1044,17 +1042,15 @@ void Compiler::lvaInitGenericsCtxt(InitVarDscInfo* varDscInfo) } #endif } -#ifndef LEGACY_BACKEND else { - // For the RyuJIT backend, we need to mark these as being on the stack, - // as this is not done elsewhere in the case that canEnreg returns false. + // We need to mark these as being on the stack, as this is not done elsewhere in the case that canEnreg + // returns false. varDsc->lvOnFrame = true; #if FEATURE_FASTTAILCALL varDscInfo->stackArgSize += TARGET_POINTER_SIZE; #endif // FEATURE_FASTTAILCALL } -#endif // !LEGACY_BACKEND compArgSize += TARGET_POINTER_SIZE; @@ -1120,17 +1116,15 @@ void Compiler::lvaInitVarArgsHandle(InitVarDscInfo* varDscInfo) } #endif // DEBUG } -#ifndef LEGACY_BACKEND else { - // For the RyuJIT backend, we need to mark these as being on the stack, - // as this is not done elsewhere in the case that canEnreg returns false. + // We need to mark these as being on the stack, as this is not done elsewhere in the case that canEnreg + // returns false. varDsc->lvOnFrame = true; #if FEATURE_FASTTAILCALL varDscInfo->stackArgSize += TARGET_POINTER_SIZE; #endif // FEATURE_FASTTAILCALL } -#endif // !LEGACY_BACKEND /* Update the total argument size, count and varDsc */ @@ -1513,15 +1507,10 @@ void Compiler::lvaCanPromoteStructType(CORINFO_CLASS_HANDLE typeHnd, #if 1 // TODO-Cleanup: Consider removing this entire #if block in the future -// This method has two callers. The one in Importer.cpp passes `sortFields == false` and the other passes -// `sortFields == true`. This is a workaround that leaves the inlining behavior the same as before while still -// performing extra struct promotion when compiling the method. -// -// The legacy back-end can't handle this more general struct promotion (notably structs with holes) in -// morph/genPushArgList()/SetupLateArgs, so in that case always check for custom layout. -#if !defined(LEGACY_BACKEND) + // This method has two callers. The one in Importer.cpp passes `sortFields == false` and the other passes + // `sortFields == true`. This is a workaround that leaves the inlining behavior the same as before while still + // performing extra struct promotion when compiling the method. if (!sortFields) // the condition "!sortFields" really means "we are inlining" -#endif { treatAsOverlapping = StructHasCustomLayout(typeFlags); } @@ -2018,7 +2007,7 @@ void Compiler::lvaPromoteStructVar(unsigned lclNum, lvaStructPromotionInfo* Stru } } -#if !defined(LEGACY_BACKEND) && !defined(_TARGET_64BIT_) +#if !defined(_TARGET_64BIT_) //------------------------------------------------------------------------ // lvaPromoteLongVars: "Struct promote" all register candidate longs as if they are structs of two ints. // @@ -2099,7 +2088,7 @@ void Compiler::lvaPromoteLongVars() } #endif // DEBUG } -#endif // !defined(LEGACY_BACKEND) && !defined(_TARGET_64BIT_) +#endif // !defined(_TARGET_64BIT_) /***************************************************************************** * Given a fldOffset in a promoted struct var, return the index of the local @@ -2222,7 +2211,7 @@ void Compiler::lvaSetVarDoNotEnregister(unsigned varNum DEBUGARG(DoNotEnregister assert(varDsc->lvPinned); break; #endif -#if !defined(LEGACY_BACKEND) && !defined(_TARGET_64BIT_) +#if !defined(_TARGET_64BIT_) case DNER_LongParamField: JITDUMP("it is a decomposed field of a long parameter\n"); break; @@ -2955,7 +2944,10 @@ int __cdecl Compiler::RefCntCmp(const void* op1, const void* op2) unsigned weight1 = dsc1->lvRefCnt; unsigned weight2 = dsc2->lvRefCnt; -#if !FEATURE_FP_REGALLOC +#ifndef _TARGET_ARM_ + // ARM-TODO: this was disabled for ARM under !FEATURE_FP_REGALLOC; it was probably a left-over from + // legacy backend. It should be enabled and verified. + /* Force integer candidates to sort above float candidates */ bool isFloat1 = isFloatRegType(dsc1->lvType); @@ -3092,7 +3084,10 @@ int __cdecl Compiler::WtdRefCntCmp(const void* op1, const void* op2) unsigned weight1 = dsc1->lvRefCntWtd; unsigned weight2 = dsc2->lvRefCntWtd; -#if !FEATURE_FP_REGALLOC +#ifndef _TARGET_ARM_ + // ARM-TODO: this was disabled for ARM under !FEATURE_FP_REGALLOC; it was probably a left-over from + // legacy backend. It should be enabled and verified. + /* Force integer candidates to sort above float candidates */ bool isFloat1 = isFloatRegType(dsc1->lvType); @@ -3322,12 +3317,12 @@ void Compiler::lvaSortByRefCount() varDsc->lvRefCntWtd = 0; } -#if !defined(_TARGET_64BIT_) && !defined(LEGACY_BACKEND) +#if !defined(_TARGET_64BIT_) if (varTypeIsLong(varDsc) && varDsc->lvPromoted) { varDsc->lvTracked = 0; } -#endif // !defined(_TARGET_64BIT_) && !defined(LEGACY_BACKEND) +#endif // !defined(_TARGET_64BIT_) // Variables that are address-exposed, and all struct locals, are never enregistered, or tracked. // (The struct may be promoted, and its field variables enregistered/tracked, or the VM may "normalize" @@ -3375,7 +3370,6 @@ void Compiler::lvaSortByRefCount() lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_PinningRef)); #endif } -#if !defined(JIT32_GCENCODER) || !defined(LEGACY_BACKEND) else if (opts.MinOpts() && !JitConfig.JitMinOptsTrackGCrefs() && varTypeIsGC(varDsc->TypeGet())) { varDsc->lvTracked = 0; @@ -3385,7 +3379,6 @@ void Compiler::lvaSortByRefCount() { lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_NoRegVars)); } -#endif // !defined(JIT32_GCENCODER) || !defined(LEGACY_BACKEND) #if defined(JIT32_GCENCODER) && defined(WIN64EXCEPTIONS) else if (lvaIsOriginalThisArg(lclNum) && (info.compMethodInfo->options & CORINFO_GENERICS_CTXT_FROM_THIS) != 0) { @@ -3541,7 +3534,6 @@ const size_t LclVarDsc::lvArgStackSize() const return stackSize; } -#ifndef LEGACY_BACKEND /********************************************************************************** * Get type of a variable when passed as an argument. */ @@ -3608,7 +3600,6 @@ var_types LclVarDsc::lvaArgType() return type; } -#endif // !LEGACY_BACKEND /***************************************************************************** * @@ -3650,26 +3641,11 @@ void Compiler::lvaMarkLclRefs(GenTree* tree) unsigned lclNum; LclVarDsc* varDsc = nullptr; -#ifdef LEGACY_BACKEND - /* GT_CHS is special it doesn't have a valid op2 */ - if (tree->gtOper == GT_CHS) + if (op2->gtOper == GT_LCL_VAR) { - if (op1->gtOper == GT_LCL_VAR) - { - lclNum = op1->gtLclVarCommon.gtLclNum; - noway_assert(lclNum < lvaCount); - varDsc = &lvaTable[lclNum]; - } - } - else -#endif - { - if (op2->gtOper == GT_LCL_VAR) - { - lclNum = op2->gtLclVarCommon.gtLclNum; - noway_assert(lclNum < lvaCount); - varDsc = &lvaTable[lclNum]; - } + lclNum = op2->gtLclVarCommon.gtLclNum; + noway_assert(lclNum < lvaCount); + varDsc = &lvaTable[lclNum]; } #if CPU_HAS_BYTE_REGS if (varDsc) @@ -4186,7 +4162,6 @@ unsigned Compiler::lvaGetMaxSpillTempSize() { unsigned result = 0; -#ifndef LEGACY_BACKEND if (lvaDoneFrameLayout >= REGALLOC_FRAME_LAYOUT) { result = tmpSize; @@ -4195,38 +4170,6 @@ unsigned Compiler::lvaGetMaxSpillTempSize() { result = MAX_SPILL_TEMP_SIZE; } -#else // LEGACY_BACKEND - if (lvaDoneFrameLayout >= FINAL_FRAME_LAYOUT) - { - result = tmpSize; - } - else - { - if (lvaDoneFrameLayout >= REGALLOC_FRAME_LAYOUT) - { - unsigned maxTmpSize = sizeof(double) + sizeof(int); - - maxTmpSize += (tmpDoubleSpillMax * sizeof(double)) + (tmpIntSpillMax * sizeof(int)); - - result = maxTmpSize; - } - else - { - result = MAX_SPILL_TEMP_SIZE; - } -#ifdef DEBUG - // When StressRegs is >=1, there can be a bunch of spills that are not - // predicted by the predictor (see logic in rsPickReg). It is very hard - // to teach the predictor about the behavior of rsPickReg for StressRegs >= 1, - // so instead let's make MaxTmpSize large enough so that we won't be wrong. - - if (codeGen->regSet.rsStressRegs() >= 1) - { - result += (REG_TMP_ORDER_COUNT * REGSIZE_BYTES); - } -#endif // DEBUG - } -#endif // LEGACY_BACKEND return result; } @@ -4819,7 +4762,6 @@ bool Compiler::lvaIsPreSpilled(unsigned lclNum, regMaskTP preSpillMask) } #endif // _TARGET_ARM_ -#ifndef LEGACY_BACKEND /***************************************************************************** * lvaUpdateArgsWithInitialReg() : For each argument variable descriptor, update * its current register with the initial register as assigned by LSRA. @@ -4847,20 +4789,10 @@ void Compiler::lvaUpdateArgsWithInitialReg() if (varDsc->lvIsRegCandidate()) { - if (varTypeIsMultiReg(varDsc)) - { - regPairNo initialRegPair = varDsc->lvArgInitRegPair; - varDsc->lvRegNum = genRegPairLo(initialRegPair); - varDsc->lvOtherReg = genRegPairHi(initialRegPair); - } - else - { - varDsc->lvRegNum = varDsc->lvArgInitReg; - } + varDsc->lvRegNum = varDsc->lvArgInitReg; } } } -#endif // !LEGACY_BACKEND /***************************************************************************** * lvaAssignVirtualFrameOffsetsToArgs() : Assign virtual stack offsets to the @@ -4899,10 +4831,8 @@ void Compiler::lvaAssignVirtualFrameOffsetsToArgs() argOffs -= codeGen->intRegState.rsCalleeRegArgCount * REGSIZE_BYTES; #endif -#ifndef LEGACY_BACKEND // Update the arg initial register locations. lvaUpdateArgsWithInitialReg(); -#endif // !LEGACY_BACKEND /* Is there a "this" argument? */ @@ -6434,16 +6364,16 @@ void Compiler::lvaAssignFrameOffsetsToPromotedStructs() // if (varDsc->lvIsStructField #ifndef UNIX_AMD64_ABI -#if !defined(_TARGET_ARM_) || defined(LEGACY_BACKEND) - // Non-legacy ARM: lo/hi parts of a promoted long arg need to be updated. +#if !defined(_TARGET_ARM_) + // ARM: lo/hi parts of a promoted long arg need to be updated. // For System V platforms there is no outgoing args space. // A register passed struct arg is homed on the stack in a separate local var. // The offset of these structs is already calculated in lvaAssignVirtualFrameOffsetToArg methos. // Make sure the code below is not executed for these structs and the offset is not changed. && !varDsc->lvIsParam -#endif // !defined(_TARGET_ARM_) || defined(LEGACY_BACKEND) -#endif // UNIX_AMD64_ABI +#endif // !defined(_TARGET_ARM_) +#endif // !UNIX_AMD64_ABI ) { LclVarDsc* parentvarDsc = &lvaTable[varDsc->lvParentLcl]; @@ -6600,66 +6530,22 @@ int Compiler::lvaAllocateTemps(int stkOffs, bool mustDoubleAlign) /***************************************************************************** * - * Dump the register a local is in right now. - * For non-LSRA, this will be the register it is always in. For LSRA, it's only the current - * location, since the location changes and it is updated throughout code generation based on - * LSRA register assignments. + * Dump the register a local is in right now. It is only the current location, since the location changes and it + * is updated throughout code generation based on LSRA register assignments. */ void Compiler::lvaDumpRegLocation(unsigned lclNum) { LclVarDsc* varDsc = lvaTable + lclNum; - var_types type = varDsc->TypeGet(); -#if FEATURE_STACK_FP_X87 - if (varTypeIsFloating(type)) - { - printf("fpu stack "); - } - else -#endif - if (isRegPairType(type)) - { - if (!doLSRA()) - { - noway_assert(varDsc->lvRegNum != REG_STK); - } - if (doLSRA() && varDsc->lvRegNum == REG_STK) - { - /* Hi-only enregistered long */ - int offset = varDsc->lvStkOffs; - printf("%-3s:[%1s0x%02X]", - getRegName(varDsc->lvOtherReg), // hi32 - (offset < 0 ? "-" : "+"), (offset < 0 ? -offset : offset)); - } - else if (varDsc->lvOtherReg != REG_STK) - { - /* Fully enregistered long */ - printf("%3s:%-3s ", - getRegName(varDsc->lvOtherReg), // hi32 - getRegName(varDsc->lvRegNum)); // lo32 - } - else - { - /* Partially enregistered long */ - int offset = varDsc->lvStkOffs + 4; - printf("[%1s0x%02X]:%-3s", (offset < 0 ? "-" : "+"), (offset < 0 ? -offset : offset), - getRegName(varDsc->lvRegNum)); // lo32 - } - } #ifdef _TARGET_ARM_ - else if (varDsc->TypeGet() == TYP_DOUBLE) + if (varDsc->TypeGet() == TYP_DOUBLE) { -#ifdef LEGACY_BACKEND - // The assigned registers are `lvRegNum:lvOtherReg` - printf("%3s:%-3s ", getRegName(varDsc->lvRegNum), getRegName(varDsc->lvOtherReg)); -#else // The assigned registers are `lvRegNum:RegNext(lvRegNum)` printf("%3s:%-3s ", getRegName(varDsc->lvRegNum), getRegName(REG_NEXT(varDsc->lvRegNum))); -#endif } -#endif // !_TARGET_ARM_ else +#endif // _TARGET_ARM_ { printf("%3s ", getRegName(varDsc->lvRegNum)); } @@ -6890,12 +6776,6 @@ void Compiler::lvaDumpEntry(unsigned lclNum, FrameLayoutState curState, size_t r { printf(" pinned"); } -#ifdef LEGACY_BACKEND - if (varDsc->lvRefAssign) - { - printf(" ref-asgn"); - } -#endif if (varDsc->lvStackByref) { printf(" stack-byref"); diff --git a/src/jit/legacyjit/.gitmirror b/src/jit/legacyjit/.gitmirror deleted file mode 100644 index f507630f94..0000000000 --- a/src/jit/legacyjit/.gitmirror +++ /dev/null @@ -1 +0,0 @@ -Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file diff --git a/src/jit/legacyjit/CMakeLists.txt b/src/jit/legacyjit/CMakeLists.txt deleted file mode 100644 index 8bf2a1dcb3..0000000000 --- a/src/jit/legacyjit/CMakeLists.txt +++ /dev/null @@ -1,65 +0,0 @@ -project(legacyjit) - -add_definitions(-DLEGACY_BACKEND) -add_definitions(-DALT_JIT) -add_definitions(-DFEATURE_NO_HOST) -add_definitions(-DSELF_NO_HOST) -add_definitions(-DFEATURE_READYTORUN_COMPILER) -remove_definitions(-DFEATURE_MERGE_JIT_AND_ENGINE) - -# No SIMD in legacy back-end. -remove_definitions(-DFEATURE_SIMD) - -# No hardware intrinsic in legacy back-end. -remove_definitions(-DFEATURE_HW_INTRINSICS) - -if(WIN32) - add_definitions(-DFX_VER_INTERNALNAME_STR=legacyjit.dll) -endif(WIN32) - -add_library_clr(legacyjit - SHARED - ${SHARED_LIB_SOURCES} - ${JIT_ARCH_SOURCES} -) - -add_dependencies(legacyjit jit_exports) - -set_property(TARGET legacyjit APPEND_STRING PROPERTY LINK_FLAGS ${JIT_EXPORTS_LINKER_OPTION}) -set_property(TARGET legacyjit APPEND_STRING PROPERTY LINK_DEPENDS ${JIT_EXPORTS_FILE}) - -set(RYUJIT_LINK_LIBRARIES - utilcodestaticnohost - gcinfo -) - -if(CLR_CMAKE_PLATFORM_UNIX) - list(APPEND RYUJIT_LINK_LIBRARIES - mscorrc_debug - coreclrpal - palrt - ) -else() - list(APPEND RYUJIT_LINK_LIBRARIES - ${STATIC_MT_CRT_LIB} - ${STATIC_MT_VCRT_LIB} - kernel32.lib - advapi32.lib - ole32.lib - oleaut32.lib - uuid.lib - user32.lib - version.lib - shlwapi.lib - bcrypt.lib - crypt32.lib - RuntimeObject.lib - ) -endif(CLR_CMAKE_PLATFORM_UNIX) - -target_link_libraries(legacyjit - ${RYUJIT_LINK_LIBRARIES} -) - -# add the install targets -install_clr(legacyjit) diff --git a/src/jit/legacynonjit/.gitmirror b/src/jit/legacynonjit/.gitmirror deleted file mode 100644 index f507630f94..0000000000 --- a/src/jit/legacynonjit/.gitmirror +++ /dev/null @@ -1 +0,0 @@ -Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file diff --git a/src/jit/legacynonjit/CMakeLists.txt b/src/jit/legacynonjit/CMakeLists.txt deleted file mode 100644 index 9e04174dd2..0000000000 --- a/src/jit/legacynonjit/CMakeLists.txt +++ /dev/null @@ -1,71 +0,0 @@ -project(legacynonjit) - -add_definitions(-DALT_JIT) -add_definitions(-DFEATURE_NO_HOST) -add_definitions(-DSELF_NO_HOST) -remove_definitions(-DFEATURE_MERGE_JIT_AND_ENGINE) - -remove_definitions(-DFEATURE_SIMD) - -remove_definitions(-DFEATURE_HW_INTRINSICS) - -add_definitions(-DLEGACY_BACKEND) - -remove_definitions(-D_TARGET_X86_=1) -add_definitions(-D_TARGET_ARM_) -set(JIT_ARCH_ALTJIT_SOURCES ${JIT_ARM_SOURCES}) - -if(FEATURE_READYTORUN) - add_definitions(-DFEATURE_READYTORUN_COMPILER) -endif(FEATURE_READYTORUN) - -if(WIN32) - add_definitions(-DFX_VER_INTERNALNAME_STR=legacynonjit.dll) -endif(WIN32) - -add_library_clr(legacynonjit - SHARED - ${SHARED_LIB_SOURCES} - ${JIT_ARCH_ALTJIT_SOURCES} -) - -add_dependencies(legacynonjit jit_exports) - -set_property(TARGET legacynonjit APPEND_STRING PROPERTY LINK_FLAGS ${JIT_EXPORTS_LINKER_OPTION}) -set_property(TARGET legacynonjit APPEND_STRING PROPERTY LINK_DEPENDS ${JIT_EXPORTS_FILE}) - -set(RYUJIT_LINK_LIBRARIES - utilcodestaticnohost - gcinfo_arm -) - -if(CLR_CMAKE_PLATFORM_UNIX) - list(APPEND RYUJIT_LINK_LIBRARIES - mscorrc_debug - coreclrpal - palrt - ) -else() - list(APPEND RYUJIT_LINK_LIBRARIES - ${STATIC_MT_CRT_LIB} - ${STATIC_MT_VCRT_LIB} - kernel32.lib - advapi32.lib - ole32.lib - oleaut32.lib - uuid.lib - user32.lib - version.lib - shlwapi.lib - bcrypt.lib - crypt32.lib - RuntimeObject.lib - ) -endif(CLR_CMAKE_PLATFORM_UNIX) - -target_link_libraries(legacynonjit - ${RYUJIT_LINK_LIBRARIES} -) - -# add the install targets -install_clr(legacynonjit) diff --git a/src/jit/legacynonjit/legacynonjit.def b/src/jit/legacynonjit/legacynonjit.def deleted file mode 100644 index 1603af74ca..0000000000 --- a/src/jit/legacynonjit/legacynonjit.def +++ /dev/null @@ -1,7 +0,0 @@ -; Licensed to the .NET Foundation under one or more agreements. -; The .NET Foundation licenses this file to you under the MIT license. -; See the LICENSE file in the project root for more information. -EXPORTS - getJit - jitStartup - sxsJitStartup diff --git a/src/jit/lir.h b/src/jit/lir.h index 9d465af8f9..4785a2a37c 100644 --- a/src/jit/lir.h +++ b/src/jit/lir.h @@ -315,9 +315,7 @@ public: inline void GenTree::SetUnusedValue() { gtLIRFlags |= LIR::Flags::UnusedValue; -#ifndef LEGACY_BACKEND ClearContained(); -#endif } inline void GenTree::ClearUnusedValue() diff --git a/src/jit/liveness.cpp b/src/jit/liveness.cpp index a4ef055782..a959358db5 100644 --- a/src/jit/liveness.cpp +++ b/src/jit/liveness.cpp @@ -14,9 +14,7 @@ #if !defined(_TARGET_64BIT_) #include "decomposelongs.h" #endif -#ifndef LEGACY_BACKEND #include "lower.h" // for LowerRange() -#endif /***************************************************************************** * @@ -127,12 +125,10 @@ void Compiler::fgLocalVarLiveness() { printf("*************** In fgLocalVarLiveness()\n"); -#ifndef LEGACY_BACKEND if (compRationalIRForm) { lvaTableDump(); } -#endif // !LEGACY_BACKEND } #endif // DEBUG @@ -182,25 +178,6 @@ void Compiler::fgLocalVarLivenessInit() assert(lvaSortAgain == false); // Set to false by lvaSortOnly() } -#ifdef LEGACY_BACKEND // RyuJIT backend does not use interference info - - for (unsigned i = 0; i < lclMAX_TRACKED; i++) - { - VarSetOps::AssignNoCopy(this, lvaVarIntf[i], VarSetOps::MakeEmpty(this)); - } - - /* If we're not optimizing at all, things are simple */ - if (opts.MinOpts()) - { - VARSET_TP allOnes(VarSetOps::MakeFull(this)); - for (unsigned i = 0; i < lvaTrackedCount; i++) - { - VarSetOps::Assign(this, lvaVarIntf[i], allOnes); - } - return; - } -#endif // LEGACY_BACKEND - // We mark a lcl as must-init in a first pass of local variable // liveness (Liveness1), then assertion prop eliminates the // uninit-use of a variable Vk, asserting it will be init'ed to @@ -222,10 +199,6 @@ void Compiler::fgLocalVarLivenessInit() } } -// Note that for the LEGACY_BACKEND this method is replaced with -// fgLegacyPerStatementLocalVarLiveness and it lives in codegenlegacy.cpp -// -#ifndef LEGACY_BACKEND //------------------------------------------------------------------------ // fgPerNodeLocalVarLiveness: // Set fgCurMemoryUse and fgCurMemoryDef when memory is read or updated @@ -430,8 +403,6 @@ void Compiler::fgPerNodeLocalVarLiveness(GenTree* tree) } } -#endif // !LEGACY_BACKEND - /*****************************************************************************/ void Compiler::fgPerBlockLocalVarLiveness() { @@ -515,34 +486,24 @@ void Compiler::fgPerBlockLocalVarLiveness() fgCurMemoryHavoc = emptyMemoryKindSet; compCurBB = block; - if (!block->IsLIR()) + if (block->IsLIR()) + { + for (GenTree* node : LIR::AsRange(block).NonPhiNodes()) + { + fgPerNodeLocalVarLiveness(node); + } + } + else { for (GenTreeStmt* stmt = block->FirstNonPhiDef(); stmt; stmt = stmt->gtNextStmt) { compCurStmt = stmt; - -#ifdef LEGACY_BACKEND - GenTree* tree = fgLegacyPerStatementLocalVarLiveness(stmt->gtStmtList, nullptr); - assert(tree == nullptr); -#else // !LEGACY_BACKEND for (GenTree* node = stmt->gtStmtList; node != nullptr; node = node->gtNext) { fgPerNodeLocalVarLiveness(node); } -#endif // !LEGACY_BACKEND } } - else - { -#ifdef LEGACY_BACKEND - unreached(); -#else // !LEGACY_BACKEND - for (GenTree* node : LIR::AsRange(block).NonPhiNodes()) - { - fgPerNodeLocalVarLiveness(node); - } -#endif // !LEGACY_BACKEND - } /* Get the TCB local and mark it as used */ @@ -1048,12 +1009,10 @@ void Compiler::fgExtendDbgLifetimes() LIR::Range initRange = LIR::EmptyRange(); initRange.InsertBefore(nullptr, zero, store); -#ifndef LEGACY_BACKEND #if !defined(_TARGET_64BIT_) DecomposeLongs::DecomposeRange(this, blockWeight, initRange); #endif // !defined(_TARGET_64BIT_) m_pLowering->LowerRange(block, initRange); -#endif // !LEGACY_BACKEND // Naively inserting the initializer at the end of the block may add code after the block's // terminator, in which case the inserted code will never be executed (and the IR for the @@ -1362,177 +1321,6 @@ void Compiler::fgLiveVarAnalysis(bool updateInternalOnly) #endif // DEBUG } -//------------------------------------------------------------------------ -// Compiler::fgMarkIntf: -// Mark any variables in varSet1 as interfering with any variables -// specified in varSet2. -// -// We ensure that the interference graph is reflective: if T_x -// interferes with T_y, then T_y interferes with T_x. -// -// Note that this function is a no-op when targeting the RyuJIT -// backend, as it does not require the interference graph. -// -// Arguments: -// varSet1 - The first set of variables. -// varSet2 - The second set of variables. -// -// Returns: -// True if any new interferences were recorded; false otherwise. -// -bool Compiler::fgMarkIntf(VARSET_VALARG_TP varSet1, VARSET_VALARG_TP varSet2) -{ -#ifdef LEGACY_BACKEND - /* If either set has no bits set (or we are not optimizing), take an early out */ - if (opts.MinOpts() || VarSetOps::IsEmpty(this, varSet2) || VarSetOps::IsEmpty(this, varSet1)) - { - return false; - } - - bool addedIntf = false; // This is set to true if we add any new interferences - - VarSetOps::Assign(this, fgMarkIntfUnionVS, varSet1); - VarSetOps::UnionD(this, fgMarkIntfUnionVS, varSet2); - - VarSetOps::Iter iter(this, fgMarkIntfUnionVS); - unsigned refIndex = 0; - while (iter.NextElem(&refIndex)) - { - // if varSet1 has this bit set then it interferes with varSet2 - if (VarSetOps::IsMember(this, varSet1, refIndex)) - { - // Calculate the set of new interference to add - VARSET_TP newIntf(VarSetOps::Diff(this, varSet2, lvaVarIntf[refIndex])); - if (!VarSetOps::IsEmpty(this, newIntf)) - { - addedIntf = true; - VarSetOps::UnionD(this, lvaVarIntf[refIndex], newIntf); - } - } - - // if varSet2 has this bit set then it interferes with varSet1 - if (VarSetOps::IsMember(this, varSet2, refIndex)) - { - // Calculate the set of new interference to add - VARSET_TP newIntf(VarSetOps::Diff(this, varSet1, lvaVarIntf[refIndex])); - if (!VarSetOps::IsEmpty(this, newIntf)) - { - addedIntf = true; - VarSetOps::UnionD(this, lvaVarIntf[refIndex], newIntf); - } - } - } - - return addedIntf; -#else - return false; -#endif -} - -//------------------------------------------------------------------------ -// Compiler::fgMarkIntf: -// Mark any variables in varSet1 as interfering with the variable -// specified by varIndex. -// -// We ensure that the interference graph is reflective: if T_x -// interferes with T_y, then T_y interferes with T_x. -// -// Note that this function is a no-op when targeting the RyuJIT -// backend, as it does not require the interference graph. -// -// Arguments: -// varSet1 - The first set of variables. -// varIndex - The second variable. -// -// Returns: -// True if any new interferences were recorded; false otherwise. -// -bool Compiler::fgMarkIntf(VARSET_VALARG_TP varSet, unsigned varIndex) -{ -#ifdef LEGACY_BACKEND - // If the input set has no bits set (or we are not optimizing), take an early out - if (opts.MinOpts() || VarSetOps::IsEmpty(this, varSet)) - { - return false; - } - - bool addedIntf = false; // This is set to true if we add any new interferences - - VarSetOps::Assign(this, fgMarkIntfUnionVS, varSet); - VarSetOps::AddElemD(this, fgMarkIntfUnionVS, varIndex); - - VarSetOps::Iter iter(this, fgMarkIntfUnionVS); - unsigned refIndex = 0; - while (iter.NextElem(&refIndex)) - { - // if varSet has this bit set then it interferes with varIndex - if (VarSetOps::IsMember(this, varSet, refIndex)) - { - // Calculate the set of new interference to add - if (!VarSetOps::IsMember(this, lvaVarIntf[refIndex], varIndex)) - { - addedIntf = true; - VarSetOps::AddElemD(this, lvaVarIntf[refIndex], varIndex); - } - } - - // if this bit is the same as varIndex then it interferes with varSet1 - if (refIndex == varIndex) - { - // Calculate the set of new interference to add - VARSET_TP newIntf(VarSetOps::Diff(this, varSet, lvaVarIntf[refIndex])); - if (!VarSetOps::IsEmpty(this, newIntf)) - { - addedIntf = true; - VarSetOps::UnionD(this, lvaVarIntf[refIndex], newIntf); - } - } - } - - return addedIntf; -#else - return false; -#endif -} - -/***************************************************************************** - * - * Mark any variables in varSet as interfering with each other, - * This is a specialized version of the above, when both args are the same - * We ensure that the interference graph is reflective: - * (if T11 interferes with T16, then T16 interferes with T11) - * This function returns true if any new interferences were added - * and returns false if no new interference were added - */ - -bool Compiler::fgMarkIntf(VARSET_VALARG_TP varSet) -{ -#ifdef LEGACY_BACKEND - /* No bits set or we are not optimizing, take an early out */ - if (VarSetOps::IsEmpty(this, varSet) || opts.MinOpts()) - return false; - - bool addedIntf = false; // This is set to true if we add any new interferences - - VarSetOps::Iter iter(this, varSet); - unsigned refIndex = 0; - while (iter.NextElem(&refIndex)) - { - // Calculate the set of new interference to add - VARSET_TP newIntf(VarSetOps::Diff(this, varSet, lvaVarIntf[refIndex])); - if (!VarSetOps::IsEmpty(this, newIntf)) - { - addedIntf = true; - VarSetOps::UnionD(this, lvaVarIntf[refIndex], newIntf); - } - } - - return addedIntf; -#else // !LEGACY_BACKEND - return false; -#endif // !LEGACY_BACKEND -} - /***************************************************************************** * For updating liveset during traversal AFTER fgComputeLife has completed */ @@ -1617,9 +1405,6 @@ void Compiler::fgComputeLifeCall(VARSET_TP& life, GenTreeCall* call) if (frameVarDsc->lvTracked) { VarSetOps::AddElemD(this, life, frameVarDsc->lvVarIndex); - - // Record interference with other live variables - fgMarkIntf(life, frameVarDsc->lvVarIndex); } } } @@ -1660,49 +1445,8 @@ void Compiler::fgComputeLifeCall(VARSET_TP& life, GenTreeCall* call) VarSetOps::AddElemD(this, life, varIndex); call->gtCallMoreFlags |= GTF_CALL_M_FRAME_VAR_DEATH; } - - // Record an interference with the other live variables - fgMarkIntf(life, varIndex); - } - } - -#ifdef LEGACY_BACKEND - /* Do we have any live variables? */ - if (!VarSetOps::IsEmpty(this, life)) - { - // For each live variable if it is a GC-ref type, mark it volatile to prevent if from being enregistered - // across the unmanaged call. - // - // Note that this is not necessary when targeting the RyuJIT backend, as its RA handles these kills itself. - - unsigned lclNum; - LclVarDsc* varDsc; - for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++) - { - /* Ignore the variable if it's not tracked */ - - if (!varDsc->lvTracked) - { - continue; - } - - unsigned varNum = varDsc->lvVarIndex; - - /* Ignore the variable if it's not live here */ - - if (!VarSetOps::IsMember(this, life, varDsc->lvVarIndex)) - { - continue; - } - - // If it is a GC-ref type then mark it DoNotEnregister. - if (varTypeIsGC(varDsc->TypeGet())) - { - lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_LiveAcrossUnmanagedCall)); - } } } -#endif // LEGACY_BACKEND } } @@ -1744,9 +1488,6 @@ void Compiler::fgComputeLifeTrackedLocalUse(VARSET_TP& life, LclVarDsc& varDsc, // So the variable is just coming to life node->gtFlags |= GTF_VAR_DEATH; VarSetOps::AddElemD(this, life, varIndex); - - // Record interference with other live variables - fgMarkIntf(life, varIndex); } //------------------------------------------------------------------------ @@ -1831,14 +1572,13 @@ bool Compiler::fgComputeLifeTrackedLocalDef(VARSET_TP& life, // life - The live set that is being computed. // keepAliveVars - The current set of variables to keep alive regardless of their actual lifetime. // varDsc - The LclVar descriptor for the variable being used or defined. -// lclVarNode - The node that corresponds to the local var def or use. Only differs from `node` when targeting -// the legacy backend. -// node - The actual tree node being processed. -void Compiler::fgComputeLifeUntrackedLocal( - VARSET_TP& life, VARSET_VALARG_TP keepAliveVars, LclVarDsc& varDsc, GenTreeLclVarCommon* lclVarNode, GenTree* node) +// lclVarNode - The node that corresponds to the local var def or use. +void Compiler::fgComputeLifeUntrackedLocal(VARSET_TP& life, + VARSET_VALARG_TP keepAliveVars, + LclVarDsc& varDsc, + GenTreeLclVarCommon* lclVarNode) { assert(lclVarNode != nullptr); - assert(node != nullptr); if (!varTypeIsStruct(varDsc.lvType) || (lvaGetPromotionType(&varDsc) == PROMOTION_TYPE_NONE)) { @@ -1849,9 +1589,9 @@ void Compiler::fgComputeLifeUntrackedLocal( for (unsigned i = varDsc.lvFieldLclStart; i < varDsc.lvFieldLclStart + varDsc.lvFieldCnt; ++i) { -#if !defined(_TARGET_64BIT_) && !defined(LEGACY_BACKEND) +#if !defined(_TARGET_64BIT_) if (!varTypeIsLong(lvaTable[i].lvType) || !lvaTable[i].lvPromoted) -#endif // !defined(_TARGET_64BIT_) && !defined(LEGACY_BACKEND) +#endif // !defined(_TARGET_64BIT_) { noway_assert(lvaTable[i].lvIsStructField); } @@ -1862,7 +1602,7 @@ void Compiler::fgComputeLifeUntrackedLocal( VarSetOps::AddElemD(this, varBit, varIndex); } } - if (node->gtFlags & GTF_VAR_DEF) + if (lclVarNode->gtFlags & GTF_VAR_DEF) { VarSetOps::DiffD(this, varBit, keepAliveVars); VarSetOps::DiffD(this, life, varBit); @@ -1873,7 +1613,7 @@ void Compiler::fgComputeLifeUntrackedLocal( // Are the variables already known to be alive? if (VarSetOps::IsSubset(this, varBit, life)) { - node->gtFlags &= ~GTF_VAR_DEATH; // Since we may now call this multiple times, reset if live. + lclVarNode->gtFlags &= ~GTF_VAR_DEATH; // Since we may now call this multiple times, reset if live. return; } @@ -1881,7 +1621,7 @@ void Compiler::fgComputeLifeUntrackedLocal( // So they are just coming to life, in the backwards traversal; in a forwards // traversal, one or more are dying. Mark this. - node->gtFlags |= GTF_VAR_DEATH; + lclVarNode->gtFlags |= GTF_VAR_DEATH; // Are all the variables becoming alive (in the backwards traversal), or just a subset? if (!VarSetOps::IsEmptyIntersection(this, varBit, life)) @@ -1896,9 +1636,6 @@ void Compiler::fgComputeLifeUntrackedLocal( // In any case, all the field vars are now live (in the backwards traversal). VarSetOps::UnionD(this, life, varBit); - - // Record interference with other live variables - fgMarkIntf(life, varBit); } //------------------------------------------------------------------------ @@ -1909,13 +1646,11 @@ void Compiler::fgComputeLifeUntrackedLocal( // Arguments: // life - The live set that is being computed. // keepAliveVars - The current set of variables to keep alive regardless of their actual lifetime. -// lclVarNode - The node that corresponds to the local var def or use. Only differs from `node` when targeting -// the legacy backend. -// node - The actual tree node being processed. +// lclVarNode - The node that corresponds to the local var def or use. // // Returns: // `true` if the local var node corresponds to a dead store; `false` otherwise. -bool Compiler::fgComputeLifeLocal(VARSET_TP& life, VARSET_VALARG_TP keepAliveVars, GenTree* lclVarNode, GenTree* node) +bool Compiler::fgComputeLifeLocal(VARSET_TP& life, VARSET_VALARG_TP keepAliveVars, GenTree* lclVarNode) { unsigned lclNum = lclVarNode->gtLclVarCommon.gtLclNum; @@ -1937,7 +1672,7 @@ bool Compiler::fgComputeLifeLocal(VARSET_TP& life, VARSET_VALARG_TP keepAliveVar } else { - fgComputeLifeUntrackedLocal(life, keepAliveVars, varDsc, lclVarNode->AsLclVarCommon(), node); + fgComputeLifeUntrackedLocal(life, keepAliveVars, varDsc, lclVarNode->AsLclVarCommon()); } return false; } @@ -1948,7 +1683,6 @@ bool Compiler::fgComputeLifeLocal(VARSET_TP& life, VARSET_VALARG_TP keepAliveVar * or subtree of a statement moving backward from startNode to endNode */ -#ifndef LEGACY_BACKEND void Compiler::fgComputeLife(VARSET_TP& life, GenTree* startNode, GenTree* endNode, @@ -1977,7 +1711,7 @@ void Compiler::fgComputeLife(VARSET_TP& life, } else if (tree->OperIsNonPhiLocal() || tree->OperIsLocalAddr()) { - bool isDeadStore = fgComputeLifeLocal(life, keepAliveVars, tree, tree); + bool isDeadStore = fgComputeLifeLocal(life, keepAliveVars, tree); if (isDeadStore) { LclVarDsc* varDsc = &lvaTable[tree->gtLclVarCommon.gtLclNum]; @@ -2085,7 +1819,7 @@ void Compiler::fgComputeLifeLIR(VARSET_TP& life, BasicBlock* block, VARSET_VALAR } else { - fgComputeLifeUntrackedLocal(life, keepAliveVars, varDsc, lclVarNode, node); + fgComputeLifeUntrackedLocal(life, keepAliveVars, varDsc, lclVarNode); } break; } @@ -2106,7 +1840,7 @@ void Compiler::fgComputeLifeLIR(VARSET_TP& life, BasicBlock* block, VARSET_VALAR } else { - isDeadStore = fgComputeLifeLocal(life, keepAliveVars, node, node); + isDeadStore = fgComputeLifeLocal(life, keepAliveVars, node); if (isDeadStore) { LIR::Use addrUse; @@ -2174,7 +1908,7 @@ void Compiler::fgComputeLifeLIR(VARSET_TP& life, BasicBlock* block, VARSET_VALAR } else { - fgComputeLifeUntrackedLocal(life, keepAliveVars, varDsc, lclVarNode, node); + fgComputeLifeUntrackedLocal(life, keepAliveVars, varDsc, lclVarNode); } break; } @@ -2277,350 +2011,6 @@ void Compiler::fgComputeLifeLIR(VARSET_TP& life, BasicBlock* block, VARSET_VALAR } } -#else // LEGACY_BACKEND - -#ifdef _PREFAST_ -#pragma warning(push) -#pragma warning(disable : 21000) // Suppress PREFast warning about overly large function -#endif - -void Compiler::fgComputeLife(VARSET_TP& life, - GenTree* startNode, - GenTree* endNode, - VARSET_VALARG_TP volatileVars, - bool* pStmtInfoDirty DEBUGARG(bool* treeModf)) -{ - GenTree* tree; - unsigned lclNum; - - GenTree* gtQMark = NULL; // current GT_QMARK node (walking the trees backwards) - GenTree* nextColonExit = 0; // gtQMark->gtOp.gtOp2 while walking the 'else' branch. - // gtQMark->gtOp.gtOp1 while walking the 'then' branch - - // TBD: This used to be an initialization to VARSET_NOT_ACCEPTABLE. Try to figure out what's going on here. - VARSET_TP entryLiveSet(VarSetOps::MakeFull(this)); // liveness when we see gtQMark - VARSET_TP gtColonLiveSet(VarSetOps::MakeFull(this)); // liveness when we see gtColon - GenTree* gtColon = NULL; - - VARSET_TP keepAliveVars(VarSetOps::Union(this, volatileVars, compCurBB->bbScope)); /* Dont kill vars in scope */ - - noway_assert(VarSetOps::Equal(this, VarSetOps::Intersection(this, keepAliveVars, life), keepAliveVars)); - noway_assert(compCurStmt->gtOper == GT_STMT); - noway_assert(endNode || (startNode == compCurStmt->gtStmt.gtStmtExpr)); - - /* NOTE: Live variable analysis will not work if you try - * to use the result of an assignment node directly */ - - for (tree = startNode; tree != endNode; tree = tree->gtPrev) - { - AGAIN: - /* For ?: nodes if we're done with the then branch, remember - * the liveness */ - if (gtQMark && (tree == gtColon)) - { - VarSetOps::Assign(this, gtColonLiveSet, life); - VarSetOps::Assign(this, gtQMark->gtQmark.gtThenLiveSet, gtColonLiveSet); - } - - /* For ?: nodes if we're done with the else branch - * then set the correct life as the union of the two branches */ - - if (gtQMark && (tree == gtQMark->gtOp.gtOp1)) - { - noway_assert(tree->gtFlags & GTF_RELOP_QMARK); - noway_assert(gtQMark->gtOp.gtOp2->gtOper == GT_COLON); - - GenTree* thenNode = gtColon->AsColon()->ThenNode(); - GenTree* elseNode = gtColon->AsColon()->ElseNode(); - - noway_assert(thenNode && elseNode); - - VarSetOps::Assign(this, gtQMark->gtQmark.gtElseLiveSet, life); - - /* Check if we optimized away the ?: */ - - if (elseNode->IsNothingNode()) - { - if (thenNode->IsNothingNode()) - { - /* This can only happen for VOID ?: */ - noway_assert(gtColon->gtType == TYP_VOID); - -#ifdef DEBUG - if (verbose) - { - printf("BB%02u - Removing dead QMark - Colon ...\n", compCurBB->bbNum); - gtDispTree(gtQMark); - printf("\n"); - } -#endif // DEBUG - - /* Remove the '?:' - keep the side effects in the condition */ - - noway_assert(tree->OperKind() & GTK_RELOP); - - /* Change the node to a NOP */ - - gtQMark->gtBashToNOP(); -#ifdef DEBUG - *treeModf = true; -#endif // DEBUG - - /* Extract and keep the side effects */ - - if (tree->gtFlags & GTF_SIDE_EFFECT) - { - GenTree* sideEffList = NULL; - - gtExtractSideEffList(tree, &sideEffList); - - if (sideEffList) - { - noway_assert(sideEffList->gtFlags & GTF_SIDE_EFFECT); -#ifdef DEBUG - if (verbose) - { - printf("Extracted side effects list from condition...\n"); - gtDispTree(sideEffList); - printf("\n"); - } -#endif // DEBUG - fgUpdateRefCntForExtract(tree, sideEffList); - - /* The NOP node becomes a GT_COMMA holding the side effect list */ - - gtQMark->ChangeOper(GT_COMMA); - gtQMark->gtFlags |= sideEffList->gtFlags & GTF_ALL_EFFECT; - - if (sideEffList->gtOper == GT_COMMA) - { - gtQMark->gtOp.gtOp1 = sideEffList->gtOp.gtOp1; - gtQMark->gtOp.gtOp2 = sideEffList->gtOp.gtOp2; - } - else - { - gtQMark->gtOp.gtOp1 = sideEffList; - gtQMark->gtOp.gtOp2 = gtNewNothingNode(); - } - } - else - { -#ifdef DEBUG - if (verbose) - { - printf("\nRemoving tree "); - printTreeID(tree); - printf(" in BB%02u as useless\n", compCurBB->bbNum); - gtDispTree(tree); - printf("\n"); - } -#endif // DEBUG - fgUpdateRefCntForExtract(tree, NULL); - } - } - - /* If top node without side effects remove it */ - - if ((gtQMark == compCurStmt->gtStmt.gtStmtExpr) && gtQMark->IsNothingNode()) - { - fgRemoveStmt(compCurBB, compCurStmt); - break; - } - - /* Re-link the nodes for this statement */ - - fgSetStmtSeq(compCurStmt); - - /* Continue analysis from this node */ - - tree = gtQMark; - - /* As the 'then' and 'else' branches are emtpy, liveness - should not have changed */ - - noway_assert(VarSetOps::Equal(this, life, entryLiveSet)); - goto SKIP_QMARK; - } - else - { - // The 'else' branch is empty and the 'then' branch is non-empty - // so swap the two branches and reverse the condition. If one is - // non-empty, we want it to be the 'else' - - GenTree* tmp = thenNode; - - gtColon->AsColon()->ThenNode() = thenNode = elseNode; - gtColon->AsColon()->ElseNode() = elseNode = tmp; - noway_assert(tree == gtQMark->gtOp.gtOp1); - gtReverseCond(tree); - - // Remember to also swap the live sets of the two branches. - const VARSET_TP& tmpVS(gtQMark->gtQmark.gtElseLiveSet); - VarSetOps::AssignNoCopy(this, gtQMark->gtQmark.gtElseLiveSet, gtQMark->gtQmark.gtThenLiveSet); - VarSetOps::AssignNoCopy(this, gtQMark->gtQmark.gtThenLiveSet, tmpVS); - - /* Re-link the nodes for this statement */ - - fgSetStmtSeq(compCurStmt); - } - } - - /* Variables in the two branches that are live at the split - * must interfere with each other */ - - fgMarkIntf(life, gtColonLiveSet); - - /* The live set at the split is the union of the two branches */ - - VarSetOps::UnionD(this, life, gtColonLiveSet); - - SKIP_QMARK: - - /* We are out of the parallel branches, the rest is sequential */ - - gtQMark = NULL; - } - - if (tree->gtOper == GT_CALL) - { - fgComputeLifeCall(life, tree->AsCall()); - continue; - } - - // Is this a use/def of a local variable? - // Generally, the last use information is associated with the lclVar node. - // However, for LEGACY_BACKEND, the information must be associated - // with the OBJ itself for promoted structs. - // In that case, the LDOBJ may be require an implementation that might itself allocate registers, - // so the variable(s) should stay live until the end of the LDOBJ. - // Note that for promoted structs lvTracked is false. - - GenTree* lclVarTree = nullptr; - if (tree->gtOper == GT_OBJ) - { - // fgIsIndirOfAddrOfLocal returns nullptr if the tree is - // not an indir(addr(local)), in which case we will set lclVarTree - // back to the original tree, and not handle it as a use/def. - lclVarTree = fgIsIndirOfAddrOfLocal(tree); - if ((lclVarTree != nullptr) && lvaTable[lclVarTree->gtLclVarCommon.gtLclNum].lvTracked) - { - lclVarTree = nullptr; - } - } - if (lclVarTree == nullptr) - { - lclVarTree = tree; - } - - if (lclVarTree->OperIsNonPhiLocal() || lclVarTree->OperIsLocalAddr()) - { - bool isDeadStore = fgComputeLifeLocal(life, keepAliveVars, lclVarTree, tree); - if (isDeadStore) - { - LclVarDsc* varDsc = &lvaTable[lclVarTree->gtLclVarCommon.gtLclNum]; - - bool doAgain = false; - if (fgRemoveDeadStore(&tree, varDsc, life, &doAgain, pStmtInfoDirty DEBUGARG(treeModf))) - { - assert(!doAgain); - break; - } - - if (doAgain) - { - goto AGAIN; - } - } - } - else - { - if (tree->gtOper == GT_QMARK && tree->gtOp.gtOp1) - { - /* Special cases - "? :" operators. - - The trees are threaded as shown below with nodes 1 to 11 linked - by gtNext. Both GT_->gtLiveSet and GT_COLON->gtLiveSet are - the union of the liveness on entry to thenTree and elseTree. - - +--------------------+ - | GT_QMARK 11| - +----------+---------+ - | - * - / \ - / \ - / \ - +---------------------+ +--------------------+ - | GT_ 3 | | GT_COLON 7 | - | w/ GTF_RELOP_QMARK | | w/ GTF_COLON_COND | - +----------+----------+ +---------+----------+ - | | - * * - / \ / \ - / \ / \ - / \ / \ - 2 1 thenTree 6 elseTree 10 - x | | - / * * - +----------------+ / / \ / \ - |prevExpr->gtNext+------/ / \ / \ - +----------------+ / \ / \ - 5 4 9 8 - - */ - - noway_assert(tree->gtOp.gtOp1->OperKind() & GTK_RELOP); - noway_assert(tree->gtOp.gtOp1->gtFlags & GTF_RELOP_QMARK); - noway_assert(tree->gtOp.gtOp2->gtOper == GT_COLON); - - if (gtQMark) - { - /* This is a nested QMARK sequence - we need to use recursion. - * Compute the liveness for each node of the COLON branches - * The new computation starts from the GT_QMARK node and ends - * when the COLON branch of the enclosing QMARK ends */ - - noway_assert(nextColonExit && - (nextColonExit == gtQMark->gtOp.gtOp1 || nextColonExit == gtQMark->gtOp.gtOp2)); - - fgComputeLife(life, tree, nextColonExit, volatileVars, pStmtInfoDirty DEBUGARG(treeModf)); - - /* Continue with exit node (the last node in the enclosing colon branch) */ - - tree = nextColonExit; - goto AGAIN; - } - else - { - gtQMark = tree; - VarSetOps::Assign(this, entryLiveSet, life); - gtColon = gtQMark->gtOp.gtOp2; - nextColonExit = gtColon; - } - } - - /* If found the GT_COLON, start the new branch with the original life */ - - if (gtQMark && tree == gtQMark->gtOp.gtOp2) - { - /* The node better be a COLON. */ - noway_assert(tree->gtOper == GT_COLON); - - VarSetOps::Assign(this, life, entryLiveSet); - nextColonExit = gtQMark->gtOp.gtOp1; - } - } - } - - /* Return the set of live variables out of this statement */ -} - -#ifdef _PREFAST_ -#pragma warning(pop) -#endif - -#endif // !LEGACY_BACKEND - // fgRemoveDeadStore - remove a store to a local which has no exposed uses. // // pTree - GenTree** to local, including store-form local or local addr (post-rationalize) @@ -2732,77 +2122,8 @@ bool Compiler::fgRemoveDeadStore(GenTree** pTree, noway_assert(rhsNode); noway_assert(tree->gtFlags & GTF_VAR_DEF); -#ifndef LEGACY_BACKEND assert(asgNode->OperIs(GT_ASG)); -#else - if (asgNode->gtOper != GT_ASG && asgNode->gtOverflowEx()) - { - // asgNode may be = (with GTF_OVERFLOW). In that case, we need to keep the - - // Dead = assignment. We change it to the right operation (taking out the assignment), - // update the flags, update order of statement, as we have changed the order of the operation - // and we start computing life again from the op_ovf node (we go backwards). Note that we - // don't need to update ref counts because we don't change them, we're only changing the - // operation. - CLANG_FORMAT_COMMENT_ANCHOR; - -#ifdef DEBUG - if (verbose) - { - printf("\nChanging dead ovf to ovf...\n"); - } -#endif // DEBUG - - switch (asgNode->gtOper) - { - case GT_ASG_ADD: - asgNode->SetOperRaw(GT_ADD); - break; - case GT_ASG_SUB: - asgNode->SetOperRaw(GT_SUB); - break; - default: - // Only add and sub allowed, we don't have ASG_MUL and ASG_DIV for ints, and - // floats don't allow OVF forms. - noway_assert(!"Unexpected ASG_OP"); - } - - asgNode->gtFlags &= ~GTF_REVERSE_OPS; - if (!((asgNode->gtOp.gtOp1->gtFlags | rhsNode->gtFlags) & GTF_ASG)) - { - asgNode->gtFlags &= ~GTF_ASG; - } - asgNode->gtOp.gtOp1->gtFlags &= ~(GTF_VAR_DEF | GTF_VAR_USEASG); - -#ifdef DEBUG - *treeModf = true; -#endif // DEBUG - // Make sure no previous cousin subtree rooted at a common ancestor has - // asked to defer the recomputation of costs. - if (!*pStmtInfoDirty) - { - /* Update ordering, costs, FP levels, etc. */ - gtSetStmtInfo(compCurStmt); - - /* Re-link the nodes for this statement */ - fgSetStmtSeq(compCurStmt); - - // Start from the old assign node, as we have changed the order of its operands. - // No need to update liveness, as nothing has changed (the target of the asgNode - // either goes dead here, in which case the whole expression is now dead, or it - // was already live). - - // TODO-Throughput: Redo this so that the graph is modified BEFORE traversing it! - // We can determine this case when we first see the asgNode - - *pTree = asgNode; - - *doAgain = true; - } - return false; - } -#endif // Do not remove if this local variable represents // a promoted struct field of an address exposed local. if (varDsc->lvIsStructField && lvaTable[varDsc->lvParentLcl].lvAddrExposed) @@ -3218,15 +2539,17 @@ void Compiler::fgInterBlockLocalVarLiveness() /* Mark any interference we might have at the end of the block */ - fgMarkIntf(life); - - if (!block->IsLIR()) + if (block->IsLIR()) + { + fgComputeLifeLIR(life, block, volatileVars); + } + else { /* Get the first statement in the block */ GenTree* firstStmt = block->FirstNonPhiDef(); - if (!firstStmt) + if (firstStmt == nullptr) { continue; } @@ -3269,14 +2592,6 @@ void Compiler::fgInterBlockLocalVarLiveness() #endif // DEBUG } while (compCurStmt != firstStmt); } - else - { -#ifdef LEGACY_BACKEND - unreached(); -#else // !LEGACY_BACKEND - fgComputeLifeLIR(life, block, volatileVars); -#endif // !LEGACY_BACKEND - } /* Done with the current block - if we removed any statements, some * variables may have become dead at the beginning of the block diff --git a/src/jit/lower.cpp b/src/jit/lower.cpp index ae207f0c9c..531e0da755 100644 --- a/src/jit/lower.cpp +++ b/src/jit/lower.cpp @@ -21,8 +21,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #pragma hdrstop #endif -#ifndef LEGACY_BACKEND // This file is ONLY used for the RyuJIT backend that uses the linear scan register allocator - #include "lower.h" #if !defined(_TARGET_64BIT_) @@ -144,7 +142,7 @@ GenTree* Lowering::LowerNode(GenTree* node) case GT_MUL: case GT_MULHI: -#if defined(_TARGET_X86_) && !defined(LEGACY_BACKEND) +#if defined(_TARGET_X86_) case GT_MUL_LONG: #endif ContainCheckMul(node->AsOp()); @@ -562,7 +560,6 @@ GenTree* Lowering::LowerSwitch(GenTree* node) // I think this is due to the fact that we use absolute addressing // instead of relative. But in CoreRT is used as a rule relative // addressing when we generate an executable. - // This bug is also present in Legacy JIT. // See also https://github.com/dotnet/coreclr/issues/13194 // Also https://github.com/dotnet/coreclr/pull/13197 useJumpSequence = useJumpSequence || comp->IsTargetAbi(CORINFO_CORERT_ABI); @@ -5985,5 +5982,3 @@ void Lowering::ContainCheckJTrue(GenTreeOp* node) cmp->gtType = TYP_VOID; cmp->gtFlags |= GTF_SET_FLAGS; } - -#endif // !LEGACY_BACKEND diff --git a/src/jit/lowerarm.cpp b/src/jit/lowerarm.cpp deleted file mode 100644 index 8cf5225388..0000000000 --- a/src/jit/lowerarm.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -XX XX -XX Lowering for ARM XX -XX XX -XX This encapsulates all the logic for lowering trees for the ARM XX -XX architecture. For a more detailed view of what is lowering, please XX -XX take a look at Lower.cpp XX -XX XX -XX XX -XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -*/ - -#include "jitpch.h" -#ifdef _MSC_VER -#pragma hdrstop -#endif - -#ifndef LEGACY_BACKEND // This file is ONLY used for the RyuJIT backend that uses the linear scan register allocator - -#ifdef _TARGET_ARM_ - -#include "jit.h" -#include "sideeffects.h" -#include "lower.h" - -#endif // _TARGET_ARM_ - -#endif // !LEGACY_BACKEND diff --git a/src/jit/lowerarm64.cpp b/src/jit/lowerarm64.cpp deleted file mode 100644 index 060a3a9fbb..0000000000 --- a/src/jit/lowerarm64.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -XX XX -XX Lowering for ARM64 XX -XX XX -XX This encapsulates all the logic for lowering trees for the ARM64 XX -XX architecture. For a more detailed view of what is lowering, please XX -XX take a look at Lower.cpp XX -XX XX -XX XX -XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -*/ - -#include "jitpch.h" -#ifdef _MSC_VER -#pragma hdrstop -#endif - -#ifndef LEGACY_BACKEND // This file is ONLY used for the RyuJIT backend that uses the linear scan register allocator - -#ifdef _TARGET_ARM64_ - -#include "jit.h" -#include "sideeffects.h" -#include "lower.h" - -#endif // _TARGET_ARM64_ - -#endif // !LEGACY_BACKEND diff --git a/src/jit/lowerarmarch.cpp b/src/jit/lowerarmarch.cpp index f18793d06c..c8f082b523 100644 --- a/src/jit/lowerarmarch.cpp +++ b/src/jit/lowerarmarch.cpp @@ -20,8 +20,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #pragma hdrstop #endif -#ifndef LEGACY_BACKEND // This file is ONLY used for the RyuJIT backend that uses the linear scan register allocator - #ifdef _TARGET_ARMARCH_ // This file is ONLY used for ARM and ARM64 architectures #include "jit.h" @@ -920,5 +918,3 @@ void Lowering::ContainCheckHWIntrinsic(GenTreeHWIntrinsic* node) #endif // FEATURE_HW_INTRINSICS #endif // _TARGET_ARMARCH_ - -#endif // !LEGACY_BACKEND diff --git a/src/jit/lowerxarch.cpp b/src/jit/lowerxarch.cpp index 8216515c4d..0bf0c241a5 100644 --- a/src/jit/lowerxarch.cpp +++ b/src/jit/lowerxarch.cpp @@ -5,7 +5,7 @@ /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XX XX -XX Lowering for AMD64 XX +XX Lowering for AMD64, x86 XX XX XX XX This encapsulates all the logic for lowering trees for the AMD64 XX XX architecture. For a more detailed view of what is lowering, please XX @@ -21,9 +21,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #pragma hdrstop #endif -#ifndef LEGACY_BACKEND // This file is ONLY used for the RyuJIT backend that uses the linear scan register allocator - -#ifdef _TARGET_XARCH_ +#ifdef _TARGET_XARCH_ // This file is only used for xarch #include "jit.h" #include "sideeffects.h" @@ -2474,5 +2472,3 @@ void Lowering::ContainCheckFloatBinary(GenTreeOp* node) } #endif // _TARGET_XARCH_ - -#endif // !LEGACY_BACKEND diff --git a/src/jit/lsra.cpp b/src/jit/lsra.cpp index 66a665b2b2..bb2711a97d 100644 --- a/src/jit/lsra.cpp +++ b/src/jit/lsra.cpp @@ -49,8 +49,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Tree nodes (GenTree): - GenTree::gtRegNum (and gtRegPair for ARM) is annotated with the register assignment for a node. If the node does not require a register, it is - annotated as such (for single registers, gtRegNum = REG_NA; for register - pair type, gtRegPair = REG_PAIR_NONE). For a variable definition or interior + annotated as such (gtRegNum = REG_NA). For a variable definition or interior tree node (an "implicit" definition), this is the register to put the result. For an expression use, this is the place to find the value that has previously been computed. @@ -97,8 +96,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #pragma hdrstop #endif -#ifndef LEGACY_BACKEND // This file is ONLY used for the RyuJIT backend that uses the linear scan register allocator - #include "lsra.h" #ifdef DEBUG @@ -10467,5 +10464,3 @@ void LinearScan::verifyResolutionMove(GenTree* resolutionMove, LsraLocation curr } } #endif // DEBUG - -#endif // !LEGACY_BACKEND diff --git a/src/jit/lsra.h b/src/jit/lsra.h index 316942be2f..40edb2dee7 100644 --- a/src/jit/lsra.h +++ b/src/jit/lsra.h @@ -796,9 +796,7 @@ public: #ifdef DEBUG private: //------------------------------------------------------------------------ - // Should we stress lsra? - // This uses the same COMPLUS variable as rsStressRegs (COMPlus_JitStressRegs) - // However, the possible values and their interpretation are entirely different. + // Should we stress lsra? This uses the COMPlus_JitStressRegs variable. // // The mask bits are currently divided into fields in which each non-zero value // is a distinct stress option (e.g. 0x3 is not a combination of 0x1 and 0x2). diff --git a/src/jit/lsraarm.cpp b/src/jit/lsraarm.cpp index 05a02381cf..db6cff70d1 100644 --- a/src/jit/lsraarm.cpp +++ b/src/jit/lsraarm.cpp @@ -20,8 +20,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #pragma hdrstop #endif -#ifndef LEGACY_BACKEND // This file is ONLY used for the RyuJIT backend that uses the linear scan register allocator - #ifdef _TARGET_ARM_ #include "jit.h" @@ -802,5 +800,3 @@ void LinearScan::BuildNode(GenTree* tree) } #endif // _TARGET_ARM_ - -#endif // !LEGACY_BACKEND diff --git a/src/jit/lsraarm64.cpp b/src/jit/lsraarm64.cpp index 6c1fc4af29..97547d589e 100644 --- a/src/jit/lsraarm64.cpp +++ b/src/jit/lsraarm64.cpp @@ -20,8 +20,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #pragma hdrstop #endif -#ifndef LEGACY_BACKEND // This file is ONLY used for the RyuJIT backend that uses the linear scan register allocator - #ifdef _TARGET_ARM64_ #include "jit.h" @@ -1008,5 +1006,3 @@ void LinearScan::BuildHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree) #endif #endif // _TARGET_ARM64_ - -#endif // !LEGACY_BACKEND diff --git a/src/jit/lsraarmarch.cpp b/src/jit/lsraarmarch.cpp index 22c96f919a..0331edee23 100644 --- a/src/jit/lsraarmarch.cpp +++ b/src/jit/lsraarmarch.cpp @@ -19,8 +19,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #pragma hdrstop #endif -#ifndef LEGACY_BACKEND // This file is ONLY used for the RyuJIT backend that uses the linear scan register allocator - #ifdef _TARGET_ARMARCH_ // This file is ONLY used for ARM and ARM64 architectures #include "jit.h" @@ -758,5 +756,3 @@ void LinearScan::BuildBlockStore(GenTreeBlk* blkNode) } #endif // _TARGET_ARMARCH_ - -#endif // !LEGACY_BACKEND diff --git a/src/jit/lsrabuild.cpp b/src/jit/lsrabuild.cpp index ffa28d1763..71e7d85264 100644 --- a/src/jit/lsrabuild.cpp +++ b/src/jit/lsrabuild.cpp @@ -20,8 +20,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #pragma hdrstop #endif -#ifndef LEGACY_BACKEND // This file is ONLY used for the RyuJIT backend that uses the linear scan register allocator - #include "lsra.h" //------------------------------------------------------------------------ @@ -744,7 +742,7 @@ regMaskTP LinearScan::getKillSetForNode(GenTree* tree) break; case GT_MULHI: -#if defined(_TARGET_X86_) && !defined(LEGACY_BACKEND) +#if defined(_TARGET_X86_) case GT_MUL_LONG: #endif killMask = RBM_RAX | RBM_RDX; @@ -1292,10 +1290,6 @@ int LinearScan::ComputeAvailableSrcCount(GenTree* node) // void LinearScan::buildRefPositionsForNode(GenTree* tree, BasicBlock* block, LsraLocation currentLoc) { -#ifdef _TARGET_ARM_ - assert(!isRegPairType(tree->TypeGet())); -#endif // _TARGET_ARM_ - // The LIR traversal doesn't visit GT_LIST or GT_ARGPLACE nodes. // GT_CLS_VAR nodes should have been eliminated by rationalizer. assert(tree->OperGet() != GT_ARGPLACE); @@ -3263,5 +3257,3 @@ void LinearScan::BuildCmp(GenTree* tree) info->srcCount = appendBinaryLocationInfoToList(tree->AsOp()); } - -#endif // !LEGACY_BACKEND diff --git a/src/jit/lsraxarch.cpp b/src/jit/lsraxarch.cpp index f7bdf80616..cebe6a13bf 100644 --- a/src/jit/lsraxarch.cpp +++ b/src/jit/lsraxarch.cpp @@ -20,8 +20,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #pragma hdrstop #endif -#ifndef LEGACY_BACKEND // This file is ONLY used for the RyuJIT backend that uses the linear scan register allocator - #ifdef _TARGET_XARCH_ #include "jit.h" @@ -278,7 +276,7 @@ void LinearScan::BuildNode(GenTree* tree) case GT_MUL: case GT_MULHI: -#if defined(_TARGET_X86_) && !defined(LEGACY_BACKEND) +#if defined(_TARGET_X86_) case GT_MUL_LONG: #endif BuildMul(tree->AsOp()); @@ -1839,9 +1837,6 @@ void LinearScan::BuildIntrinsic(GenTree* tree) case CORINFO_INTRINSIC_Round: case CORINFO_INTRINSIC_Ceiling: case CORINFO_INTRINSIC_Floor: -#if defined(LEGACY_BACKEND) - NYI_X86("Math intrinsics Round, Ceiling, and Floor"); -#endif // LEGACY_BACKEND break; default: @@ -2836,5 +2831,3 @@ bool LinearScan::ExcludeNonByteableRegisters(GenTree* tree) #endif // _TARGET_X86_ #endif // _TARGET_XARCH_ - -#endif // !LEGACY_BACKEND diff --git a/src/jit/morph.cpp b/src/jit/morph.cpp index 495eb78ea8..07883d51af 100644 --- a/src/jit/morph.cpp +++ b/src/jit/morph.cpp @@ -74,10 +74,6 @@ GenTree* Compiler::fgMorphIntoHelperCall(GenTree* tree, int helper, GenTreeArgLi tree->gtCall.gtInlineCandidateInfo = nullptr; tree->gtCall.gtControlExpr = nullptr; -#ifdef LEGACY_BACKEND - tree->gtCall.gtCallRegUsedMask = RBM_NONE; -#endif // LEGACY_BACKEND - #if DEBUG // Helper calls are never candidates. @@ -89,7 +85,7 @@ GenTree* Compiler::fgMorphIntoHelperCall(GenTree* tree, int helper, GenTreeArgLi tree->gtCall.gtEntryPoint.accessType = IAT_VALUE; #endif -#if (defined(_TARGET_X86_) || defined(_TARGET_ARM_)) && !defined(LEGACY_BACKEND) +#ifndef _TARGET_64BIT_ if (varTypeIsLong(tree)) { GenTreeCall* callNode = tree->AsCall(); @@ -98,7 +94,7 @@ GenTree* Compiler::fgMorphIntoHelperCall(GenTree* tree, int helper, GenTreeArgLi retTypeDesc->InitializeLongReturnType(this); callNode->ClearOtherRegs(); } -#endif // _TARGET_XXX_ +#endif // !_TARGET_64BIT_ if (tree->OperMayThrow(this)) { @@ -121,20 +117,6 @@ GenTree* Compiler::fgMorphIntoHelperCall(GenTree* tree, int helper, GenTreeArgLi return tree; } -/***************************************************************************** - * - * Determine if a relop must be morphed to a qmark to manifest a boolean value. - * This is done when code generation can't create straight-line code to do it. - */ -bool Compiler::fgMorphRelopToQmark(GenTree* tree) -{ -#ifndef LEGACY_BACKEND - return false; -#else // LEGACY_BACKEND - return (genActualType(tree->TypeGet()) == TYP_LONG) || varTypeIsFloating(tree->TypeGet()); -#endif // LEGACY_BACKEND -} - /***************************************************************************** * * Morph a cast node (we perform some very simple transformations here). @@ -173,8 +155,6 @@ GenTree* Compiler::fgMorphCast(GenTree* tree) // integral types everybody else can get straight there // except for when using helpers if (srcType == TYP_FLOAT -#if !FEATURE_STACK_FP_X87 - #if defined(_TARGET_ARM64_) // Amd64: src = float, dst is overflow conversion. // This goes through helper and hence src needs to be converted to double. @@ -187,8 +167,6 @@ GenTree* Compiler::fgMorphCast(GenTree* tree) // Arm: src = float, dst = int64/uint64 or overflow conversion. && (tree->gtOverflow() || varTypeIsLong(dstType)) #endif - -#endif // FEATURE_STACK_FP_X87 ) { oper = gtNewCastNode(TYP_DOUBLE, oper, false, TYP_DOUBLE); @@ -222,45 +200,24 @@ GenTree* Compiler::fgMorphCast(GenTree* tree) switch (dstType) { case TYP_INT: -#ifdef _TARGET_X86_ // there is no rounding convert to integer instruction on ARM or x64 so skip this -#ifdef LEGACY_BACKEND - // the RyuJIT backend does not use the x87 FPU and therefore - // does not support folding the cast conv.i4(round.d(d)) - if ((oper->gtOper == GT_INTRINSIC) && - (oper->gtIntrinsic.gtIntrinsicId == CORINFO_INTRINSIC_Round)) - { - /* optimization: conv.i4(round.d(d)) -> round.i(d) */ - oper->gtType = dstType; - return fgMorphTree(oper); - } - // if SSE2 is not enabled, we need the helper - else -#endif // LEGACY_BACKEND - if (!opts.compCanUseSSE2) - { - return fgMorphCastIntoHelper(tree, CORINFO_HELP_DBL2INT, oper); - } - else -#endif // _TARGET_X86_ - { - goto OPTIMIZECAST; - } -#if defined(_TARGET_ARM_) || defined(_TARGET_AMD64_) - case TYP_UINT: goto OPTIMIZECAST; -#else // _TARGET_ARM_ + case TYP_UINT: +#if defined(_TARGET_ARM_) || defined(_TARGET_AMD64_) + goto OPTIMIZECAST; +#else // _TARGET_X86_ return fgMorphCastIntoHelper(tree, CORINFO_HELP_DBL2UINT, oper); -#endif // _TARGET_ARM_ +#endif // _TARGET_X86_ -#ifdef _TARGET_AMD64_ - // SSE2 has instructions to convert a float/double directly to a long case TYP_LONG: +#ifdef _TARGET_AMD64_ + // SSE2 has instructions to convert a float/double directly to a long + // TODO-X86-CQ: should this be enabled for x86 also? goto OPTIMIZECAST; -#else - case TYP_LONG: +#else // !_TARGET_AMD64_ return fgMorphCastIntoHelper(tree, CORINFO_HELP_DBL2LNG, oper); -#endif //_TARGET_AMD64_ +#endif // !_TARGET_AMD64_ + case TYP_ULONG: return fgMorphCastIntoHelper(tree, CORINFO_HELP_DBL2ULNG, oper); default: @@ -384,18 +341,14 @@ GenTree* Compiler::fgMorphCast(GenTree* tree) oper = gtNewCastNode(TYP_LONG, oper, true, TYP_LONG); oper->gtFlags |= (tree->gtFlags & (GTF_OVERFLOW | GTF_EXCEPT)); tree->gtFlags &= ~GTF_UNSIGNED; -#ifndef LEGACY_BACKEND return fgMorphCastIntoHelper(tree, CORINFO_HELP_LNG2DBL, oper); -#endif } } -#ifndef LEGACY_BACKEND else if (((tree->gtFlags & GTF_UNSIGNED) == 0) && (srcType == TYP_LONG) && varTypeIsFloating(dstType)) { return fgMorphCastIntoHelper(tree, CORINFO_HELP_LNG2DBL, oper); } -#endif -#endif //_TARGET_XARCH_ +#endif //_TARGET_X86_ else if (varTypeIsGC(srcType) != varTypeIsGC(dstType)) { // We are casting away GC information. we would like to just @@ -743,27 +696,6 @@ OPTIMIZECAST: } break; -#ifdef LEGACY_BACKEND - - /* If op1 is a mod node, mark it with the GTF_MOD_INT_RESULT flag - so that the code generator will know not to convert the result - of the idiv to a regpair */ - case GT_MOD: - if (dstType == TYP_INT) - { - tree->gtOp.gtOp1->gtFlags |= GTF_MOD_INT_RESULT; - } - - break; - case GT_UMOD: - if (dstType == TYP_UINT) - { - tree->gtOp.gtOp1->gtFlags |= GTF_MOD_INT_RESULT; - } - break; - -#endif // LEGACY_BACKEND - case GT_COMMA: // Check for cast of a GT_COMMA with a throw overflow // Bug 110829: Since this optimization will bash the types @@ -787,15 +719,7 @@ OPTIMIZECAST: commaOp2->ChangeOperConst(GT_CNS_DBL); commaOp2->gtDblCon.gtDconVal = 0.0; // Change the types of oper and commaOp2 - // X87 promotes everything to TYP_DOUBLE - // But other's are a little more precise - const var_types newTyp -#if FEATURE_X87_DOUBLES - = TYP_DOUBLE; -#else // FEATURE_X87_DOUBLES - = tree->gtType; -#endif // FEATURE_X87_DOUBLES - oper->gtType = commaOp2->gtType = newTyp; + oper->gtType = commaOp2->gtType = tree->gtType; } else { @@ -1540,13 +1464,13 @@ void fgArgInfo::ArgsComplete() continue; #endif } -#if defined(_TARGET_ARM_) && !defined(LEGACY_BACKEND) +#if defined(_TARGET_ARM_) else if (curArgTabEntry->isSplit) { hasStructRegArg = true; hasStackArgs = true; } -#endif +#endif // _TARGET_ARM_ else // we have a register argument, next we look for a struct type. { if (varTypeIsStruct(argx) UNIX_AMD64_ABI_ONLY(|| curArgTabEntry->isStruct)) @@ -1666,17 +1590,16 @@ void fgArgInfo::ArgsComplete() { prevArgTabEntry->needPlace = true; } -#if defined(_TARGET_ARM_) && !defined(LEGACY_BACKEND) +#if defined(_TARGET_ARM_) else if (prevArgTabEntry->isSplit) { prevArgTabEntry->needPlace = true; } -#endif +#endif // _TARGET_ARM_ #endif } } -#ifndef LEGACY_BACKEND #if FEATURE_MULTIREG_ARGS // For RyuJIT backend we will expand a Multireg arg into a GT_FIELD_LIST // with multiple indirections, so here we consider spilling it into a tmp LclVar. @@ -1765,7 +1688,6 @@ void fgArgInfo::ArgsComplete() } } #endif // FEATURE_MULTIREG_ARGS -#endif // LEGACY_BACKEND } // We only care because we can't spill structs and qmarks involve a lot of spilling, but @@ -1814,7 +1736,7 @@ void fgArgInfo::ArgsComplete() // morphing the register arguments of any GT_IND with a GTF_IND_RNGCHK flag // Thus we can not reorder the argument after any stack based argument // (Note that GT_LCLHEAP sets the GTF_EXCEPT flag so we don't need to - // check for it explicitly + // check for it explicitly.) // if (argx->gtFlags & GTF_EXCEPT) { @@ -2189,7 +2111,7 @@ GenTree* Compiler::fgMakeTmpArgNode(unsigned tmpVarNum UNIX_AMD64_ABI_ONLY_ARG(c if (varTypeIsStruct(type)) { -#if defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_) || (!defined(LEGACY_BACKEND) && defined(_TARGET_ARM_)) +#if defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_) || defined(_TARGET_ARM_) #ifdef UNIX_AMD64_ABI @@ -2257,7 +2179,7 @@ GenTree* Compiler::fgMakeTmpArgNode(unsigned tmpVarNum UNIX_AMD64_ABI_ONLY_ARG(c #endif // FEATURE_MULTIREG_ARGS } -#else // not (_TARGET_AMD64_ or _TARGET_ARM64_ or (!LEGACY_BACKEND and _TARGET_ARM_)) +#else // not (_TARGET_AMD64_ or _TARGET_ARM64_ or _TARGET_ARM_) // other targets, we pass the struct by value assert(varTypeIsStruct(type)); @@ -2268,7 +2190,7 @@ GenTree* Compiler::fgMakeTmpArgNode(unsigned tmpVarNum UNIX_AMD64_ABI_ONLY_ARG(c // gtNewObjNode will set the GTF_EXCEPT flag if this is not a local stack object. arg = gtNewObjNode(lvaGetStruct(tmpVarNum), addrNode); -#endif // not (_TARGET_AMD64_ or _TARGET_ARM64_ or (!LEGACY_BACKEND and _TARGET_ARM_)) +#endif // not (_TARGET_AMD64_ or _TARGET_ARM64_ or _TARGET_ARM_) } // (varTypeIsStruct(type)) @@ -2391,7 +2313,6 @@ void fgArgInfo::EvalArgsToTemps() LclVarDsc* varDsc = compiler->lvaTable + tmpVarNum; -#ifndef LEGACY_BACKEND if (compiler->fgOrder == Compiler::FGOrderLinear) { // We'll reference this temporary variable just once @@ -2399,7 +2320,6 @@ void fgArgInfo::EvalArgsToTemps() // setting up this argument. varDsc->lvRefCnt = 1; } -#endif // !LEGACY_BACKEND var_types lclVarType = genActualType(argx->gtType); var_types scalarType = TYP_UNKNOWN; @@ -2407,14 +2327,14 @@ void fgArgInfo::EvalArgsToTemps() if (setupArg->OperIsCopyBlkOp()) { setupArg = compiler->fgMorphCopyBlock(setupArg); -#if defined(_TARGET_ARM64_) || (!defined(LEGACY_BACKEND) && defined(_TARGET_ARM_)) +#if defined(_TARGET_ARMARCH_) // This scalar LclVar widening step is only performed for ARM architectures. // CORINFO_CLASS_HANDLE clsHnd = compiler->lvaGetStruct(tmpVarNum); unsigned structSize = varDsc->lvExactSize; scalarType = compiler->getPrimitiveTypeForStruct(structSize, clsHnd); -#endif // _TARGET_ARM*_ +#endif // _TARGET_ARMARCH_ } // scalarType can be set to a wider type for ARM architectures: (3 => 4) or (5,6,7 => 8) @@ -2446,9 +2366,6 @@ void fgArgInfo::EvalArgsToTemps() argReg = genRegArgNext(argReg); allUsedRegs |= genRegMask(argReg); } -#ifdef LEGACY_BACKEND - callTree->gtCall.gtCallRegUsedMask |= allUsedRegs; -#endif // LEGACY_BACKEND } #endif // _TARGET_ARM_ } @@ -2833,7 +2750,6 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) bool hasStackArgCopy = false; #endif -#ifndef LEGACY_BACKEND // Data structure for keeping track of non-standard args. Non-standard args are those that are not passed // following the normal calling convention or in the normal argument registers. We either mark existing // arguments as non-standard (such as the x8 return buffer register on ARM64), or we manually insert the @@ -2938,7 +2854,6 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) } } nonStandardArgs(this); -#endif // !LEGACY_BACKEND // Count of args. On first morph, this is counted before we've filled in the arg table. // On remorph, we grab it from the arg table. @@ -2999,7 +2914,6 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) // *********** END NOTE ********* CLANG_FORMAT_COMMENT_ANCHOR; -#if !defined(LEGACY_BACKEND) #if defined(_TARGET_X86_) || defined(_TARGET_ARM_) // The x86 and arm32 CORINFO_HELP_INIT_PINVOKE_FRAME helpers has a custom calling convention. // Set the argument registers correctly here. @@ -3173,7 +3087,6 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) } #endif // FEATURE_READYTORUN_COMPILER && _TARGET_ARMARCH_ -#endif // !LEGACY_BACKEND // Allocate the fgArgInfo for the call node; // @@ -3339,12 +3252,10 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) } #endif // FEATURE_MULTIREG_ARGS -#ifndef LEGACY_BACKEND // Record the index of any nonStandard arg that we may be processing here, as we are // about to call fgMorphTree on it and fgMorphTree may replace it with a new tree. GenTree* orig_argx = *parentArgx; int nonStandard_index = nonStandardArgs.Find(orig_argx); -#endif // !LEGACY_BACKEND argx = fgMorphTree(*parentArgx); *parentArgx = argx; @@ -3352,14 +3263,12 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) assert(args->OperIsList()); assert(argx == args->Current()); -#ifndef LEGACY_BACKEND if ((nonStandard_index != -1) && (argx != orig_argx)) { // We need to update the node field for this nonStandard arg here // as it was changed by the call to fgMorphTree nonStandardArgs.Replace(nonStandard_index, argx); } -#endif // !LEGACY_BACKEND /* Change the node to TYP_I_IMPL so we don't report GC info * NOTE: We deferred this from the importer because of the inliner */ @@ -3567,8 +3476,8 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) hasMultiregStructArgs = true; } } -#else // !UNIX_AMD64_ABI - size = 1; // On AMD64, all primitives fit in a single (64-bit) 'slot' +#else // !UNIX_AMD64_ABI + size = 1; // On AMD64, all primitives fit in a single (64-bit) 'slot' #endif // UNIX_AMD64_ABI #elif defined(_TARGET_ARM64_) if (isStructArg) @@ -3833,18 +3742,14 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) { fgAddSkippedRegsInPromotedStructArg(varDsc, intArgRegNum, &argSkippedRegMask); } -#if !defined(LEGACY_BACKEND) copyBlkClass = objClass; -#endif } } -#if !defined(LEGACY_BACKEND) if (structSize < TARGET_POINTER_SIZE) { copyBlkClass = objClass; } -#endif #endif // _TARGET_ARM_ } #ifndef UNIX_AMD64_ABI @@ -4162,7 +4067,6 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) isRegArg = false; } -#ifndef LEGACY_BACKEND // If there are nonstandard args (outside the calling convention) they were inserted above // and noted them in a table so we can recognize them here and build their argInfo. // @@ -4186,7 +4090,6 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) } } #endif // defined(_TARGET_X86_) -#endif // !LEGACY_BACKEND } // end !reMorphing // @@ -4378,7 +4281,6 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) #endif } -#ifndef LEGACY_BACKEND if (argx->gtOper == GT_MKREFANY) { // 'Lower' the MKREFANY tree and insert it. @@ -4423,9 +4325,8 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) lvaSetVarAddrExposed(tmp); #endif // !_TARGET_X86_ } -#endif // !LEGACY_BACKEND -#if defined(_TARGET_X86_) && !defined(LEGACY_BACKEND) +#if defined(_TARGET_X86_) if (isStructArg) { GenTree* lclNode = fgIsIndirOfAddrOfLocal(argx); @@ -4460,7 +4361,7 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) } } } -#endif // _TARGET_X86_ && !LEGACY_BACKEND +#endif // _TARGET_X86_ #ifdef UNIX_AMD64_ABI if (isStructArg && !isRegArg) @@ -4478,19 +4379,6 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) if (!reMorphing) { call->fgArgInfo->ArgsComplete(); - -#ifdef LEGACY_BACKEND - call->gtCallRegUsedMask = genIntAllRegArgMask(intArgRegNum); -#if defined(_TARGET_ARM_) - call->gtCallRegUsedMask &= ~argSkippedRegMask; -#endif - if (fltArgRegNum > 0) - { -#if defined(_TARGET_ARM_) - call->gtCallRegUsedMask |= genFltAllRegArgMask(fltArgRegNum) & ~fltArgSkippedRegMask; -#endif - } -#endif // LEGACY_BACKEND } if (call->gtCallArgs) @@ -4611,17 +4499,12 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) #else // !UNIX_AMD64_ABI -#ifndef LEGACY_BACKEND // In the future we can migrate UNIX_AMD64 to use this // method instead of fgMorphSystemVStructArgs - - // We only require morphing of structs that may be passed in multiple registers - // for the RyuJIT backend. if (hasMultiregStructArgs) { fgMorphMultiregStructArgs(call); } -#endif // LEGACY_BACKEND #endif // UNIX_AMD64_ABI @@ -6094,7 +5977,6 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree) elemOffs = offsetof(CORINFO_Array, u1Elems); } -#ifndef LEGACY_BACKEND // In minopts, we expand GT_INDEX to GT_IND(GT_INDEX_ADDR) in order to minimize the size of the IR. As minopts // compilation time is roughly proportional to the size of the IR, this helps keep compilation times down. // Furthermore, this representation typically saves on code size in minopts w.r.t. the complete expansion @@ -6142,7 +6024,6 @@ GenTree* Compiler::fgMorphArrayIndex(GenTree* tree) return indir; } -#endif // LEGACY_BACKEND GenTree* arrRef = asIndex->Arr(); GenTree* index = asIndex->Index(); @@ -7894,35 +7775,9 @@ void Compiler::fgMorphTailCall(GenTreeCall* call, void* pfnCopyArgs) // Add the extra VSD parameter if needed if (call->IsVirtualStub()) { -#ifdef LEGACY_BACKEND - GenTree* arg; - if (call->gtCallType == CT_INDIRECT) - { - arg = gtClone(call->gtCallAddr, true); - noway_assert(arg != nullptr); - } - else - { - noway_assert(call->gtCallMoreFlags & GTF_CALL_M_VIRTSTUB_REL_INDIRECT); - ssize_t addr = ssize_t(call->gtStubCallStubAddr); - arg = gtNewIconHandleNode(addr, GTF_ICON_FTN_ADDR); - - // Change the call type, so we can add the extra indirection here, rather than in codegen - call->gtCallAddr = gtNewIconHandleNode(addr, GTF_ICON_FTN_ADDR); - call->gtStubCallStubAddr = NULL; - call->gtCallType = CT_INDIRECT; - } - arg->gtRegNum = virtualStubParamInfo->GetReg(); - // Add the extra indirection to generate the real target - call->gtCallAddr = gtNewOperNode(GT_IND, TYP_I_IMPL, call->gtCallAddr); - call->gtFlags |= GTF_EXCEPT; - call->gtCallArgs = gtNewListNode(arg, call->gtCallArgs); - -#else // !LEGACY_BACKEND GenTree* stubAddrArg = fgGetStubAddrArg(call); // And push the stub address onto the list of arguments call->gtCallArgs = gtNewListNode(stubAddrArg, call->gtCallArgs); -#endif // !LEGACY_BACKEND } else if (call->IsVirtualVtable()) { @@ -7975,13 +7830,8 @@ void Compiler::fgMorphTailCall(GenTreeCall* call, void* pfnCopyArgs) call->gtFlags |= GTF_EXCEPT; } -// Now inject a placeholder for the real call target that codegen will generate -#ifdef LEGACY_BACKEND - GenTree* arg = new (this, GT_NOP) GenTreeOp(GT_NOP, TYP_I_IMPL); - codeGen->genMarkTreeInReg(arg, REG_TAILCALL_ADDR); -#else // !LEGACY_BACKEND - GenTree* arg = gtNewIconNode(0, TYP_I_IMPL); -#endif // !LEGACY_BACKEND + // Now inject a placeholder for the real call target that codegen will generate + GenTree* arg = gtNewIconNode(0, TYP_I_IMPL); call->gtCallArgs = gtNewListNode(arg, call->gtCallArgs); // Lastly inject the pointer for the copy routine @@ -7989,17 +7839,11 @@ void Compiler::fgMorphTailCall(GenTreeCall* call, void* pfnCopyArgs) arg = gtNewIconHandleNode(ssize_t(pfnCopyArgs), GTF_ICON_FTN_ADDR); call->gtCallArgs = gtNewListNode(arg, call->gtCallArgs); -// It is now a varargs tail call -#ifdef LEGACY_BACKEND - call->gtCallMoreFlags = GTF_CALL_M_VARARGS | GTF_CALL_M_TAILCALL; -#else // !LEGACY_BACKEND + // It is now a varargs tail call call->gtCallMoreFlags |= GTF_CALL_M_VARARGS | GTF_CALL_M_TAILCALL | GTF_CALL_M_TAILCALL_VIA_HELPER; -#endif // !LEGACY_BACKEND call->gtFlags &= ~GTF_CALL_POP_ARGS; -#elif defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND) - - // x86 classic codegen doesn't require any morphing +#elif defined(_TARGET_XARCH_) // For the helper-assisted tail calls, we need to push all the arguments // into a single list, and then add a few extra at the beginning or end. @@ -8714,7 +8558,6 @@ GenTree* Compiler::fgMorphCall(GenTreeCall* call) { szFailReason = "Opportunistic tail call cannot be dispatched as epilog+jmp"; } -#ifndef LEGACY_BACKEND else if (!call->IsVirtualStub() && call->HasNonStandardAddedArgs(this)) { // If we are here, it means that the call is an explicitly ".tail" prefixed and cannot be @@ -8741,7 +8584,6 @@ GenTree* Compiler::fgMorphCall(GenTreeCall* call) szFailReason = "Non-qualified fast tail call"; } #endif -#endif // LEGACY_BACKEND } } @@ -9709,21 +9551,12 @@ GenTree* Compiler::fgMorphOneAsgBlockOp(GenTree* tree) } else { -#ifndef LEGACY_BACKEND - - // The source argument of the copyblk can potentially - // be accessed only through indir(addr(lclVar)) - // or indir(lclVarAddr) in rational form and liveness - // won't account for these uses. That said, - // we have to mark this local as address exposed so - // we don't delete it as a dead store later on. + // The source argument of the copyblk can potentially be accessed only through indir(addr(lclVar)) + // or indir(lclVarAddr) in rational form and liveness won't account for these uses. That said, + // we have to mark this local as address exposed so we don't delete it as a dead store later on. unsigned lclVarNum = lclVarTree->gtLclVarCommon.gtLclNum; lvaTable[lclVarNum].lvAddrExposed = true; lvaSetVarDoNotEnregister(lclVarNum DEBUGARG(DNER_AddrExposed)); - -#else // LEGACY_BACKEND - lvaSetVarDoNotEnregister(lclVarTree->gtLclVarCommon.gtLclNum DEBUGARG(DNER_LocalField)); -#endif // LEGACY_BACKEND GenTree* srcAddr; if (src == lclVarTree) { @@ -10343,7 +10176,7 @@ GenTree* Compiler::fgMorphBlkNode(GenTree* tree, bool isDest) // // Notes: // This does the following: -// - Ensures that a struct operand is a block node or (for non-LEGACY_BACKEND) lclVar. +// - Ensures that a struct operand is a block node or lclVar. // - Ensures that any COMMAs are above ADDR nodes. // Although 'tree' WAS an operand of a block assignment, the assignment // may have been retyped to be a scalar assignment. @@ -10416,14 +10249,12 @@ GenTree* Compiler::fgMorphBlockOperand(GenTree* tree, var_types asgType, unsigne LclVarDsc* varDsc = &(lvaTable[lclNode->gtLclNum]); if (varTypeIsStruct(varDsc) && (varDsc->lvExactSize == blockWidth) && (varDsc->lvType == asgType)) { -#ifndef LEGACY_BACKEND if (effectiveVal != lclNode) { JITDUMP("Replacing block node [%06d] with lclVar V%02u\n", dspTreeID(tree), lclNode->gtLclNum); effectiveVal = lclNode; } needsIndirection = false; -#endif // !LEGACY_BACKEND } else { @@ -10499,7 +10330,7 @@ GenTree* Compiler::fgMorphBlockOperand(GenTree* tree, var_types asgType, unsigne // void Compiler::fgMorphUnsafeBlk(GenTreeObj* dest) { -#if defined(CPBLK_UNROLL_LIMIT) && !defined(JIT32_GCENCODER) && !defined(LEGACY_BACKEND) +#if defined(CPBLK_UNROLL_LIMIT) && !defined(JIT32_GCENCODER) assert(dest->gtGcPtrCount != 0); unsigned blockWidth = dest->AsBlk()->gtBlkSize; #ifdef DEBUG @@ -10975,14 +10806,6 @@ GenTree* Compiler::fgMorphCopyBlock(GenTree* tree) rhs = fgMorphBlockOperand(rhs, asgType, blockWidth, false /*!isDest*/); asg->gtOp.gtOp2 = rhs; -#ifdef LEGACY_BACKEND - if (!rhs->OperIsIndir()) - { - noway_assert(rhs->gtOper == GT_LCL_VAR); - GenTree* rhsAddr = gtNewOperNode(GT_ADDR, TYP_BYREF, rhs); - rhs = gtNewIndir(TYP_STRUCT, rhsAddr); - } -#endif // LEGACY_BACKEND // Formerly, liveness did not consider copyblk arguments of simple types as being // a use or def, so these variables were marked as address-exposed. // TODO-1stClassStructs: This should no longer be needed. @@ -11741,25 +11564,6 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac) } #endif -#ifdef LEGACY_BACKEND - __fallthrough; - - case GT_ASG_ADD: - case GT_ASG_SUB: - case GT_ASG_MUL: - case GT_ASG_DIV: - case GT_ASG_MOD: - case GT_ASG_UDIV: - case GT_ASG_UMOD: - case GT_ASG_OR: - case GT_ASG_XOR: - case GT_ASG_AND: - case GT_ASG_LSH: - case GT_ASG_RSH: - case GT_ASG_RSZ: - case GT_CHS: -#endif - // We can't CSE the LHS of an assignment. Only r-values can be CSEed. // Previously, the "lhs" (addr) of a block op was CSE'd. So, to duplicate the former // behavior, allow CSE'ing if is a struct type (or a TYP_REF transformed from a struct type) @@ -11953,11 +11757,7 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac) } #if USE_HELPERS_FOR_INT_DIV - if (typ == TYP_INT -#if defined(LEGACY_BACKEND) - && !fgIsSignedDivOptimizable(op2) -#endif // LEGACY_BACKEND - ) + if (typ == TYP_INT) { helper = CORINFO_HELP_DIV; goto USE_HELPER_FOR_ARITH; @@ -11965,12 +11765,10 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac) #endif #endif // !_TARGET_64BIT_ -#ifndef LEGACY_BACKEND if (op2->gtOper == GT_CAST && op2->gtOp.gtOp1->IsCnsIntOrI()) { op2 = gtFoldExprConst(op2); } -#endif // !LEGACY_BACKEND break; case GT_UDIV: @@ -11982,11 +11780,7 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac) goto USE_HELPER_FOR_ARITH; } #if USE_HELPERS_FOR_INT_DIV - if (typ == TYP_INT -#if defined(LEGACY_BACKEND) - && !fgIsUnsignedDivOptimizable(op2) -#endif // LEGACY_BACKEND - ) + if (typ == TYP_INT) { helper = CORINFO_HELP_UDIV; goto USE_HELPER_FOR_ARITH; @@ -12101,20 +11895,12 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac) #if USE_HELPERS_FOR_INT_DIV if (typ == TYP_INT) { - if (oper == GT_UMOD -#if defined(LEGACY_BACKEND) - && !fgIsUnsignedModOptimizable(op2) -#endif // LEGACY_BACKEND - ) + if (oper == GT_UMOD) { helper = CORINFO_HELP_UMOD; goto USE_HELPER_FOR_ARITH; } - else if (oper == GT_MOD -#if defined(LEGACY_BACKEND) - && !fgIsSignedModOptimizable(op2) -#endif // LEGACY_BACKEND - ) + else if (oper == GT_MOD) { helper = CORINFO_HELP_MOD; goto USE_HELPER_FOR_ARITH; @@ -12123,7 +11909,6 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac) #endif #endif // !_TARGET_64BIT_ -#ifndef LEGACY_BACKEND if (op2->gtOper == GT_CAST && op2->gtOp.gtOp1->IsCnsIntOrI()) { op2 = gtFoldExprConst(op2); @@ -12188,7 +11973,6 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac) } } #endif // !_TARGET_ARM64_ -#endif // !LEGACY_BACKEND break; USE_HELPER_FOR_ARITH: @@ -12781,24 +12565,6 @@ DONE_MORPHING_CHILDREN: } fgAssignSetVarDef(tree); -#ifdef LEGACY_BACKEND - __fallthrough; - - case GT_ASG_ADD: - case GT_ASG_SUB: - case GT_ASG_MUL: - case GT_ASG_DIV: - case GT_ASG_MOD: - case GT_ASG_UDIV: - case GT_ASG_UMOD: - case GT_ASG_OR: - case GT_ASG_XOR: - case GT_ASG_AND: - case GT_ASG_LSH: - case GT_ASG_RSH: - case GT_ASG_RSZ: -#endif - /* We can't CSE the LHS of an assignment */ /* We also must set in the pre-morphing phase, otherwise assertionProp doesn't see it */ if (op1->IsLocal() || (op1->TypeGet() != TYP_STRUCT)) @@ -13296,161 +13062,8 @@ DONE_MORPHING_CHILDREN: COMPARE: noway_assert(tree->OperKind() & GTK_RELOP); - -#ifdef LEGACY_BACKEND - /* Check if the result of the comparison is used for a jump. - * If not then only the int (i.e. 32 bit) case is handled in - * the code generator through the (x86) "set" instructions. - * For the rest of the cases, the simplest way is to - * "simulate" the comparison with ?: - * - * On ARM, we previously used the IT instruction, but the IT instructions - * have mostly been declared obsolete and off-limits, so all cases on ARM - * get converted to ?: */ - - if (!(tree->gtFlags & GTF_RELOP_JMP_USED) && fgMorphRelopToQmark(op1)) - { - /* We convert it to "(CMP_TRUE) ? (1):(0)" */ - - op1 = tree; - op1->gtFlags |= (GTF_RELOP_JMP_USED | GTF_RELOP_QMARK | GTF_DONT_CSE); - op1->gtRequestSetFlags(); - - op2 = new (this, GT_COLON) GenTreeColon(TYP_INT, gtNewIconNode(1), gtNewIconNode(0)); - op2 = fgMorphTree(op2); - - tree = gtNewQmarkNode(TYP_INT, op1, op2); - - fgMorphTreeDone(tree); - - return tree; - } -#endif // LEGACY_BACKEND break; -#ifdef LEGACY_BACKEND - case GT_QMARK: - { - /* If op1 is a comma throw node then we won't be keeping op2 */ - if (fgIsCommaThrow(op1)) - { - break; - } - - /* Get hold of the two branches */ - - noway_assert(op2->OperGet() == GT_COLON); - GenTree* thenNode = op2->AsColon()->ThenNode(); - GenTree* elseNode = op2->AsColon()->ElseNode(); - - /* Try to hoist assignments out of qmark colon constructs. - ie. replace (cond?(x=a):(x=b)) with (x=(cond?a:b)). */ - - if (tree->TypeGet() == TYP_VOID && thenNode->OperGet() == GT_ASG && elseNode->OperGet() == GT_ASG && - thenNode->TypeGet() != TYP_LONG && GenTree::Compare(thenNode->gtOp.gtOp1, elseNode->gtOp.gtOp1) && - thenNode->gtOp.gtOp2->TypeGet() == elseNode->gtOp.gtOp2->TypeGet()) - { - noway_assert(thenNode->TypeGet() == elseNode->TypeGet()); - - GenTree* asg = thenNode; - GenTree* colon = op2; - colon->gtOp.gtOp1 = thenNode->gtOp.gtOp2; - colon->gtOp.gtOp2 = elseNode->gtOp.gtOp2; - tree->gtType = colon->gtType = asg->gtOp.gtOp2->gtType; - asg->gtOp.gtOp2 = tree; - - // Asg will have all the flags that the QMARK had - asg->gtFlags |= (tree->gtFlags & GTF_ALL_EFFECT); - - // Colon flag won't have the flags that x had. - colon->gtFlags &= ~GTF_ALL_EFFECT; - colon->gtFlags |= (colon->gtOp.gtOp1->gtFlags | colon->gtOp.gtOp2->gtFlags) & GTF_ALL_EFFECT; - - DEBUG_DESTROY_NODE(elseNode->gtOp.gtOp1); - DEBUG_DESTROY_NODE(elseNode); - - return asg; - } - - /* If the 'else' branch is empty swap the two branches and reverse the condition */ - - if (elseNode->IsNothingNode()) - { - /* This can only happen for VOID ?: */ - noway_assert(op2->gtType == TYP_VOID); - - /* If the thenNode and elseNode are both nop nodes then optimize away the QMARK */ - if (thenNode->IsNothingNode()) - { - // We may be able to throw away op1 (unless it has side-effects) - - if ((op1->gtFlags & GTF_SIDE_EFFECT) == 0) - { - /* Just return a a Nop Node */ - return thenNode; - } - else - { - /* Just return the relop, but clear the special flags. Note - that we can't do that for longs and floats (see code under - COMPARE label above) */ - - if (!fgMorphRelopToQmark(op1->gtOp.gtOp1)) - { - op1->gtFlags &= ~(GTF_RELOP_QMARK | GTF_RELOP_JMP_USED); - return op1; - } - } - } - else - { - GenTree* tmp = elseNode; - - op2->AsColon()->ElseNode() = elseNode = thenNode; - op2->AsColon()->ThenNode() = thenNode = tmp; - gtReverseCond(op1); - } - } - -#if !defined(_TARGET_ARM_) - // If we have (cond)?0:1, then we just return "cond" for TYP_INTs - // - // Don't do this optimization for ARM: we always require assignment - // to boolean to remain ?:, since we don't have any way to generate - // this with straight-line code, like x86 does using setcc (at least - // after the IT instruction is deprecated). - - if (genActualType(op1->gtOp.gtOp1->gtType) == TYP_INT && genActualType(typ) == TYP_INT && - thenNode->gtOper == GT_CNS_INT && elseNode->gtOper == GT_CNS_INT) - { - ival1 = thenNode->gtIntCon.gtIconVal; - ival2 = elseNode->gtIntCon.gtIconVal; - - // Is one constant 0 and the other 1? - if ((ival1 | ival2) == 1 && (ival1 & ival2) == 0) - { - // If the constants are {1, 0}, reverse the condition - if (ival1 == 1) - { - gtReverseCond(op1); - } - - // Unmark GTF_RELOP_JMP_USED on the condition node so it knows that it - // needs to materialize the result as a 0 or 1. - noway_assert(op1->gtFlags & (GTF_RELOP_QMARK | GTF_RELOP_JMP_USED)); - op1->gtFlags &= ~(GTF_RELOP_QMARK | GTF_RELOP_JMP_USED); - - DEBUG_DESTROY_NODE(tree); - DEBUG_DESTROY_NODE(op2); - - return op1; - } - } -#endif // !_TARGET_ARM_ - } - break; // end case GT_QMARK -#endif // LEGACY_BACKEND - case GT_MUL: #ifndef _TARGET_64BIT_ @@ -13813,9 +13426,6 @@ DONE_MORPHING_CHILDREN: break; -#ifdef LEGACY_BACKEND - case GT_CHS: -#endif case GT_NOT: case GT_NEG: @@ -14652,11 +14262,6 @@ GenTree* Compiler::fgMorphSmpOpOptional(GenTreeOp* tree) switch (oper) { -#ifdef LEGACY_BACKEND - genTreeOps cmop; - bool dstIsSafeLclVar; -#endif - case GT_ASG: if (varTypeIsStruct(typ) && !tree->IsPhiDefn()) { @@ -14683,45 +14288,14 @@ GenTree* Compiler::fgMorphSmpOpOptional(GenTreeOp* tree) break; } -#ifdef LEGACY_BACKEND - /* We'll convert "a = a x" into "a = x" */ - /* and also "a = x a" into "a = x" for communative ops */ - - /* Are we assigning to a GT_LCL_VAR ? */ - - dstIsSafeLclVar = (op1->gtOper == GT_LCL_VAR); - - /* If we have a GT_LCL_VAR, then is the address taken? */ - if (dstIsSafeLclVar) + if (op2->gtFlags & GTF_ASG) { - unsigned lclNum = op1->gtLclVarCommon.gtLclNum; - LclVarDsc* varDsc = lvaTable + lclNum; - - noway_assert(lclNum < lvaCount); - - /* Is the address taken? */ - if (varDsc->lvAddrExposed) - { - dstIsSafeLclVar = false; - } - else if (op2->gtFlags & GTF_ASG) - { - break; - } + break; } - if (!dstIsSafeLclVar) -#endif // LEGACY_BACKEND + if ((op2->gtFlags & GTF_CALL) && (op1->gtFlags & GTF_ALL_EFFECT)) { - if (op2->gtFlags & GTF_ASG) - { - break; - } - - if ((op2->gtFlags & GTF_CALL) && (op1->gtFlags & GTF_ALL_EFFECT)) - { - break; - } + break; } /* Special case: a cast that can be thrown away */ @@ -14749,264 +14323,6 @@ GenTree* Compiler::fgMorphSmpOpOptional(GenTreeOp* tree) } } -#ifdef LEGACY_BACKEND - /* Make sure we have the operator range right */ - - static_assert(GT_SUB == GT_ADD + 1, "bad oper value"); - static_assert(GT_MUL == GT_ADD + 2, "bad oper value"); - static_assert(GT_DIV == GT_ADD + 3, "bad oper value"); - static_assert(GT_MOD == GT_ADD + 4, "bad oper value"); - static_assert(GT_UDIV == GT_ADD + 5, "bad oper value"); - static_assert(GT_UMOD == GT_ADD + 6, "bad oper value"); - - static_assert(GT_OR == GT_ADD + 7, "bad oper value"); - static_assert(GT_XOR == GT_ADD + 8, "bad oper value"); - static_assert(GT_AND == GT_ADD + 9, "bad oper value"); - - static_assert(GT_LSH == GT_ADD + 10, "bad oper value"); - static_assert(GT_RSH == GT_ADD + 11, "bad oper value"); - static_assert(GT_RSZ == GT_ADD + 12, "bad oper value"); - - /* Check for a suitable operator on the RHS */ - - cmop = op2->OperGet(); - - switch (cmop) - { - case GT_NEG: - // GT_CHS only supported for integer types - if (varTypeIsFloating(tree->TypeGet())) - { - break; - } - - goto ASG_OP; - - case GT_MUL: - // GT_ASG_MUL only supported for floating point types - if (!varTypeIsFloating(tree->TypeGet())) - { - break; - } - - __fallthrough; - - case GT_ADD: - case GT_SUB: - if (op2->gtOverflow()) - { - /* Disable folding into "=" if the result can be - visible to anyone as may throw an exception and - the assignment should not proceed - We are safe with an assignment to a local variables - */ - if (ehBlockHasExnFlowDsc(compCurBB)) - { - break; - } - if (!dstIsSafeLclVar) - { - break; - } - } -#ifndef _TARGET_AMD64_ - // This is hard for byte-operations as we need to make - // sure both operands are in RBM_BYTE_REGS. - if (varTypeIsByte(op2->TypeGet())) - break; -#endif // _TARGET_AMD64_ - goto ASG_OP; - - case GT_DIV: - case GT_UDIV: - // GT_ASG_DIV only supported for floating point types - if (!varTypeIsFloating(tree->TypeGet())) - { - break; - } - - case GT_LSH: - case GT_RSH: - case GT_RSZ: - case GT_OR: - case GT_XOR: - case GT_AND: - ASG_OP: - { - bool bReverse = false; - bool bAsgOpFoldable = fgShouldCreateAssignOp(tree, &bReverse); - if (bAsgOpFoldable) - { - if (bReverse) - { - // We will transform this from "a = x a" to "a = x" - // so we can now destroy the duplicate "a" - DEBUG_DESTROY_NODE(op2->gtOp.gtOp2); - op2->gtOp.gtOp2 = op2->gtOp.gtOp1; - } - - /* Special case: "x |= -1" and "x &= 0" */ - if (((cmop == GT_AND) && op2->gtOp.gtOp2->IsIntegralConst(0)) || - ((cmop == GT_OR) && op2->gtOp.gtOp2->IsIntegralConst(-1))) - { - /* Simply change to an assignment */ - tree->gtOp2 = op2->gtOp.gtOp2; - break; - } - - if (cmop == GT_NEG) - { - /* This is "x = -x;", use the flipsign operator */ - - tree->ChangeOper(GT_CHS); - - if (op1->gtOper == GT_LCL_VAR) - { - op1->gtFlags |= GTF_VAR_USEASG; - } - - tree->gtOp2 = gtNewIconNode(0, op1->TypeGet()); - - break; - } - - if (cmop == GT_RSH && varTypeIsSmall(op1->TypeGet()) && varTypeIsUnsigned(op1->TypeGet())) - { - // Changing from x = x op y to x op= y when x is a small integer type - // makes the op size smaller (originally the op size was 32 bits, after - // sign or zero extension of x, and there is an implicit truncation in the - // assignment). - // This is ok in most cases because the upper bits were - // lost when assigning the op result to a small type var, - // but it may not be ok for the right shift operation where the higher bits - // could be shifted into the lower bits and preserved. - // Signed right shift of signed x still works (i.e. (sbyte)((int)(sbyte)x >>signed y) == - // (sbyte)x >>signed y)) as do unsigned right shift ((ubyte)((int)(ubyte)x >>unsigned y) == - // (ubyte)x >>unsigned y), but signed right shift of an unigned small type may give the - // wrong - // result: - // e.g. (ubyte)((int)(ubyte)0xf0 >>signed 4) == 0x0f, - // but (ubyte)0xf0 >>signed 4 == 0xff which is incorrect. - // The result becomes correct if we use >>unsigned instead of >>signed. - noway_assert(op1->TypeGet() == op2->gtOp.gtOp1->TypeGet()); - cmop = GT_RSZ; - } - - /* Replace with an assignment operator */ - noway_assert(GT_ADD - GT_ADD == GT_ASG_ADD - GT_ASG_ADD); - noway_assert(GT_SUB - GT_ADD == GT_ASG_SUB - GT_ASG_ADD); - noway_assert(GT_OR - GT_ADD == GT_ASG_OR - GT_ASG_ADD); - noway_assert(GT_XOR - GT_ADD == GT_ASG_XOR - GT_ASG_ADD); - noway_assert(GT_AND - GT_ADD == GT_ASG_AND - GT_ASG_ADD); - noway_assert(GT_LSH - GT_ADD == GT_ASG_LSH - GT_ASG_ADD); - noway_assert(GT_RSH - GT_ADD == GT_ASG_RSH - GT_ASG_ADD); - noway_assert(GT_RSZ - GT_ADD == GT_ASG_RSZ - GT_ASG_ADD); - - tree->SetOper((genTreeOps)(cmop - GT_ADD + GT_ASG_ADD)); - tree->gtOp2 = op2->gtOp.gtOp2; - - /* Propagate GTF_OVERFLOW */ - - if (op2->gtOverflowEx()) - { - tree->gtType = op2->gtType; - tree->gtFlags |= (op2->gtFlags & (GTF_OVERFLOW | GTF_EXCEPT | GTF_UNSIGNED)); - } - -#if FEATURE_SET_FLAGS - - /* Propagate GTF_SET_FLAGS */ - if (op2->gtSetFlags()) - { - tree->gtRequestSetFlags(); - } - -#endif // FEATURE_SET_FLAGS - - DEBUG_DESTROY_NODE(op2); - op2 = tree->gtOp2; - - /* The target is used as well as being defined */ - if (op1->OperIsLocal()) - { - op1->gtFlags |= GTF_VAR_USEASG; - } - -#if CPU_HAS_FP_SUPPORT - /* Check for the special case "x += y * x;" */ - - // GT_ASG_MUL only supported for floating point types - if (cmop != GT_ADD && cmop != GT_SUB) - { - break; - } - - if (op2->gtOper == GT_MUL && varTypeIsFloating(tree->TypeGet())) - { - if (GenTree::Compare(op1, op2->gtOp.gtOp1)) - { - /* Change "x += x * y" into "x *= (y + 1)" */ - - op2 = op2->gtOp.gtOp2; - } - else if (GenTree::Compare(op1, op2->gtOp.gtOp2)) - { - /* Change "x += y * x" into "x *= (y + 1)" */ - - op2 = op2->gtOp.gtOp1; - } - else - { - break; - } - - op1 = gtNewDconNode(1.0); - - /* Now make the "*=" node */ - - if (cmop == GT_ADD) - { - /* Change "x += x * y" into "x *= (y + 1)" */ - - tree->gtOp2 = op2 = gtNewOperNode(GT_ADD, tree->TypeGet(), op2, op1); - } - else - { - /* Change "x -= x * y" into "x *= (1 - y)" */ - - noway_assert(cmop == GT_SUB); - tree->gtOp2 = op2 = gtNewOperNode(GT_SUB, tree->TypeGet(), op1, op2); - } - tree->ChangeOper(GT_ASG_MUL); - } -#endif // CPU_HAS_FP_SUPPORT - } - } - - break; - - case GT_NOT: - - /* Is the destination identical to the first RHS sub-operand? */ - - if (GenTree::Compare(op1, op2->gtOp.gtOp1)) - { - /* This is "x = ~x" which is the same as "x ^= -1" - * Transform the node into a GT_ASG_XOR */ - - noway_assert(genActualType(typ) == TYP_INT || genActualType(typ) == TYP_LONG); - - op2->gtOp.gtOp2 = (genActualType(typ) == TYP_INT) ? gtNewIconNode(-1) : gtNewLconNode(-1); - - cmop = GT_XOR; - goto ASG_OP; - } - - break; - default: - break; - } -#endif // LEGACY_BACKEND break; case GT_MUL: @@ -15254,7 +14570,6 @@ bool Compiler::fgOperIsBitwiseRotationRoot(genTreeOps oper) GenTree* Compiler::fgRecognizeAndMorphBitwiseRotation(GenTree* tree) { -#ifndef LEGACY_BACKEND // // Check for a rotation pattern, e.g., // @@ -15476,7 +14791,6 @@ GenTree* Compiler::fgRecognizeAndMorphBitwiseRotation(GenTree* tree) return tree; } } -#endif // LEGACY_BACKEND return tree; } @@ -16647,11 +15961,7 @@ bool Compiler::fgMorphBlockStmt(BasicBlock* block, GenTreeStmt* stmt DEBUGARG(co * for reentrant calls. */ -#ifdef LEGACY_BACKEND -void Compiler::fgMorphStmts(BasicBlock* block, bool* mult, bool* lnot, bool* loadw) -#else void Compiler::fgMorphStmts(BasicBlock* block, bool* lnot, bool* loadw) -#endif { fgRemoveRestOfBlock = false; @@ -16660,9 +15970,6 @@ void Compiler::fgMorphStmts(BasicBlock* block, bool* lnot, bool* loadw) compCurBB = block; *lnot = *loadw = false; -#ifdef LEGACY_BACKEND - *mult = false; -#endif fgCurrentlyInUseArgTemps = hashBv::Create(this); @@ -16830,25 +16137,6 @@ void Compiler::fgMorphStmts(BasicBlock* block, bool* lnot, bool* loadw) { continue; } - -#ifdef LEGACY_BACKEND - /* Note whether we have two or more +=/-= operators in a row */ - - if (tree->gtOper == GT_ASG_ADD || tree->gtOper == GT_ASG_SUB) - { - if (prev && prev->gtOper == tree->gtOper) - { - *mult = true; - } - } - - /* Note "x = a[i] & icon" followed by "x |= a[i] << 8" */ - - if (tree->gtOper == GT_ASG_OR && prev && prev->gtOper == GT_ASG) - { - *loadw = true; - } -#endif // LEGACY_BACKEND } if (fgRemoveRestOfBlock) @@ -16954,10 +16242,6 @@ void Compiler::fgMorphBlocks() do { -#ifdef LEGACY_BACKEND - bool mult = false; -#endif - #if OPT_BOOL_OPS bool lnot = false; #endif @@ -16983,172 +16267,9 @@ void Compiler::fgMorphBlocks() } #endif -/* Process all statement trees in the basic block */ + /* Process all statement trees in the basic block */ -#ifndef LEGACY_BACKEND fgMorphStmts(block, &lnot, &loadw); -#else - fgMorphStmts(block, &mult, &lnot, &loadw); - - if (mult && (opts.compFlags & CLFLG_TREETRANS) && !opts.compDbgCode && !opts.MinOpts()) - { - for (GenTree* tree = block->bbTreeList; tree; tree = tree->gtNext) - { - assert(tree->gtOper == GT_STMT); - GenTree* last = tree->gtStmt.gtStmtExpr; - - if (last->gtOper == GT_ASG_ADD || last->gtOper == GT_ASG_SUB) - { - GenTree* temp; - GenTree* next; - - GenTree* dst1 = last->gtOp.gtOp1; - GenTree* src1 = last->gtOp.gtOp2; - - if (!last->IsCnsIntOrI()) - { - goto NOT_CAFFE; - } - - if (dst1->gtOper != GT_LCL_VAR) - { - goto NOT_CAFFE; - } - if (!src1->IsCnsIntOrI()) - { - goto NOT_CAFFE; - } - - for (;;) - { - GenTree* dst2; - GenTree* src2; - - /* Look at the next statement */ - - temp = tree->gtNext; - if (!temp) - { - goto NOT_CAFFE; - } - - noway_assert(temp->gtOper == GT_STMT); - next = temp->gtStmt.gtStmtExpr; - - if (next->gtOper != last->gtOper) - { - goto NOT_CAFFE; - } - if (next->gtType != last->gtType) - { - goto NOT_CAFFE; - } - - dst2 = next->gtOp.gtOp1; - src2 = next->gtOp.gtOp2; - - if (dst2->gtOper != GT_LCL_VAR) - { - goto NOT_CAFFE; - } - if (dst2->gtLclVarCommon.gtLclNum != dst1->gtLclVarCommon.gtLclNum) - { - goto NOT_CAFFE; - } - - if (!src2->IsCnsIntOrI()) - { - goto NOT_CAFFE; - } - - if (last->gtOverflow() != next->gtOverflow()) - { - goto NOT_CAFFE; - } - - const ssize_t i1 = src1->gtIntCon.gtIconVal; - const ssize_t i2 = src2->gtIntCon.gtIconVal; - const ssize_t itemp = i1 + i2; - - /* if the operators are checking for overflow, check for overflow of the operands */ - - if (next->gtOverflow()) - { - if (next->TypeGet() == TYP_LONG) - { - if (next->gtFlags & GTF_UNSIGNED) - { - ClrSafeInt si1(i1); - if ((si1 + ClrSafeInt(i2)).IsOverflow()) - { - goto NOT_CAFFE; - } - } - else - { - ClrSafeInt si1(i1); - if ((si1 + ClrSafeInt(i2)).IsOverflow()) - { - goto NOT_CAFFE; - } - } - } - else if (next->gtFlags & GTF_UNSIGNED) - { - ClrSafeInt si1(i1); - if ((si1 + ClrSafeInt(i2)).IsOverflow()) - { - goto NOT_CAFFE; - } - } - else - { - ClrSafeInt si1(i1); - if ((si1 + ClrSafeInt(i2)).IsOverflow()) - { - goto NOT_CAFFE; - } - } - } - - /* Fold the two increments/decrements into one */ - - src1->gtIntCon.gtIconVal = itemp; -#ifdef _TARGET_64BIT_ - if (src1->gtType == TYP_INT) - { - src1->AsIntCon()->TruncateOrSignExtend32(); - } -#endif //_TARGET_64BIT_ - - /* Remove the second statement completely */ - - noway_assert(tree->gtNext == temp); - noway_assert(temp->gtPrev == tree); - - if (temp->gtNext) - { - noway_assert(temp->gtNext->gtPrev == temp); - - temp->gtNext->gtPrev = tree; - tree->gtNext = temp->gtNext; - } - else - { - tree->gtNext = nullptr; - - noway_assert(block->bbTreeList->gtPrev == temp); - - block->bbTreeList->gtPrev = tree; - } - } - } - - NOT_CAFFE:; - } - } - -#endif // LEGACY_BACKEND /* Are we using a single return block? */ @@ -17525,14 +16646,7 @@ Compiler::fgWalkResult Compiler::fgAssertNoQmark(GenTree** tree, fgWalkData* dat void Compiler::fgCheckQmarkAllowedForm(GenTree* tree) { assert(tree->OperGet() == GT_QMARK); -#ifndef LEGACY_BACKEND assert(!"Qmarks beyond morph disallowed."); -#else // LEGACY_BACKEND - GenTree* colon = tree->gtOp.gtOp2; - - assert(colon->gtOp.gtOp1->IsIntegralConst(0)); - assert(colon->gtOp.gtOp2->IsIntegralConst(1)); -#endif // LEGACY_BACKEND } /***************************************************************************** @@ -19682,94 +18796,6 @@ bool Compiler::fgNodesMayInterfere(GenTree* write, GenTree* read) } } -#ifdef LEGACY_BACKEND -/** This predicate decides whether we will fold a tree with the structure: - * x = x y where x could be any arbitrary expression into - * x = y. - * - * This modification is only performed when the target architecture supports - * complex addressing modes. In the case of ARM for example, this transformation - * yields no benefit. - * - * In case this functions decides we can proceed to fold into an assignment operator - * we need to inspect whether the operator is commutative to tell fgMorph whether we need to - * reverse the tree due to the fact we saw x = y x and we want to fold that into - * x = y because the operator property. - */ -bool Compiler::fgShouldCreateAssignOp(GenTree* tree, bool* bReverse) -{ -#if CPU_LOAD_STORE_ARCH - /* In the case of a load/store architecture, there's no gain by doing any of this, we bail. */ - return false; -#else - GenTree* op1 = tree->gtOp.gtOp1; - GenTree* op2 = tree->gtGetOp2(); - genTreeOps cmop = op2->OperGet(); - - /* Is the destination identical to the first RHS sub-operand? */ - if (GenTree::Compare(op1, op2->gtOp.gtOp1)) - { - /* - Do not transform the following tree - - [0024CFA4] ----------- const int 1 - [0024CFDC] ----G------ | int - [0024CF5C] ----------- lclVar ubyte V01 tmp0 - [0024D05C] -A--G------ = ubyte - [0024D014] D------N--- lclVar ubyte V01 tmp0 - - to - - [0024CFA4] ----------- const int 1 - [0024D05C] -A--G------ |= ubyte - [0024D014] U------N--- lclVar ubyte V01 tmp0 - - , when V01 is a struct field local. - */ - - if (op1->gtOper == GT_LCL_VAR && varTypeIsSmall(op1->TypeGet()) && op1->TypeGet() != op2->gtOp.gtOp2->TypeGet()) - { - unsigned lclNum = op1->gtLclVarCommon.gtLclNum; - LclVarDsc* varDsc = lvaTable + lclNum; - - if (varDsc->lvIsStructField) - { - return false; - } - } - - *bReverse = false; - return true; - } - else if (GenTree::OperIsCommutative(cmop)) - { - /* For commutative ops only, check for "a = x a" */ - - /* Should we be doing this at all? */ - if ((opts.compFlags & CLFLG_TREETRANS) == 0) - { - return false; - } - - /* Can we swap the operands to cmop ... */ - if ((op2->gtOp.gtOp1->gtFlags & GTF_ALL_EFFECT) && (op2->gtOp.gtOp2->gtFlags & GTF_ALL_EFFECT)) - { - // Both sides must have side effects to prevent swap */ - return false; - } - - /* Is the destination identical to the second RHS sub-operand? */ - if (GenTree::Compare(op1, op2->gtOp.gtOp2)) - { - *bReverse = true; - return true; - } - } - return false; -#endif // !CPU_LOAD_STORE_ARCH -} -#endif // LEGACY_BACKEND - #ifdef FEATURE_SIMD //----------------------------------------------------------------------------------- diff --git a/src/jit/optcse.cpp b/src/jit/optcse.cpp index 6299ae374c..019abd6792 100644 --- a/src/jit/optcse.cpp +++ b/src/jit/optcse.cpp @@ -1935,12 +1935,6 @@ public: no_cse_cost = candidate->UseCount() * candidate->Cost(); yes_cse_cost = (candidate->DefCount() * cse_def_cost) + (candidate->UseCount() * cse_use_cost); -#if CPU_LONG_USES_REGPAIR - if (candidate->Expr()->TypeGet() == TYP_LONG) - { - yes_cse_cost *= 2; - } -#endif no_cse_cost += extra_no_cost; yes_cse_cost += extra_yes_cost; @@ -1999,11 +1993,6 @@ public: // int incr = BB_UNITY_WEIGHT; -#if CPU_LONG_USES_REGPAIR - if (successfulCandidate->Expr()->TypeGet() == TYP_LONG) - incr *= 2; -#endif - if (cseRefCnt > aggressiveRefCnt) { aggressiveRefCnt += incr; diff --git a/src/jit/optimizer.cpp b/src/jit/optimizer.cpp index bd3d29650f..37be0a5b00 100644 --- a/src/jit/optimizer.cpp +++ b/src/jit/optimizer.cpp @@ -3197,16 +3197,10 @@ bool Compiler::optComputeLoopRep(int constInit, switch (iterOper) { -#ifdef LEGACY_BACKEND - case GT_ASG_SUB: -#endif case GT_SUB: iterInc = -iterInc; __fallthrough; -#ifdef LEGACY_BACKEND - case GT_ASG_ADD: -#endif case GT_ADD: if (constInitX != constLimitX) { @@ -3235,13 +3229,6 @@ bool Compiler::optComputeLoopRep(int constInit, *iterCount = loopCount; return true; -#ifdef LEGACY_BACKEND - case GT_ASG_MUL: - case GT_ASG_DIV: - case GT_ASG_RSH: - case GT_ASG_LSH: - case GT_ASG_UDIV: -#endif case GT_MUL: case GT_DIV: case GT_RSH: @@ -3257,16 +3244,10 @@ bool Compiler::optComputeLoopRep(int constInit, case GT_LT: switch (iterOper) { -#ifdef LEGACY_BACKEND - case GT_ASG_SUB: -#endif case GT_SUB: iterInc = -iterInc; __fallthrough; -#ifdef LEGACY_BACKEND - case GT_ASG_ADD: -#endif case GT_ADD: if (constInitX < constLimitX) { @@ -3295,13 +3276,6 @@ bool Compiler::optComputeLoopRep(int constInit, *iterCount = loopCount; return true; -#ifdef LEGACY_BACKEND - case GT_ASG_MUL: - case GT_ASG_DIV: - case GT_ASG_RSH: - case GT_ASG_LSH: - case GT_ASG_UDIV: -#endif case GT_MUL: case GT_DIV: case GT_RSH: @@ -3317,16 +3291,10 @@ bool Compiler::optComputeLoopRep(int constInit, case GT_LE: switch (iterOper) { -#ifdef LEGACY_BACKEND - case GT_ASG_SUB: -#endif case GT_SUB: iterInc = -iterInc; __fallthrough; -#ifdef LEGACY_BACKEND - case GT_ASG_ADD: -#endif case GT_ADD: if (constInitX <= constLimitX) { @@ -3355,13 +3323,6 @@ bool Compiler::optComputeLoopRep(int constInit, *iterCount = loopCount; return true; -#ifdef LEGACY_BACKEND - case GT_ASG_MUL: - case GT_ASG_DIV: - case GT_ASG_RSH: - case GT_ASG_LSH: - case GT_ASG_UDIV: -#endif case GT_MUL: case GT_DIV: case GT_RSH: @@ -3377,16 +3338,10 @@ bool Compiler::optComputeLoopRep(int constInit, case GT_GT: switch (iterOper) { -#ifdef LEGACY_BACKEND - case GT_ASG_SUB: -#endif case GT_SUB: iterInc = -iterInc; __fallthrough; -#ifdef LEGACY_BACKEND - case GT_ASG_ADD: -#endif case GT_ADD: if (constInitX > constLimitX) { @@ -3415,13 +3370,6 @@ bool Compiler::optComputeLoopRep(int constInit, *iterCount = loopCount; return true; -#ifdef LEGACY_BACKEND - case GT_ASG_MUL: - case GT_ASG_DIV: - case GT_ASG_RSH: - case GT_ASG_LSH: - case GT_ASG_UDIV: -#endif case GT_MUL: case GT_DIV: case GT_RSH: @@ -3437,16 +3385,10 @@ bool Compiler::optComputeLoopRep(int constInit, case GT_GE: switch (iterOper) { -#ifdef LEGACY_BACKEND - case GT_ASG_SUB: -#endif case GT_SUB: iterInc = -iterInc; __fallthrough; -#ifdef LEGACY_BACKEND - case GT_ASG_ADD: -#endif case GT_ADD: if (constInitX >= constLimitX) { @@ -3475,13 +3417,6 @@ bool Compiler::optComputeLoopRep(int constInit, *iterCount = loopCount; return true; -#ifdef LEGACY_BACKEND - case GT_ASG_MUL: - case GT_ASG_DIV: - case GT_ASG_RSH: - case GT_ASG_LSH: - case GT_ASG_UDIV: -#endif case GT_MUL: case GT_DIV: case GT_RSH: @@ -8267,12 +8202,7 @@ bool Compiler::optIdentifyLoopOptInfo(unsigned loopNum, LoopCloneContext* contex } // TODO-CQ: CLONE: Mark increasing or decreasing loops. - if (( -#ifdef LEGACY_BACKEND - pLoop->lpIterOper() != GT_ASG_ADD && -#endif - pLoop->lpIterOper() != GT_ADD) || - (pLoop->lpIterConst() != 1)) + if ((pLoop->lpIterOper() != GT_ADD) || (pLoop->lpIterConst() != 1)) { JITDUMP("> Loop iteration operator not matching\n"); return false; @@ -8285,16 +8215,8 @@ bool Compiler::optIdentifyLoopOptInfo(unsigned loopNum, LoopCloneContext* contex return false; } - if (!(((pLoop->lpTestOper() == GT_LT || pLoop->lpTestOper() == GT_LE) && (pLoop->lpIterOper() == GT_ADD -#ifdef LEGACY_BACKEND - || pLoop->lpIterOper() == GT_ASG_ADD -#endif - )) || - ((pLoop->lpTestOper() == GT_GT || pLoop->lpTestOper() == GT_GE) && (pLoop->lpIterOper() == GT_SUB -#ifdef LEGACY_BACKEND - || pLoop->lpIterOper() == GT_ASG_SUB -#endif - )))) + if (!(((pLoop->lpTestOper() == GT_LT || pLoop->lpTestOper() == GT_LE) && (pLoop->lpIterOper() == GT_ADD)) || + ((pLoop->lpTestOper() == GT_GT || pLoop->lpTestOper() == GT_GE) && (pLoop->lpIterOper() == GT_SUB)))) { JITDUMP("> Loop test (%s) doesn't agree with the direction (%s) of the pLoop->\n", GenTree::OpName(pLoop->lpTestOper()), GenTree::OpName(pLoop->lpIterOper())); diff --git a/src/jit/rangecheck.cpp b/src/jit/rangecheck.cpp index 3d95ba81fc..d80576d97c 100644 --- a/src/jit/rangecheck.cpp +++ b/src/jit/rangecheck.cpp @@ -324,11 +324,7 @@ void RangeCheck::Widen(BasicBlock* block, GenTree* tree, Range* pRange) bool RangeCheck::IsBinOpMonotonicallyIncreasing(GenTreeOp* binop) { -#ifdef LEGACY_BACKEND - assert(binop->OperIs(GT_ADD, GT_ASG_ADD)); -#else assert(binop->OperIs(GT_ADD)); -#endif GenTree* op1 = binop->gtGetOp1(); GenTree* op2 = binop->gtGetOp2(); @@ -411,15 +407,8 @@ bool RangeCheck::IsMonotonicallyIncreasing(GenTree* expr, bool rejectNegativeCon case GT_ASG: return IsMonotonicallyIncreasing(asg->gtGetOp2(), rejectNegativeConst); -#ifdef LEGACY_BACKEND - case GT_ASG_ADD: - return IsBinOpMonotonicallyIncreasing(asg); -#endif - default: -#ifndef LEGACY_BACKEND noway_assert(false); -#endif // All other 'asg->OperGet()' kinds, return false break; } @@ -812,11 +801,7 @@ void RangeCheck::MergeAssertion(BasicBlock* block, GenTree* op, Range* pRange DE // Compute the range for a binary operation. Range RangeCheck::ComputeRangeForBinOp(BasicBlock* block, GenTreeOp* binop, bool monotonic DEBUGARG(int indent)) { -#ifdef LEGACY_BACKEND - assert(binop->OperIs(GT_ADD, GT_ASG_ADD)); -#else assert(binop->OperIs(GT_ADD)); -#endif GenTree* op1 = binop->gtGetOp1(); GenTree* op2 = binop->gtGetOp2(); @@ -907,18 +892,8 @@ Range RangeCheck::ComputeRangeForLocalDef(BasicBlock* block, return range; } -#ifdef LEGACY_BACKEND - case GT_ASG_ADD: - // If the operator of the definition is +=, then compute the range of the operands of +. - // Note that gtGetOp1 will return op1 to be the lhs; in the formulation of ssa, we have - // a side table for defs and the lhs of a += is considered to be a use for SSA numbering. - return ComputeRangeForBinOp(asgBlock, asg, monotonic DEBUGARG(indent)); -#endif - default: -#ifndef LEGACY_BACKEND noway_assert(false); -#endif // All other 'asg->OperGet()' kinds, return Limit::keUnknown break; } @@ -1046,16 +1021,8 @@ bool RangeCheck::DoesVarDefOverflow(GenTreeLclVarCommon* lcl) case GT_ASG: return DoesOverflow(asgBlock, asg->gtGetOp2()); -#ifdef LEGACY_BACKEND - case GT_ASG_ADD: - // For GT_ASG_ADD, op2 is use, op1 is also use since we side table for defs in useasg case. - return DoesBinOpOverflow(asgBlock, asg); -#endif - default: -#ifndef LEGACY_BACKEND noway_assert(false); -#endif // All other 'asg->OperGet()' kinds, conservatively return true break; } diff --git a/src/jit/rationalize.cpp b/src/jit/rationalize.cpp index a2ab62fcc6..a08d042ed9 100644 --- a/src/jit/rationalize.cpp +++ b/src/jit/rationalize.cpp @@ -7,7 +7,6 @@ #pragma hdrstop #endif -#ifndef LEGACY_BACKEND // return op that is the store equivalent of the given load opcode genTreeOps storeForm(genTreeOps loadForm) { @@ -1068,4 +1067,3 @@ void Rationalizer::DoPhase() comp->compRationalIRForm = true; } -#endif // LEGACY_BACKEND diff --git a/src/jit/regalloc.cpp b/src/jit/regalloc.cpp index 6046d31f05..70e33f4925 100644 --- a/src/jit/regalloc.cpp +++ b/src/jit/regalloc.cpp @@ -19,40 +19,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #endif #include "regalloc.h" -#if FEATURE_FP_REGALLOC -Compiler::enumConfigRegisterFP Compiler::raConfigRegisterFP() -{ - DWORD val = JitConfig.JitRegisterFP(); - - return (enumConfigRegisterFP)(val & 0x3); -} -#endif // FEATURE_FP_REGALLOC - -regMaskTP Compiler::raConfigRestrictMaskFP() -{ - regMaskTP result = RBM_NONE; - -#if FEATURE_FP_REGALLOC - switch (raConfigRegisterFP()) - { - case CONFIG_REGISTER_FP_NONE: - result = RBM_NONE; - break; - case CONFIG_REGISTER_FP_CALLEE_TRASH: - result = RBM_FLT_CALLEE_TRASH; - break; - case CONFIG_REGISTER_FP_CALLEE_SAVED: - result = RBM_FLT_CALLEE_SAVED; - break; - case CONFIG_REGISTER_FP_FULL: - result = RBM_ALLFLOAT; - break; - } -#endif - - return result; -} - #if DOUBLE_ALIGN DWORD Compiler::getCanDoubleAlign() { @@ -133,6478 +99,173 @@ bool Compiler::shouldDoubleAlign( } else { - // OK we passed all of the benefit tests, so we'll predict a double aligned frame. - JITDUMP(" Predicting to create a double-aligned frame\n"); - doDoubleAlign = true; - } - return doDoubleAlign; -} -#endif // DOUBLE_ALIGN - -#ifdef LEGACY_BACKEND // We don't use any of the old register allocator functions when LSRA is used instead. - -void Compiler::raInit() -{ -#if FEATURE_STACK_FP_X87 - /* We have not assigned any FP variables to registers yet */ - - VarSetOps::AssignNoCopy(this, optAllFPregVars, VarSetOps::UninitVal()); -#endif - codeGen->intRegState.rsIsFloat = false; - codeGen->floatRegState.rsIsFloat = true; - - rpReverseEBPenreg = false; - rpAsgVarNum = -1; - rpPassesMax = 6; - rpPassesPessimize = rpPassesMax - 3; - if (opts.compDbgCode) - { - rpPassesMax++; - } - rpStkPredict = (unsigned)-1; - rpFrameType = FT_NOT_SET; - rpLostEnreg = false; - rpMustCreateEBPCalled = false; - rpRegAllocDone = false; - rpMaskPInvokeEpilogIntf = RBM_NONE; - - rpPredictMap[PREDICT_NONE] = RBM_NONE; - rpPredictMap[PREDICT_ADDR] = RBM_NONE; - -#if FEATURE_FP_REGALLOC - rpPredictMap[PREDICT_REG] = RBM_ALLINT | RBM_ALLFLOAT; - rpPredictMap[PREDICT_SCRATCH_REG] = RBM_ALLINT | RBM_ALLFLOAT; -#else - rpPredictMap[PREDICT_REG] = RBM_ALLINT; - rpPredictMap[PREDICT_SCRATCH_REG] = RBM_ALLINT; -#endif - -#define REGDEF(name, rnum, mask, sname) rpPredictMap[PREDICT_REG_##name] = RBM_##name; -#include "register.h" - -#if defined(_TARGET_ARM_) - - rpPredictMap[PREDICT_PAIR_R0R1] = RBM_R0 | RBM_R1; - rpPredictMap[PREDICT_PAIR_R2R3] = RBM_R2 | RBM_R3; - rpPredictMap[PREDICT_REG_SP] = RBM_ILLEGAL; - -#elif defined(_TARGET_AMD64_) - - rpPredictMap[PREDICT_NOT_REG_EAX] = RBM_ALLINT & ~RBM_EAX; - rpPredictMap[PREDICT_NOT_REG_ECX] = RBM_ALLINT & ~RBM_ECX; - rpPredictMap[PREDICT_REG_ESP] = RBM_ILLEGAL; - -#elif defined(_TARGET_X86_) - - rpPredictMap[PREDICT_NOT_REG_EAX] = RBM_ALLINT & ~RBM_EAX; - rpPredictMap[PREDICT_NOT_REG_ECX] = RBM_ALLINT & ~RBM_ECX; - rpPredictMap[PREDICT_REG_ESP] = RBM_ILLEGAL; - rpPredictMap[PREDICT_PAIR_EAXEDX] = RBM_EAX | RBM_EDX; - rpPredictMap[PREDICT_PAIR_ECXEBX] = RBM_ECX | RBM_EBX; - -#endif - - rpBestRecordedPrediction = NULL; -} - -/***************************************************************************** - * - * The following table(s) determines the order in which registers are considered - * for variables to live in - */ - -const regNumber* Compiler::raGetRegVarOrder(var_types regType, unsigned* wbVarOrderSize) -{ -#if FEATURE_FP_REGALLOC - if (varTypeIsFloating(regType)) - { - static const regNumber raRegVarOrderFlt[] = {REG_VAR_ORDER_FLT}; - const unsigned raRegVarOrderFltSize = _countof(raRegVarOrderFlt); - - if (wbVarOrderSize != NULL) - *wbVarOrderSize = raRegVarOrderFltSize; - - return &raRegVarOrderFlt[0]; - } - else -#endif - { - static const regNumber raRegVarOrder[] = {REG_VAR_ORDER}; - const unsigned raRegVarOrderSize = _countof(raRegVarOrder); - - if (wbVarOrderSize != NULL) - *wbVarOrderSize = raRegVarOrderSize; - - return &raRegVarOrder[0]; - } -} - -#ifdef DEBUG - -/***************************************************************************** - * - * Dump out the variable interference graph - * - */ - -void Compiler::raDumpVarIntf() -{ - unsigned lclNum; - LclVarDsc* varDsc; - - printf("Var. interference graph for %s\n", info.compFullName); - - for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++) - { - /* Ignore the variable if it's not tracked */ - - if (!varDsc->lvTracked) - continue; - - /* Get hold of the index and the interference mask for the variable */ - unsigned varIndex = varDsc->lvVarIndex; - - printf(" V%02u,T%02u and ", lclNum, varIndex); - - unsigned refIndex; - - for (refIndex = 0; refIndex < lvaTrackedCount; refIndex++) - { - if (VarSetOps::IsMember(this, lvaVarIntf[varIndex], refIndex)) - printf("T%02u ", refIndex); - else - printf(" "); - } - - printf("\n"); - } - - printf("\n"); -} - -/***************************************************************************** - * - * Dump out the register interference graph - * - */ -void Compiler::raDumpRegIntf() -{ - printf("Reg. interference graph for %s\n", info.compFullName); - - unsigned lclNum; - LclVarDsc* varDsc; - - for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++) - { - unsigned varNum; - - /* Ignore the variable if it's not tracked */ - - if (!varDsc->lvTracked) - continue; - - /* Get hold of the index and the interference mask for the variable */ - - varNum = varDsc->lvVarIndex; - - printf(" V%02u,T%02u and ", lclNum, varNum); - - if (varDsc->IsFloatRegType()) - { -#if !FEATURE_STACK_FP_X87 - for (regNumber regNum = REG_FP_FIRST; regNum <= REG_FP_LAST; regNum = REG_NEXT(regNum)) - { - if (VarSetOps::IsMember(this, raLclRegIntf[regNum], varNum)) - printf("%3s ", getRegName(regNum, true)); - else - printf(" "); - } -#endif - } - else - { - for (regNumber regNum = REG_INT_FIRST; regNum <= REG_INT_LAST; regNum = REG_NEXT(regNum)) - { - if (VarSetOps::IsMember(this, raLclRegIntf[regNum], varNum)) - printf("%3s ", getRegName(regNum)); - else - printf(" "); - } - } - - printf("\n"); - } - - printf("\n"); -} -#endif // DEBUG - -/***************************************************************************** - * - * We'll adjust the ref counts based on interference - * - */ - -void Compiler::raAdjustVarIntf() -{ - // This method was not correct and has been disabled. - return; -} - -/*****************************************************************************/ -/*****************************************************************************/ -/* Determine register mask for a call/return from type. - */ - -inline regMaskTP Compiler::genReturnRegForTree(GenTree* tree) -{ - var_types type = tree->TypeGet(); - - if (varTypeIsStruct(type) && IsHfa(tree)) - { - int retSlots = GetHfaCount(tree); - return ((1 << retSlots) - 1) << REG_FLOATRET; - } - - const static regMaskTP returnMap[TYP_COUNT] = { - RBM_ILLEGAL, // TYP_UNDEF, - RBM_NONE, // TYP_VOID, - RBM_INTRET, // TYP_BOOL, - RBM_INTRET, // TYP_BYTE, - RBM_INTRET, // TYP_UBYTE, - RBM_INTRET, // TYP_SHORT, - RBM_INTRET, // TYP_USHORT, - RBM_INTRET, // TYP_INT, - RBM_INTRET, // TYP_UINT, - RBM_LNGRET, // TYP_LONG, - RBM_LNGRET, // TYP_ULONG, - RBM_FLOATRET, // TYP_FLOAT, - RBM_DOUBLERET, // TYP_DOUBLE, - RBM_INTRET, // TYP_REF, - RBM_INTRET, // TYP_BYREF, - RBM_ILLEGAL, // TYP_STRUCT, - RBM_ILLEGAL, // TYP_BLK, - RBM_ILLEGAL, // TYP_LCLBLK, - RBM_ILLEGAL, // TYP_UNKNOWN, - }; - - assert((unsigned)type < _countof(returnMap)); - assert(returnMap[TYP_LONG] == RBM_LNGRET); - assert(returnMap[TYP_DOUBLE] == RBM_DOUBLERET); - assert(returnMap[TYP_REF] == RBM_INTRET); - assert(returnMap[TYP_STRUCT] == RBM_ILLEGAL); - - regMaskTP result = returnMap[type]; - assert(result != RBM_ILLEGAL); - return result; -} - -/*****************************************************************************/ - -/****************************************************************************/ - -#ifdef DEBUG - -static void dispLifeSet(Compiler* comp, VARSET_VALARG_TP mask, VARSET_VALARG_TP life) -{ - unsigned lclNum; - LclVarDsc* varDsc; - - for (lclNum = 0, varDsc = comp->lvaTable; lclNum < comp->lvaCount; lclNum++, varDsc++) - { - if (!varDsc->lvTracked) - continue; - - if (!VarSetOps::IsMember(comp, mask, varDsc->lvVarIndex)) - continue; - - if (VarSetOps::IsMember(comp, life, varDsc->lvVarIndex)) - printf("V%02u ", lclNum); - } -} - -#endif - -/*****************************************************************************/ -#ifdef DEBUG -/***************************************************************************** - * - * Debugging helpers - display variables liveness info. - */ - -void dispFPvarsInBBlist(BasicBlock* beg, BasicBlock* end, VARSET_TP mask, Compiler* comp) -{ - do - { - printf("BB%02u: ", beg->bbNum); - - printf(" in = [ "); - dispLifeSet(comp, mask, beg->bbLiveIn); - printf("] ,"); - - printf(" out = [ "); - dispLifeSet(comp, mask, beg->bbLiveOut); - printf("]"); - - if (beg->bbFlags & BBF_VISITED) - printf(" inner=%u", beg->bbFPinVars); - - printf("\n"); - - beg = beg->bbNext; - if (!beg) - return; - } while (beg != end); -} - -#if FEATURE_STACK_FP_X87 -void Compiler::raDispFPlifeInfo() -{ - BasicBlock* block; - - for (block = fgFirstBB; block; block = block->bbNext) - { - GenTree* stmt; - - printf("BB%02u: in = [ ", block->bbNum); - dispLifeSet(this, optAllFloatVars, block->bbLiveIn); - printf("]\n\n"); - - VARSET_TP life(VarSetOps::MakeCopy(this, block->bbLiveIn)); - for (stmt = block->bbTreeList; stmt; stmt = stmt->gtNext) - { - GenTree* tree; - - noway_assert(stmt->gtOper == GT_STMT); - - for (tree = stmt->gtStmt.gtStmtList; tree; tree = tree->gtNext) - { - VarSetOps::AssignNoCopy(this, life, fgUpdateLiveSet(life, tree)); - - dispLifeSet(this, optAllFloatVars, life); - printf(" "); - gtDispTree(tree, 0, NULL, true); - } - - printf("\n"); - } - - printf("BB%02u: out = [ ", block->bbNum); - dispLifeSet(this, optAllFloatVars, block->bbLiveOut); - printf("]\n\n"); - } -} -#endif // FEATURE_STACK_FP_X87 -/*****************************************************************************/ -#endif // DEBUG -/*****************************************************************************/ - -/*****************************************************************************/ - -void Compiler::raSetRegVarOrder( - var_types regType, regNumber* customVarOrder, unsigned* customVarOrderSize, regMaskTP prefReg, regMaskTP avoidReg) -{ - unsigned normalVarOrderSize; - const regNumber* normalVarOrder = raGetRegVarOrder(regType, &normalVarOrderSize); - unsigned index; - unsigned listIndex = 0; - regMaskTP usedReg = avoidReg; - - noway_assert(*customVarOrderSize >= normalVarOrderSize); - - if (prefReg) - { - /* First place the preferred registers at the start of customVarOrder */ - - regMaskTP regBit; - regNumber regNum; - - for (index = 0; index < normalVarOrderSize; index++) - { - regNum = normalVarOrder[index]; - regBit = genRegMask(regNum); - - if (usedReg & regBit) - continue; - - if (prefReg & regBit) - { - usedReg |= regBit; - noway_assert(listIndex < normalVarOrderSize); - customVarOrder[listIndex++] = regNum; - prefReg -= regBit; - if (prefReg == 0) - break; - } - } - -#if CPU_HAS_BYTE_REGS - /* Then if byteable registers are preferred place them */ - - if (prefReg & RBM_BYTE_REG_FLAG) - { - for (index = 0; index < normalVarOrderSize; index++) - { - regNum = normalVarOrder[index]; - regBit = genRegMask(regNum); - - if (usedReg & regBit) - continue; - - if (RBM_BYTE_REGS & regBit) - { - usedReg |= regBit; - noway_assert(listIndex < normalVarOrderSize); - customVarOrder[listIndex++] = regNum; - } - } - } - -#endif // CPU_HAS_BYTE_REGS - } - - /* Now place all the non-preferred registers */ - - for (index = 0; index < normalVarOrderSize; index++) - { - regNumber regNum = normalVarOrder[index]; - regMaskTP regBit = genRegMask(regNum); - - if (usedReg & regBit) - continue; - - usedReg |= regBit; - noway_assert(listIndex < normalVarOrderSize); - customVarOrder[listIndex++] = regNum; - } - - if (avoidReg) - { - /* Now place the "avoid" registers */ - - for (index = 0; index < normalVarOrderSize; index++) - { - regNumber regNum = normalVarOrder[index]; - regMaskTP regBit = genRegMask(regNum); - - if (avoidReg & regBit) - { - noway_assert(listIndex < normalVarOrderSize); - customVarOrder[listIndex++] = regNum; - avoidReg -= regBit; - if (avoidReg == 0) - break; - } - } - } - - *customVarOrderSize = listIndex; - noway_assert(listIndex == normalVarOrderSize); -} - -/***************************************************************************** - * - * Setup the raAvoidArgRegMask and rsCalleeRegArgMaskLiveIn - */ - -void Compiler::raSetupArgMasks(RegState* regState) -{ - /* Determine the registers holding incoming register arguments */ - /* and setup raAvoidArgRegMask to the set of registers that we */ - /* may want to avoid when enregistering the locals. */ - - regState->rsCalleeRegArgMaskLiveIn = RBM_NONE; - raAvoidArgRegMask = RBM_NONE; - - LclVarDsc* argsEnd = lvaTable + info.compArgsCount; - - for (LclVarDsc* argDsc = lvaTable; argDsc < argsEnd; argDsc++) - { - noway_assert(argDsc->lvIsParam); - - // Is it a register argument ? - if (!argDsc->lvIsRegArg) - continue; - - // only process args that apply to the current register file - if ((argDsc->IsFloatRegType() && !info.compIsVarArgs && !opts.compUseSoftFP) != regState->rsIsFloat) - { - continue; - } - - // Is it dead on entry ?? - // In certain cases such as when compJmpOpUsed is true, - // or when we have a generic type context arg that we must report - // then the arguments have to be kept alive throughout the prolog. - // So we have to consider it as live on entry. - // - bool keepArgAlive = compJmpOpUsed; - if ((unsigned(info.compTypeCtxtArg) != BAD_VAR_NUM) && lvaReportParamTypeArg() && - ((lvaTable + info.compTypeCtxtArg) == argDsc)) - { - keepArgAlive = true; - } - - if (!keepArgAlive && argDsc->lvTracked && !VarSetOps::IsMember(this, fgFirstBB->bbLiveIn, argDsc->lvVarIndex)) - { - continue; - } - - // The code to set the regState for each arg is outlined for shared use - // by linear scan - regNumber inArgReg = raUpdateRegStateForArg(regState, argDsc); - - // Do we need to try to avoid this incoming arg registers? - - // If it's not tracked, don't do the stuff below. - if (!argDsc->lvTracked) - continue; - - // If the incoming arg is used after a call it is live accross - // a call and will have to be allocated to a caller saved - // register anyway (a very common case). - // - // In this case it is pointless to ask that the higher ref count - // locals to avoid using the incoming arg register - - unsigned argVarIndex = argDsc->lvVarIndex; - - /* Does the incoming register and the arg variable interfere? */ - - if (!VarSetOps::IsMember(this, raLclRegIntf[inArgReg], argVarIndex)) - { - // No they do not interfere, - // so we add inArgReg to raAvoidArgRegMask - - raAvoidArgRegMask |= genRegMask(inArgReg); - } -#ifdef _TARGET_ARM_ - if (argDsc->lvType == TYP_DOUBLE) - { - // Avoid the double register argument pair for register allocation. - if (!VarSetOps::IsMember(this, raLclRegIntf[inArgReg + 1], argVarIndex)) - { - raAvoidArgRegMask |= genRegMask(static_cast(inArgReg + 1)); - } - } -#endif - } -} - -#endif // LEGACY_BACKEND - -// The code to set the regState for each arg is outlined for shared use -// by linear scan. (It is not shared for System V AMD64 platform.) -regNumber Compiler::raUpdateRegStateForArg(RegState* regState, LclVarDsc* argDsc) -{ - regNumber inArgReg = argDsc->lvArgReg; - regMaskTP inArgMask = genRegMask(inArgReg); - - if (regState->rsIsFloat) - { - noway_assert(inArgMask & RBM_FLTARG_REGS); - } - else // regState is for the integer registers - { - // This might be the fixed return buffer register argument (on ARM64) - // We check and allow inArgReg to be theFixedRetBuffReg - if (hasFixedRetBuffReg() && (inArgReg == theFixedRetBuffReg())) - { - // We should have a TYP_BYREF or TYP_I_IMPL arg and not a TYP_STRUCT arg - noway_assert(argDsc->lvType == TYP_BYREF || argDsc->lvType == TYP_I_IMPL); - // We should have recorded the variable number for the return buffer arg - noway_assert(info.compRetBuffArg != BAD_VAR_NUM); - } - else // we have a regular arg - { - noway_assert(inArgMask & RBM_ARG_REGS); - } - } - - regState->rsCalleeRegArgMaskLiveIn |= inArgMask; - -#ifdef _TARGET_ARM_ - if (argDsc->lvType == TYP_DOUBLE) - { - if (info.compIsVarArgs || opts.compUseSoftFP) - { - assert((inArgReg == REG_R0) || (inArgReg == REG_R2)); - assert(!regState->rsIsFloat); - } - else - { - assert(regState->rsIsFloat); - assert(emitter::isDoubleReg(inArgReg)); - } - regState->rsCalleeRegArgMaskLiveIn |= genRegMask((regNumber)(inArgReg + 1)); - } - else if (argDsc->lvType == TYP_LONG) - { - assert((inArgReg == REG_R0) || (inArgReg == REG_R2)); - assert(!regState->rsIsFloat); - regState->rsCalleeRegArgMaskLiveIn |= genRegMask((regNumber)(inArgReg + 1)); - } -#endif // _TARGET_ARM_ - -#if FEATURE_MULTIREG_ARGS - if (varTypeIsStruct(argDsc->lvType)) - { - if (argDsc->lvIsHfaRegArg()) - { - assert(regState->rsIsFloat); - unsigned cSlots = GetHfaCount(argDsc->lvVerTypeInfo.GetClassHandleForValueClass()); - for (unsigned i = 1; i < cSlots; i++) - { - assert(inArgReg + i <= LAST_FP_ARGREG); - regState->rsCalleeRegArgMaskLiveIn |= genRegMask(static_cast(inArgReg + i)); - } - } - else - { - unsigned cSlots = argDsc->lvSize() / TARGET_POINTER_SIZE; - for (unsigned i = 1; i < cSlots; i++) - { - regNumber nextArgReg = (regNumber)(inArgReg + i); - if (nextArgReg > REG_ARG_LAST) - { - break; - } - assert(regState->rsIsFloat == false); - regState->rsCalleeRegArgMaskLiveIn |= genRegMask(nextArgReg); - } - } - } -#endif // FEATURE_MULTIREG_ARGS - - return inArgReg; -} - -#ifdef LEGACY_BACKEND // We don't use any of the old register allocator functions when LSRA is used instead. - -/***************************************************************************** - * - * Assign variables to live in registers, etc. - */ - -void Compiler::raAssignVars() -{ -#ifdef DEBUG - if (verbose) - printf("*************** In raAssignVars()\n"); -#endif - /* We need to keep track of which registers we ever touch */ - - codeGen->regSet.rsClearRegsModified(); - -#if FEATURE_STACK_FP_X87 - // FP register allocation - raEnregisterVarsStackFP(); - raGenerateFPRefCounts(); -#endif - - /* Predict registers used by code generation */ - rpPredictRegUse(); // New reg predictor/allocator - - // Change all unused promoted non-argument struct locals to a non-GC type (in this case TYP_INT) - // so that the gc tracking logic and lvMustInit logic will ignore them. - - unsigned lclNum; - LclVarDsc* varDsc; - - for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++) - { - if (varDsc->lvType != TYP_STRUCT) - continue; - - if (!varDsc->lvPromoted) - continue; - - if (varDsc->lvIsParam) - continue; - - if (varDsc->lvRefCnt > 0) - continue; - -#ifdef DEBUG - if (verbose) - { - printf("Mark unused struct local V%02u\n", lclNum); - } - - lvaPromotionType promotionType = lvaGetPromotionType(varDsc); - - if (promotionType == PROMOTION_TYPE_DEPENDENT) - { - // This should only happen when all its field locals are unused as well. - - for (unsigned varNum = varDsc->lvFieldLclStart; varNum < varDsc->lvFieldLclStart + varDsc->lvFieldCnt; - varNum++) - { - noway_assert(lvaTable[varNum].lvRefCnt == 0); - lvaTable[varNum].lvIsStructField = false; - } - } - else - { - noway_assert(promotionType == PROMOTION_TYPE_INDEPENDENT); - } - - varDsc->lvUnusedStruct = 1; -#endif - - // Change such struct locals to ints - - varDsc->lvType = TYP_INT; // Bash to a non-gc type. - noway_assert(!varDsc->lvTracked); - noway_assert(!varDsc->lvRegister); - varDsc->lvOnFrame = false; // Force it not to be onstack. - varDsc->lvMustInit = false; // Force not to init it. - varDsc->lvStkOffs = 0; // Set it to anything other than BAD_STK_OFFS to make genSetScopeInfo() happy - } -} - -/*****************************************************************************/ -/*****************************************************************************/ - -/***************************************************************************** - * - * Given a regNumber return the correct predictReg enum value - */ - -inline static rpPredictReg rpGetPredictForReg(regNumber reg) -{ - return (rpPredictReg)(((int)reg) + ((int)PREDICT_REG_FIRST)); -} - -/***************************************************************************** - * - * Given a varIndex return the correct predictReg enum value - */ - -inline static rpPredictReg rpGetPredictForVarIndex(unsigned varIndex) -{ - return (rpPredictReg)(varIndex + ((int)PREDICT_REG_VAR_T00)); -} - -/***************************************************************************** - * - * Given a rpPredictReg return the correct varNumber value - */ - -inline static unsigned rpGetVarIndexForPredict(rpPredictReg predict) -{ - return (unsigned)predict - (unsigned)PREDICT_REG_VAR_T00; -} - -/***************************************************************************** - * - * Given a rpPredictReg return true if it specifies a Txx register - */ - -inline static bool rpHasVarIndexForPredict(rpPredictReg predict) -{ - if ((predict >= PREDICT_REG_VAR_T00) && (predict <= PREDICT_REG_VAR_MAX)) - return true; - else - return false; -} - -/***************************************************************************** - * - * Given a regmask return the correct predictReg enum value - */ - -static rpPredictReg rpGetPredictForMask(regMaskTP regmask) -{ - rpPredictReg result = PREDICT_NONE; - if (regmask != 0) /* Check if regmask has zero bits set */ - { - if (((regmask - 1) & regmask) == 0) /* Check if regmask has one bit set */ - { - DWORD reg = 0; - assert(FitsIn(regmask)); - BitScanForward(®, (DWORD)regmask); - return rpGetPredictForReg((regNumber)reg); - } - -#if defined(_TARGET_ARM_) - /* It has multiple bits set */ - else if (regmask == (RBM_R0 | RBM_R1)) - { - result = PREDICT_PAIR_R0R1; - } - else if (regmask == (RBM_R2 | RBM_R3)) - { - result = PREDICT_PAIR_R2R3; - } -#elif defined(_TARGET_X86_) - /* It has multiple bits set */ - else if (regmask == (RBM_EAX | RBM_EDX)) - { - result = PREDICT_PAIR_EAXEDX; - } - else if (regmask == (RBM_ECX | RBM_EBX)) - { - result = PREDICT_PAIR_ECXEBX; - } -#endif - else /* It doesn't match anything */ - { - result = PREDICT_NONE; - assert(!"unreachable"); - NO_WAY("bad regpair"); - } - } - return result; -} - -/***************************************************************************** - * - * Record a variable to register(s) interference - */ - -bool Compiler::rpRecordRegIntf(regMaskTP regMask, VARSET_VALARG_TP life DEBUGARG(const char* msg)) - -{ - bool addedIntf = false; - - if (regMask != 0) - { - for (regNumber regNum = REG_FIRST; regNum < REG_COUNT; regNum = REG_NEXT(regNum)) - { - regMaskTP regBit = genRegMask(regNum); - - if (regMask & regBit) - { - VARSET_TP newIntf(VarSetOps::Diff(this, life, raLclRegIntf[regNum])); - if (!VarSetOps::IsEmpty(this, newIntf)) - { -#ifdef DEBUG - if (verbose) - { - VarSetOps::Iter newIntfIter(this, newIntf); - unsigned varNum = 0; - while (newIntfIter.NextElem(&varNum)) - { - unsigned lclNum = lvaTrackedToVarNum[varNum]; - LclVarDsc* varDsc = &lvaTable[varNum]; -#if FEATURE_FP_REGALLOC - // Only print the useful interferences - // i.e. floating point LclVar interference with floating point registers - // or integer LclVar interference with general purpose registers - if (varTypeIsFloating(varDsc->TypeGet()) == genIsValidFloatReg(regNum)) -#endif - { - printf("Record interference between V%02u,T%02u and %s -- %s\n", lclNum, varNum, - getRegName(regNum), msg); - } - } - } -#endif - addedIntf = true; - VarSetOps::UnionD(this, raLclRegIntf[regNum], newIntf); - } - - regMask -= regBit; - if (regMask == 0) - break; - } - } - } - return addedIntf; -} - -/***************************************************************************** - * - * Record a new variable to variable(s) interference - */ - -bool Compiler::rpRecordVarIntf(unsigned varNum, VARSET_VALARG_TP intfVar DEBUGARG(const char* msg)) -{ - noway_assert((varNum >= 0) && (varNum < lvaTrackedCount)); - noway_assert(!VarSetOps::IsEmpty(this, intfVar)); - - VARSET_TP oneVar(VarSetOps::MakeEmpty(this)); - VarSetOps::AddElemD(this, oneVar, varNum); - - bool newIntf = fgMarkIntf(intfVar, oneVar); - - if (newIntf) - rpAddedVarIntf = true; - -#ifdef DEBUG - if (verbose && newIntf) - { - for (unsigned oneNum = 0; oneNum < lvaTrackedCount; oneNum++) - { - if (VarSetOps::IsMember(this, intfVar, oneNum)) - { - unsigned lclNum = lvaTrackedToVarNum[varNum]; - unsigned lclOne = lvaTrackedToVarNum[oneNum]; - printf("Record interference between V%02u,T%02u and V%02u,T%02u -- %s\n", lclNum, varNum, lclOne, - oneNum, msg); - } - } - } -#endif - - return newIntf; -} - -/***************************************************************************** - * - * Determine preferred register mask for a given predictReg value - */ - -inline regMaskTP Compiler::rpPredictRegMask(rpPredictReg predictReg, var_types type) -{ - if (rpHasVarIndexForPredict(predictReg)) - predictReg = PREDICT_REG; - - noway_assert((unsigned)predictReg < _countof(rpPredictMap)); - noway_assert(rpPredictMap[predictReg] != RBM_ILLEGAL); - - regMaskTP regAvailForType = rpPredictMap[predictReg]; - if (varTypeIsFloating(type)) - { - regAvailForType &= RBM_ALLFLOAT; - } - else - { - regAvailForType &= RBM_ALLINT; - } -#ifdef _TARGET_ARM_ - if (type == TYP_DOUBLE) - { - if ((predictReg >= PREDICT_REG_F0) && (predictReg <= PREDICT_REG_F31)) - { - // Fix 388433 ARM JitStress WP7 - if ((regAvailForType & RBM_DBL_REGS) != 0) - { - regAvailForType |= (regAvailForType << 1); - } - else - { - regAvailForType = RBM_NONE; - } - } - } -#endif - return regAvailForType; -} - -/***************************************************************************** - * - * Predict register choice for a type. - * - * Adds the predicted registers to rsModifiedRegsMask. - */ -regMaskTP Compiler::rpPredictRegPick(var_types type, rpPredictReg predictReg, regMaskTP lockedRegs) -{ - regMaskTP preferReg = rpPredictRegMask(predictReg, type); - regNumber regNum; - regMaskTP regBits; - - // Add any reserved register to the lockedRegs - lockedRegs |= codeGen->regSet.rsMaskResvd; - - /* Clear out the lockedRegs from preferReg */ - preferReg &= ~lockedRegs; - - if (rpAsgVarNum != -1) - { - noway_assert((rpAsgVarNum >= 0) && (rpAsgVarNum < (int)lclMAX_TRACKED)); - - /* Don't pick the register used by rpAsgVarNum either */ - LclVarDsc* tgtVar = lvaTable + lvaTrackedToVarNum[rpAsgVarNum]; - noway_assert(tgtVar->lvRegNum != REG_STK); - - preferReg &= ~genRegMask(tgtVar->lvRegNum); - } - - switch (type) - { - case TYP_BOOL: - case TYP_BYTE: - case TYP_UBYTE: - case TYP_SHORT: - case TYP_USHORT: - case TYP_INT: - case TYP_UINT: - case TYP_REF: - case TYP_BYREF: -#ifdef _TARGET_AMD64_ - case TYP_LONG: -#endif // _TARGET_AMD64_ - - // expand preferReg to all non-locked registers if no bits set - preferReg = codeGen->regSet.rsUseIfZero(preferReg & RBM_ALLINT, RBM_ALLINT & ~lockedRegs); - - if (preferReg == 0) // no bits set? - { - // Add one predefined spill choice register if no bits set. - // (The jit will introduce one spill temp) - preferReg |= RBM_SPILL_CHOICE; - rpPredictSpillCnt++; - -#ifdef DEBUG - if (verbose) - printf("Predict one spill temp\n"); -#endif - } - - if (preferReg != 0) - { - /* Iterate the registers in the order specified by rpRegTmpOrder */ - - for (unsigned index = 0; index < REG_TMP_ORDER_COUNT; index++) - { - regNum = rpRegTmpOrder[index]; - regBits = genRegMask(regNum); - - if ((preferReg & regBits) == regBits) - { - goto RET; - } - } - } - /* Otherwise we have allocated all registers, so do nothing */ - break; - -#ifndef _TARGET_AMD64_ - case TYP_LONG: - - if ((preferReg == 0) || // no bits set? - ((preferReg & (preferReg - 1)) == 0)) // or only one bit set? - { - // expand preferReg to all non-locked registers - preferReg = RBM_ALLINT & ~lockedRegs; - } - - if (preferReg == 0) // no bits set? - { - // Add EAX:EDX to the registers - // (The jit will introduce two spill temps) - preferReg = RBM_PAIR_TMP; - rpPredictSpillCnt += 2; -#ifdef DEBUG - if (verbose) - printf("Predict two spill temps\n"); -#endif - } - else if ((preferReg & (preferReg - 1)) == 0) // only one bit set? - { - if ((preferReg & RBM_PAIR_TMP_LO) == 0) - { - // Add EAX to the registers - // (The jit will introduce one spill temp) - preferReg |= RBM_PAIR_TMP_LO; - } - else - { - // Add EDX to the registers - // (The jit will introduce one spill temp) - preferReg |= RBM_PAIR_TMP_HI; - } - rpPredictSpillCnt++; -#ifdef DEBUG - if (verbose) - printf("Predict one spill temp\n"); -#endif - } - - regPairNo regPair; - regPair = codeGen->regSet.rsFindRegPairNo(preferReg); - if (regPair != REG_PAIR_NONE) - { - regBits = genRegPairMask(regPair); - goto RET; - } - - /* Otherwise we have allocated all registers, so do nothing */ - break; -#endif // _TARGET_AMD64_ - -#ifdef _TARGET_ARM_ - case TYP_STRUCT: -#endif - - case TYP_FLOAT: - case TYP_DOUBLE: - -#if FEATURE_FP_REGALLOC - regMaskTP restrictMask; - restrictMask = (raConfigRestrictMaskFP() | RBM_FLT_CALLEE_TRASH); - assert((restrictMask & RBM_SPILL_CHOICE_FLT) == RBM_SPILL_CHOICE_FLT); - - // expand preferReg to all available non-locked registers if no bits set - preferReg = codeGen->regSet.rsUseIfZero(preferReg & restrictMask, restrictMask & ~lockedRegs); - regMaskTP preferDouble; - preferDouble = preferReg & (preferReg >> 1); - - if ((preferReg == 0) // no bits set? -#ifdef _TARGET_ARM_ - || ((type == TYP_DOUBLE) && - ((preferReg & (preferReg >> 1)) == 0)) // or two consecutive bits set for TYP_DOUBLE -#endif - ) - { - // Add one predefined spill choice register if no bits set. - // (The jit will introduce one spill temp) - preferReg |= RBM_SPILL_CHOICE_FLT; - rpPredictSpillCnt++; - -#ifdef DEBUG - if (verbose) - printf("Predict one spill temp (float)\n"); -#endif - } - - assert(preferReg != 0); - - /* Iterate the registers in the order specified by raRegFltTmpOrder */ - - for (unsigned index = 0; index < REG_FLT_TMP_ORDER_COUNT; index++) - { - regNum = raRegFltTmpOrder[index]; - regBits = genRegMask(regNum); - - if (varTypeIsFloating(type)) - { -#ifdef _TARGET_ARM_ - if (type == TYP_DOUBLE) - { - if ((regBits & RBM_DBL_REGS) == 0) - { - continue; // We must restrict the set to the double registers - } - else - { - // TYP_DOUBLE use two consecutive registers - regBits |= genRegMask(REG_NEXT(regNum)); - } - } -#endif - // See if COMPlus_JitRegisterFP is restricting this FP register - // - if ((restrictMask & regBits) != regBits) - continue; - } - - if ((preferReg & regBits) == regBits) - { - goto RET; - } - } - /* Otherwise we have allocated all registers, so do nothing */ - break; - -#else // !FEATURE_FP_REGALLOC - - return RBM_NONE; - -#endif - - default: - noway_assert(!"unexpected type in reg use prediction"); - } - - /* Abnormal return */ - noway_assert(!"Ran out of registers in rpPredictRegPick"); - return RBM_NONE; - -RET: - /* - * If during the first prediction we need to allocate - * one of the registers that we used for coloring locals - * then flag this by setting rpPredictAssignAgain. - * We will have to go back and repredict the registers - */ - if ((rpPasses == 0) && ((rpPredictAssignMask & regBits) == regBits)) - rpPredictAssignAgain = true; - - // Add a register interference to each of the last use variables - if (!VarSetOps::IsEmpty(this, rpLastUseVars) || !VarSetOps::IsEmpty(this, rpUseInPlace)) - { - VARSET_TP lastUse(VarSetOps::MakeEmpty(this)); - VarSetOps::Assign(this, lastUse, rpLastUseVars); - VARSET_TP inPlaceUse(VarSetOps::MakeEmpty(this)); - VarSetOps::Assign(this, inPlaceUse, rpUseInPlace); - // While we still have any lastUse or inPlaceUse bits - VARSET_TP useUnion(VarSetOps::Union(this, lastUse, inPlaceUse)); - - VARSET_TP varAsSet(VarSetOps::MakeEmpty(this)); - VarSetOps::Iter iter(this, useUnion); - unsigned varNum = 0; - while (iter.NextElem(&varNum)) - { - // We'll need this for one of the calls... - VarSetOps::ClearD(this, varAsSet); - VarSetOps::AddElemD(this, varAsSet, varNum); - - // If this varBit and lastUse? - if (VarSetOps::IsMember(this, lastUse, varNum)) - { - // Record a register to variable interference - rpRecordRegIntf(regBits, varAsSet DEBUGARG("last use RegPick")); - } - - // If this varBit and inPlaceUse? - if (VarSetOps::IsMember(this, inPlaceUse, varNum)) - { - // Record a register to variable interference - rpRecordRegIntf(regBits, varAsSet DEBUGARG("used in place RegPick")); - } - } - } - codeGen->regSet.rsSetRegsModified(regBits); - - return regBits; -} - -/***************************************************************************** - * - * Predict integer register use for generating an address mode for a tree, - * by setting tree->gtUsedRegs to all registers used by this tree and its - * children. - * tree - is the child of a GT_IND node - * type - the type of the GT_IND node (floating point/integer) - * lockedRegs - are the registers which are currently held by - * a previously evaluated node. - * rsvdRegs - registers which should not be allocated because they will - * be needed to evaluate a node in the future - * - Also if rsvdRegs has the RBM_LASTUSE bit set then - * the rpLastUseVars set should be saved and restored - * so that we don't add any new variables to rpLastUseVars - * lenCSE - is non-NULL only when we have a lenCSE expression - * - * Return the scratch registers to be held by this tree. (one or two registers - * to form an address expression) - */ - -regMaskTP Compiler::rpPredictAddressMode( - GenTree* tree, var_types type, regMaskTP lockedRegs, regMaskTP rsvdRegs, GenTree* lenCSE) -{ - GenTree* op1; - GenTree* op2; - GenTree* opTemp; - genTreeOps oper = tree->OperGet(); - regMaskTP op1Mask; - regMaskTP op2Mask; - regMaskTP regMask; - ssize_t sh; - ssize_t cns = 0; - bool rev; - bool hasTwoAddConst = false; - bool restoreLastUseVars = false; - VARSET_TP oldLastUseVars(VarSetOps::MakeEmpty(this)); - - /* do we need to save and restore the rpLastUseVars set ? */ - if ((rsvdRegs & RBM_LASTUSE) && (lenCSE == NULL)) - { - restoreLastUseVars = true; - VarSetOps::Assign(this, oldLastUseVars, rpLastUseVars); - } - rsvdRegs &= ~RBM_LASTUSE; - - /* if not an add, then just force it to a register */ - - if (oper != GT_ADD) - { - if (oper == GT_ARR_ELEM) - { - regMask = rpPredictTreeRegUse(tree, PREDICT_NONE, lockedRegs, rsvdRegs); - goto DONE; - } - else - { - goto NO_ADDR_EXPR; - } - } - - op1 = tree->gtOp.gtOp1; - op2 = tree->gtOp.gtOp2; - rev = ((tree->gtFlags & GTF_REVERSE_OPS) != 0); - - /* look for (x + y) + icon address mode */ - - if (op2->OperGet() == GT_CNS_INT) - { - cns = op2->gtIntCon.gtIconVal; - - /* if not an add, then just force op1 into a register */ - if (op1->OperGet() != GT_ADD) - goto ONE_ADDR_EXPR; - - hasTwoAddConst = true; - - /* Record the 'rev' flag, reverse evaluation order */ - rev = ((op1->gtFlags & GTF_REVERSE_OPS) != 0); - - op2 = op1->gtOp.gtOp2; - op1 = op1->gtOp.gtOp1; // Overwrite op1 last!! - } - - /* Check for CNS_INT or LSH of CNS_INT in op2 slot */ - - sh = 0; - if (op2->OperGet() == GT_LSH) - { - if (op2->gtOp.gtOp2->OperGet() == GT_CNS_INT) - { - sh = op2->gtOp.gtOp2->gtIntCon.gtIconVal; - opTemp = op2->gtOp.gtOp1; - } - else - { - opTemp = NULL; - } - } - else - { - opTemp = op2; - } - - if (opTemp != NULL) - { - if (opTemp->OperGet() == GT_NOP) - { - opTemp = opTemp->gtOp.gtOp1; - } - - // Is this a const operand? - if (opTemp->OperGet() == GT_CNS_INT) - { - // Compute the new cns value that Codegen will end up using - cns += (opTemp->gtIntCon.gtIconVal << sh); - - goto ONE_ADDR_EXPR; - } - } - - /* Check for LSH in op1 slot */ - - if (op1->OperGet() != GT_LSH) - goto TWO_ADDR_EXPR; - - opTemp = op1->gtOp.gtOp2; - - if (opTemp->OperGet() != GT_CNS_INT) - goto TWO_ADDR_EXPR; - - sh = opTemp->gtIntCon.gtIconVal; - - /* Check for LSH of 0, special case */ - if (sh == 0) - goto TWO_ADDR_EXPR; - -#if defined(_TARGET_XARCH_) - - /* Check for LSH of 1 2 or 3 */ - if (sh > 3) - goto TWO_ADDR_EXPR; - -#elif defined(_TARGET_ARM_) - - /* Check for LSH of 1 to 30 */ - if (sh > 30) - goto TWO_ADDR_EXPR; - -#else - - goto TWO_ADDR_EXPR; - -#endif - - /* Matched a leftShift by 'sh' subtree, move op1 down */ - op1 = op1->gtOp.gtOp1; - -TWO_ADDR_EXPR: - - /* Now we have to evaluate op1 and op2 into registers */ - - /* Evaluate op1 and op2 in the correct order */ - if (rev) - { - op2Mask = rpPredictTreeRegUse(op2, PREDICT_REG, lockedRegs, rsvdRegs | op1->gtRsvdRegs); - op1Mask = rpPredictTreeRegUse(op1, PREDICT_REG, lockedRegs | op2Mask, rsvdRegs); - } - else - { - op1Mask = rpPredictTreeRegUse(op1, PREDICT_REG, lockedRegs, rsvdRegs | op2->gtRsvdRegs); - op2Mask = rpPredictTreeRegUse(op2, PREDICT_REG, lockedRegs | op1Mask, rsvdRegs); - } - - /* If op1 and op2 must be spilled and reloaded then - * op1 and op2 might be reloaded into the same register - * This can only happen when all the registers are lockedRegs - */ - if ((op1Mask == op2Mask) && (op1Mask != 0)) - { - /* We'll need to grab a different register for op2 */ - op2Mask = rpPredictRegPick(TYP_INT, PREDICT_REG, op1Mask); - } - -#ifdef _TARGET_ARM_ - // On the ARM we need a scratch register to evaluate the shifted operand for trees that have this form - // [op2 + op1<validDispForLdSt(cns, type)) - { - op2Mask |= rpPredictRegPick(TYP_INT, PREDICT_REG, (lockedRegs | op1Mask | op2Mask)); - } - - // - // If we create a CSE that immediately dies then we may need to add an additional register interference - // so we don't color the CSE into R3 - // - if (!rev && (op1Mask != RBM_NONE) && (op2->OperGet() == GT_COMMA)) - { - opTemp = op2->gtOp.gtOp2; - if (opTemp->OperGet() == GT_LCL_VAR) - { - unsigned varNum = opTemp->gtLclVar.gtLclNum; - LclVarDsc* varDsc = &lvaTable[varNum]; - - if (varDsc->lvTracked && !VarSetOps::IsMember(this, compCurLife, varDsc->lvVarIndex)) - { - rpRecordRegIntf(RBM_TMP_0, - VarSetOps::MakeSingleton(this, varDsc->lvVarIndex) DEBUGARG("dead CSE (gt_ind)")); - } - } - } -#endif - - regMask = (op1Mask | op2Mask); - tree->gtUsedRegs = (regMaskSmall)regMask; - goto DONE; - -ONE_ADDR_EXPR: - - /* now we have to evaluate op1 into a register */ - - op1Mask = rpPredictTreeRegUse(op1, PREDICT_REG, lockedRegs, rsvdRegs); - op2Mask = RBM_NONE; - -#ifdef _TARGET_ARM_ - // - // On the ARM we will need another scratch register when we have an 'cns' that is too large for the ld/st - // instruction - // - if (!codeGen->validDispForLdSt(cns, type)) - { - op2Mask |= rpPredictRegPick(TYP_INT, PREDICT_REG, (lockedRegs | op1Mask | op2Mask)); - } -#endif - - regMask = (op1Mask | op2Mask); - tree->gtUsedRegs = (regMaskSmall)regMask; - goto DONE; - -NO_ADDR_EXPR: - -#if !CPU_LOAD_STORE_ARCH - if (oper == GT_CNS_INT) - { - /* Indirect of a constant does not require a register */ - regMask = RBM_NONE; - } - else -#endif - { - /* now we have to evaluate tree into a register */ - regMask = rpPredictTreeRegUse(tree, PREDICT_REG, lockedRegs, rsvdRegs); - } - -DONE: - regMaskTP regUse = tree->gtUsedRegs; - - if (!VarSetOps::IsEmpty(this, compCurLife)) - { - // Add interference between the current set of life variables and - // the set of temporary registers need to evaluate the sub tree - if (regUse) - { - rpRecordRegIntf(regUse, compCurLife DEBUGARG("tmp use (gt_ind)")); - } - } - - /* Do we need to resore the oldLastUseVars value */ - if (restoreLastUseVars) - { - /* - * If we used a GT_ASG targeted register then we need to add - * a variable interference between any new last use variables - * and the GT_ASG targeted register - */ - if (!VarSetOps::Equal(this, rpLastUseVars, oldLastUseVars) && rpAsgVarNum != -1) - { - rpRecordVarIntf(rpAsgVarNum, - VarSetOps::Diff(this, rpLastUseVars, oldLastUseVars) DEBUGARG("asgn conflict (gt_ind)")); - } - VarSetOps::Assign(this, rpLastUseVars, oldLastUseVars); - } - - return regMask; -} - -/***************************************************************************** - * - * - */ - -void Compiler::rpPredictRefAssign(unsigned lclNum) -{ - LclVarDsc* varDsc = lvaTable + lclNum; - - varDsc->lvRefAssign = 1; - -#if NOGC_WRITE_BARRIERS -#ifdef DEBUG - if (verbose) - { - if (!VarSetOps::IsMember(this, raLclRegIntf[REG_EDX], varDsc->lvVarIndex)) - printf("Record interference between V%02u,T%02u and REG WRITE BARRIER -- ref assign\n", lclNum, - varDsc->lvVarIndex); - } -#endif - - /* Make sure that write barrier pointer variables never land in EDX */ - VarSetOps::AddElemD(this, raLclRegIntf[REG_EDX], varDsc->lvVarIndex); -#endif // NOGC_WRITE_BARRIERS -} - -/***************************************************************************** - * - * Predict the internal temp physical register usage for a block assignment tree, - * by setting tree->gtUsedRegs. - * Records the internal temp physical register usage for this tree. - * Returns a mask of interfering registers for this tree. - * - * Each of the switch labels in this function updates regMask and assigns tree->gtUsedRegs - * to the set of scratch registers needed when evaluating the tree. - * Generally tree->gtUsedRegs and the return value retMask are the same, except when the - * parameter "lockedRegs" conflicts with the computed tree->gtUsedRegs, in which case we - * predict additional internal temp physical registers to spill into. - * - * tree - is the child of a GT_IND node - * predictReg - what type of register does the tree need - * lockedRegs - are the registers which are currently held by a previously evaluated node. - * Don't modify lockedRegs as it is used at the end to compute a spill mask. - * rsvdRegs - registers which should not be allocated because they will - * be needed to evaluate a node in the future - * - Also, if rsvdRegs has the RBM_LASTUSE bit set then - * the rpLastUseVars set should be saved and restored - * so that we don't add any new variables to rpLastUseVars. - */ -regMaskTP Compiler::rpPredictBlkAsgRegUse(GenTree* tree, - rpPredictReg predictReg, - regMaskTP lockedRegs, - regMaskTP rsvdRegs) -{ - regMaskTP regMask = RBM_NONE; - regMaskTP interferingRegs = RBM_NONE; - - bool hasGCpointer = false; - bool dstIsOnStack = false; - bool useMemHelper = false; - bool useBarriers = false; - GenTreeBlk* dst = tree->gtGetOp1()->AsBlk(); - GenTree* dstAddr = dst->Addr(); - GenTree* srcAddrOrFill = tree->gtGetOp2IfPresent(); - - size_t blkSize = dst->gtBlkSize; - - hasGCpointer = (dst->HasGCPtr()); - - bool isCopyBlk = tree->OperIsCopyBlkOp(); - bool isCopyObj = isCopyBlk && hasGCpointer; - bool isInitBlk = tree->OperIsInitBlkOp(); - - if (isCopyBlk) - { - assert(srcAddrOrFill->OperIsIndir()); - srcAddrOrFill = srcAddrOrFill->AsIndir()->Addr(); - } - else - { - // For initBlk, we don't need to worry about the GC pointers. - hasGCpointer = false; - } - - if (blkSize != 0) - { - if (isCopyObj) - { - dstIsOnStack = (dstAddr->gtOper == GT_ADDR && (dstAddr->gtFlags & GTF_ADDR_ONSTACK)); - } - - if (isInitBlk) - { - if (srcAddrOrFill->OperGet() != GT_CNS_INT) - { - useMemHelper = true; - } - } - } - else - { - useMemHelper = true; - } - - if (hasGCpointer && !dstIsOnStack) - { - useBarriers = true; - } - -#ifdef _TARGET_ARM_ - // - // On ARM For COPYBLK & INITBLK we have special treatment for constant lengths. - // - if (!useMemHelper && !useBarriers) - { - bool useLoop = false; - unsigned fullStoreCount = blkSize / TARGET_POINTER_SIZE; - - // A mask to use to force the predictor to choose low registers (to reduce code size) - regMaskTP avoidReg = (RBM_R12 | RBM_LR); - - // Allow the src and dst to be used in place, unless we use a loop, in which - // case we will need scratch registers as we will be writing to them. - rpPredictReg srcAndDstPredict = PREDICT_REG; - - // Will we be using a loop to implement this INITBLK/COPYBLK? - if ((isCopyBlk && (fullStoreCount >= 8)) || (isInitBlk && (fullStoreCount >= 16))) - { - useLoop = true; - avoidReg = RBM_NONE; - srcAndDstPredict = PREDICT_SCRATCH_REG; - } - - if (tree->gtFlags & GTF_REVERSE_OPS) - { - regMask |= rpPredictTreeRegUse(srcAddrOrFill, srcAndDstPredict, lockedRegs, - dstAddr->gtRsvdRegs | avoidReg | RBM_LASTUSE); - regMask |= rpPredictTreeRegUse(dstAddr, srcAndDstPredict, lockedRegs | regMask, avoidReg); - } - else - { - regMask |= rpPredictTreeRegUse(dstAddr, srcAndDstPredict, lockedRegs, - srcAddrOrFill->gtRsvdRegs | avoidReg | RBM_LASTUSE); - regMask |= rpPredictTreeRegUse(srcAddrOrFill, srcAndDstPredict, lockedRegs | regMask, avoidReg); - } - - // We need at least one scratch register for a copyBlk - if (isCopyBlk) - { - // Pick a low register to reduce the code size - regMask |= rpPredictRegPick(TYP_INT, PREDICT_SCRATCH_REG, lockedRegs | regMask | avoidReg); - } - - if (useLoop) - { - if (isCopyBlk) - { - // We need a second temp register for a copyBlk (our code gen is load two/store two) - // Pick another low register to reduce the code size - regMask |= rpPredictRegPick(TYP_INT, PREDICT_SCRATCH_REG, lockedRegs | regMask | avoidReg); - } - - // We need a loop index register - regMask |= rpPredictRegPick(TYP_INT, PREDICT_SCRATCH_REG, lockedRegs | regMask); - } - - tree->gtUsedRegs = dstAddr->gtUsedRegs | srcAddrOrFill->gtUsedRegs | (regMaskSmall)regMask; - - return interferingRegs; - } -#endif - // What order should the Dest, Val/Src, and Size be calculated - GenTree* opsPtr[3]; - regMaskTP regsPtr[3]; - -#if defined(_TARGET_XARCH_) - fgOrderBlockOps(tree, RBM_EDI, (isInitBlk) ? RBM_EAX : RBM_ESI, RBM_ECX, opsPtr, regsPtr); - - // We're going to use these, might as well make them available now - - codeGen->regSet.rsSetRegsModified(RBM_EDI | RBM_ECX); - if (isCopyBlk) - codeGen->regSet.rsSetRegsModified(RBM_ESI); - -#elif defined(_TARGET_ARM_) - - if (useMemHelper) - { - // For all other cases that involve non-constants, we just call memcpy/memset - // JIT helpers - fgOrderBlockOps(tree, RBM_ARG_0, RBM_ARG_1, RBM_ARG_2, opsPtr, regsPtr); - interferingRegs |= RBM_CALLEE_TRASH; -#ifdef DEBUG - if (verbose) - printf("Adding interference with RBM_CALLEE_TRASH for memcpy/memset\n"); -#endif - } - else // useBarriers - { - assert(useBarriers); - assert(isCopyBlk); - - fgOrderBlockOps(tree, RBM_ARG_0, RBM_ARG_1, REG_TMP_1, opsPtr, regsPtr); - - // For this case Codegen will call the CORINFO_HELP_ASSIGN_BYREF helper - interferingRegs |= RBM_CALLEE_TRASH_NOGC; -#ifdef DEBUG - if (verbose) - printf("Adding interference with RBM_CALLEE_TRASH_NOGC for Byref WriteBarrier\n"); -#endif - } -#else // !_TARGET_X86_ && !_TARGET_ARM_ -#error "Non-ARM or x86 _TARGET_ in RegPredict for INITBLK/COPYBLK" -#endif // !_TARGET_X86_ && !_TARGET_ARM_ - regMaskTP opsPtr2RsvdRegs = opsPtr[2] == nullptr ? RBM_NONE : opsPtr[2]->gtRsvdRegs; - regMask |= rpPredictTreeRegUse(opsPtr[0], rpGetPredictForMask(regsPtr[0]), lockedRegs, - opsPtr[1]->gtRsvdRegs | opsPtr2RsvdRegs | RBM_LASTUSE); - regMask |= regsPtr[0]; - opsPtr[0]->gtUsedRegs |= regsPtr[0]; - rpRecordRegIntf(regsPtr[0], compCurLife DEBUGARG("movsd dest")); - - regMask |= rpPredictTreeRegUse(opsPtr[1], rpGetPredictForMask(regsPtr[1]), lockedRegs | regMask, - opsPtr2RsvdRegs | RBM_LASTUSE); - regMask |= regsPtr[1]; - opsPtr[1]->gtUsedRegs |= regsPtr[1]; - rpRecordRegIntf(regsPtr[1], compCurLife DEBUGARG("movsd src")); - - regMaskSmall opsPtr2UsedRegs = (regMaskSmall)regsPtr[2]; - if (opsPtr[2] == nullptr) - { - // If we have no "size" node, we will predict that regsPtr[2] will be used for the size. - // Note that it is quite possible that no register is required, but this preserves - // former behavior. - regMask |= rpPredictRegPick(TYP_INT, rpGetPredictForMask(regsPtr[2]), lockedRegs | regMask); - rpRecordRegIntf(regsPtr[2], compCurLife DEBUGARG("tmp use")); - } - else - { - regMask |= rpPredictTreeRegUse(opsPtr[2], rpGetPredictForMask(regsPtr[2]), lockedRegs | regMask, RBM_NONE); - opsPtr[2]->gtUsedRegs |= opsPtr2UsedRegs; - } - regMask |= opsPtr2UsedRegs; - - tree->gtUsedRegs = opsPtr[0]->gtUsedRegs | opsPtr[1]->gtUsedRegs | opsPtr2UsedRegs | (regMaskSmall)regMask; - return interferingRegs; -} - -/***************************************************************************** - * - * Predict the internal temp physical register usage for a tree by setting tree->gtUsedRegs. - * Returns a regMask with the internal temp physical register usage for this tree. - * - * Each of the switch labels in this function updates regMask and assigns tree->gtUsedRegs - * to the set of scratch registers needed when evaluating the tree. - * Generally tree->gtUsedRegs and the return value retMask are the same, except when the - * parameter "lockedRegs" conflicts with the computed tree->gtUsedRegs, in which case we - * predict additional internal temp physical registers to spill into. - * - * tree - is the child of a GT_IND node - * predictReg - what type of register does the tree need - * lockedRegs - are the registers which are currently held by a previously evaluated node. - * Don't modify lockedRegs as it is used at the end to compute a spill mask. - * rsvdRegs - registers which should not be allocated because they will - * be needed to evaluate a node in the future - * - Also, if rsvdRegs has the RBM_LASTUSE bit set then - * the rpLastUseVars set should be saved and restored - * so that we don't add any new variables to rpLastUseVars. - */ - -#pragma warning(disable : 4701) - -#ifdef _PREFAST_ -#pragma warning(push) -#pragma warning(disable : 21000) // Suppress PREFast warning about overly large function -#endif -regMaskTP Compiler::rpPredictTreeRegUse(GenTree* tree, - rpPredictReg predictReg, - regMaskTP lockedRegs, - regMaskTP rsvdRegs) -{ - regMaskTP regMask = DUMMY_INIT(RBM_ILLEGAL); - regMaskTP op2Mask; - regMaskTP tmpMask; - rpPredictReg op1PredictReg; - rpPredictReg op2PredictReg; - LclVarDsc* varDsc = NULL; - VARSET_TP oldLastUseVars(VarSetOps::UninitVal()); - - VARSET_TP varBits(VarSetOps::UninitVal()); - VARSET_TP lastUseVarBits(VarSetOps::MakeEmpty(this)); - - bool restoreLastUseVars = false; - regMaskTP interferingRegs = RBM_NONE; - -#ifdef DEBUG - // if (verbose) printf("rpPredictTreeRegUse() [%08x]\n", tree); - noway_assert(tree); - noway_assert(((RBM_ILLEGAL & RBM_ALLINT) == 0)); - noway_assert(RBM_ILLEGAL); - noway_assert((lockedRegs & RBM_ILLEGAL) == 0); - /* impossible values, to make sure that we set them */ - tree->gtUsedRegs = RBM_ILLEGAL; -#endif - - /* Figure out what kind of a node we have */ - - genTreeOps oper = tree->OperGet(); - var_types type = tree->TypeGet(); - unsigned kind = tree->OperKind(); - - // In the comma case, we care about whether this is "effectively" ADDR(IND(...)) - genTreeOps effectiveOper = tree->gtEffectiveVal()->OperGet(); - if ((predictReg == PREDICT_ADDR) && (effectiveOper != GT_IND)) - predictReg = PREDICT_NONE; - else if (rpHasVarIndexForPredict(predictReg)) - { - // The only place where predictReg is set to a var is in the PURE - // assignment case where varIndex is the var being assigned to. - // We need to check whether the variable is used between here and - // its redefinition. - unsigned varIndex = rpGetVarIndexForPredict(predictReg); - unsigned lclNum = lvaTrackedToVarNum[varIndex]; - bool found = false; - for (GenTree* nextTree = tree->gtNext; nextTree != NULL && !found; nextTree = nextTree->gtNext) - { - if (nextTree->gtOper == GT_LCL_VAR && nextTree->gtLclVarCommon.gtLclNum == lclNum) - { - // Is this the pure assignment? - if ((nextTree->gtFlags & GTF_VAR_DEF) == 0) - { - predictReg = PREDICT_SCRATCH_REG; - } - found = true; - break; - } - } - assert(found); - } - - if (rsvdRegs & RBM_LASTUSE) - { - restoreLastUseVars = true; - VarSetOps::Assign(this, oldLastUseVars, rpLastUseVars); - rsvdRegs &= ~RBM_LASTUSE; - } - - /* Is this a constant or leaf node? */ - - if (kind & (GTK_CONST | GTK_LEAF)) - { - bool lastUse = false; - regMaskTP enregMask = RBM_NONE; - - switch (oper) - { -#ifdef _TARGET_ARM_ - case GT_CNS_DBL: - // Codegen for floating point constants on the ARM is currently - // movw/movt rT1, - // movw/movt rT2, - // vmov.i2d dT0, rT1,rT2 - // - // For TYP_FLOAT one integer register is required - // - // These integer register(s) immediately die - tmpMask = rpPredictRegPick(TYP_INT, PREDICT_REG, lockedRegs | rsvdRegs); - if (type == TYP_DOUBLE) - { - // For TYP_DOUBLE a second integer register is required - // - tmpMask |= rpPredictRegPick(TYP_INT, PREDICT_REG, lockedRegs | rsvdRegs | tmpMask); - } - - // We also need a floating point register that we keep - // - if (predictReg == PREDICT_NONE) - predictReg = PREDICT_SCRATCH_REG; - - regMask = rpPredictRegPick(type, predictReg, lockedRegs | rsvdRegs); - tree->gtUsedRegs = regMask | tmpMask; - goto RETURN_CHECK; -#endif - - case GT_CNS_INT: - case GT_CNS_LNG: - - if (rpHasVarIndexForPredict(predictReg)) - { - unsigned tgtIndex = rpGetVarIndexForPredict(predictReg); - rpAsgVarNum = tgtIndex; - - // We don't need any register as we plan on writing to the rpAsgVarNum register - predictReg = PREDICT_NONE; - - LclVarDsc* tgtVar = lvaTable + lvaTrackedToVarNum[tgtIndex]; - tgtVar->lvDependReg = true; - - if (type == TYP_LONG) - { - assert(oper == GT_CNS_LNG); - - if (tgtVar->lvOtherReg == REG_STK) - { - // Well we do need one register for a partially enregistered - type = TYP_INT; - predictReg = PREDICT_SCRATCH_REG; - } - } - } - else - { -#if !CPU_LOAD_STORE_ARCH - /* If the constant is a handle then it will need to have a relocation - applied to it. It will need to be loaded into a register. - But never throw away an existing hint. - */ - if (opts.compReloc && tree->IsCnsIntOrI() && tree->IsIconHandle()) -#endif - { - if (predictReg == PREDICT_NONE) - predictReg = PREDICT_SCRATCH_REG; - } - } - break; - - case GT_NO_OP: - break; - - case GT_CLS_VAR: - if ((predictReg == PREDICT_NONE) && (genActualType(type) == TYP_INT) && - (genTypeSize(type) < sizeof(int))) - { - predictReg = PREDICT_SCRATCH_REG; - } -#ifdef _TARGET_ARM_ - // Unaligned loads/stores for floating point values must first be loaded into integer register(s) - // - if ((tree->gtFlags & GTF_IND_UNALIGNED) && varTypeIsFloating(type)) - { - // These integer register(s) immediately die - tmpMask = rpPredictRegPick(TYP_INT, PREDICT_REG, lockedRegs | rsvdRegs); - // Two integer registers are required for a TYP_DOUBLE - if (type == TYP_DOUBLE) - tmpMask |= rpPredictRegPick(TYP_INT, PREDICT_REG, lockedRegs | rsvdRegs | tmpMask); - } - // We need a temp register in some cases of loads/stores to a class var - if (predictReg == PREDICT_NONE) - { - predictReg = PREDICT_SCRATCH_REG; - } -#endif - if (rpHasVarIndexForPredict(predictReg)) - { - unsigned tgtIndex = rpGetVarIndexForPredict(predictReg); - rpAsgVarNum = tgtIndex; - - // We don't need any register as we plan on writing to the rpAsgVarNum register - predictReg = PREDICT_NONE; - - LclVarDsc* tgtVar = lvaTable + lvaTrackedToVarNum[tgtIndex]; - tgtVar->lvDependReg = true; - - if (type == TYP_LONG) - { - if (tgtVar->lvOtherReg == REG_STK) - { - // Well we do need one register for a partially enregistered - type = TYP_INT; - predictReg = PREDICT_SCRATCH_REG; - } - } - } - break; - - case GT_LCL_FLD: -#ifdef _TARGET_ARM_ - // Check for a misalignment on a Floating Point field - // - if (varTypeIsFloating(type)) - { - if ((tree->gtLclFld.gtLclOffs % emitTypeSize(tree->TypeGet())) != 0) - { - // These integer register(s) immediately die - tmpMask = rpPredictRegPick(TYP_INT, PREDICT_REG, lockedRegs | rsvdRegs); - // Two integer registers are required for a TYP_DOUBLE - if (type == TYP_DOUBLE) - tmpMask |= rpPredictRegPick(TYP_INT, PREDICT_REG, lockedRegs | rsvdRegs | tmpMask); - } - } -#endif - __fallthrough; - - case GT_LCL_VAR: - case GT_REG_VAR: - - varDsc = lvaTable + tree->gtLclVarCommon.gtLclNum; - - VarSetOps::Assign(this, varBits, fgGetVarBits(tree)); - compUpdateLifeVar(tree, &lastUseVarBits); - lastUse = !VarSetOps::IsEmpty(this, lastUseVarBits); - -#if FEATURE_STACK_FP_X87 - // If it's a floating point var, there's nothing to do - if (varTypeIsFloating(type)) - { - tree->gtUsedRegs = RBM_NONE; - regMask = RBM_NONE; - goto RETURN_CHECK; - } -#endif - - // If the variable is already a register variable, no need to go further. - if (oper == GT_REG_VAR) - break; - - /* Apply the type of predictReg to the LCL_VAR */ - - if (predictReg == PREDICT_REG) - { - PREDICT_REG_COMMON: - if (varDsc->lvRegNum == REG_STK) - break; - - goto GRAB_COUNT; - } - else if (predictReg == PREDICT_SCRATCH_REG) - { - noway_assert(predictReg == PREDICT_SCRATCH_REG); - - /* Is this the last use of a local var? */ - if (lastUse) - { - if (VarSetOps::IsEmptyIntersection(this, rpUseInPlace, lastUseVarBits)) - goto PREDICT_REG_COMMON; - } - } - else if (rpHasVarIndexForPredict(predictReg)) - { - /* Get the tracked local variable that has an lvVarIndex of tgtIndex1 */ - { - unsigned tgtIndex1 = rpGetVarIndexForPredict(predictReg); - LclVarDsc* tgtVar = lvaTable + lvaTrackedToVarNum[tgtIndex1]; - VarSetOps::MakeSingleton(this, tgtIndex1); - - noway_assert(tgtVar->lvVarIndex == tgtIndex1); - noway_assert(tgtVar->lvRegNum != REG_STK); /* Must have been enregistered */ -#ifndef _TARGET_AMD64_ - // On amd64 we have the occasional spec-allowed implicit conversion from TYP_I_IMPL to TYP_INT - // so this assert is meaningless - noway_assert((type != TYP_LONG) || (tgtVar->TypeGet() == TYP_LONG)); -#endif // !_TARGET_AMD64_ - - if (varDsc->lvTracked) - { - unsigned srcIndex; - srcIndex = varDsc->lvVarIndex; - - // If this register has it's last use here then we will prefer - // to color to the same register as tgtVar. - if (lastUse) - { - /* - * Add an entry in the lvaVarPref graph to indicate - * that it would be worthwhile to color these two variables - * into the same physical register. - * This will help us avoid having an extra copy instruction - */ - VarSetOps::AddElemD(this, lvaVarPref[srcIndex], tgtIndex1); - VarSetOps::AddElemD(this, lvaVarPref[tgtIndex1], srcIndex); - } - - // Add a variable interference from srcIndex to each of the last use variables - if (!VarSetOps::IsEmpty(this, rpLastUseVars)) - { - rpRecordVarIntf(srcIndex, rpLastUseVars DEBUGARG("src reg conflict")); - } - } - rpAsgVarNum = tgtIndex1; - - /* We will rely on the target enregistered variable from the GT_ASG */ - varDsc = tgtVar; - } - GRAB_COUNT: - unsigned grabCount; - grabCount = 0; - - if (genIsValidFloatReg(varDsc->lvRegNum)) - { - enregMask = genRegMaskFloat(varDsc->lvRegNum, varDsc->TypeGet()); - } - else - { - enregMask = genRegMask(varDsc->lvRegNum); - } - -#ifdef _TARGET_ARM_ - if ((type == TYP_DOUBLE) && (varDsc->TypeGet() == TYP_FLOAT)) - { - // We need to compute the intermediate value using a TYP_DOUBLE - // but we storing the result in a TYP_SINGLE enregistered variable - // - grabCount++; - } - else -#endif - { - /* We can't trust a prediction of rsvdRegs or lockedRegs sets */ - if (enregMask & (rsvdRegs | lockedRegs)) - { - grabCount++; - } -#ifndef _TARGET_64BIT_ - if (type == TYP_LONG) - { - if (varDsc->lvOtherReg != REG_STK) - { - tmpMask = genRegMask(varDsc->lvOtherReg); - enregMask |= tmpMask; - - /* We can't trust a prediction of rsvdRegs or lockedRegs sets */ - if (tmpMask & (rsvdRegs | lockedRegs)) - grabCount++; - } - else // lvOtherReg == REG_STK - { - grabCount++; - } - } -#endif // _TARGET_64BIT_ - } - - varDsc->lvDependReg = true; - - if (grabCount == 0) - { - /* Does not need a register */ - predictReg = PREDICT_NONE; - // noway_assert(!VarSetOps::IsEmpty(this, varBits)); - VarSetOps::UnionD(this, rpUseInPlace, varBits); - } - else // (grabCount > 0) - { -#ifndef _TARGET_64BIT_ - /* For TYP_LONG and we only need one register then change the type to TYP_INT */ - if ((type == TYP_LONG) && (grabCount == 1)) - { - /* We will need to pick one register */ - type = TYP_INT; - // noway_assert(!VarSetOps::IsEmpty(this, varBits)); - VarSetOps::UnionD(this, rpUseInPlace, varBits); - } - noway_assert((type == TYP_DOUBLE) || - (grabCount == (genTypeSize(genActualType(type)) / REGSIZE_BYTES))); -#else // !_TARGET_64BIT_ - noway_assert(grabCount == 1); -#endif // !_TARGET_64BIT_ - } - } - else if (type == TYP_STRUCT) - { -#ifdef _TARGET_ARM_ - // TODO-ARM-Bug?: Passing structs in registers on ARM hits an assert here when - // predictReg is PREDICT_REG_R0 to PREDICT_REG_R3 - // As a workaround we just bash it to PREDICT_NONE here - // - if (predictReg != PREDICT_NONE) - predictReg = PREDICT_NONE; -#endif - // Currently predictReg is saying that we will not need any scratch registers - noway_assert(predictReg == PREDICT_NONE); - - /* We may need to sign or zero extend a small type when pushing a struct */ - if (varDsc->lvPromoted && !varDsc->lvAddrExposed) - { - for (unsigned varNum = varDsc->lvFieldLclStart; - varNum < varDsc->lvFieldLclStart + varDsc->lvFieldCnt; varNum++) - { - LclVarDsc* fldVar = lvaTable + varNum; - - if (fldVar->lvStackAligned()) - { - // When we are stack aligned Codegen will just use - // a push instruction and thus doesn't need any register - // since we can push both a register or a stack frame location - continue; - } - - if (varTypeIsByte(fldVar->TypeGet())) - { - // We will need to reserve one byteable register, - // - type = TYP_BYTE; - predictReg = PREDICT_SCRATCH_REG; -#if CPU_HAS_BYTE_REGS - // It is best to enregister this fldVar in a byteable register - // - fldVar->addPrefReg(RBM_BYTE_REG_FLAG, this); -#endif - } - else if (varTypeIsShort(fldVar->TypeGet())) - { - bool isEnregistered = fldVar->lvTracked && (fldVar->lvRegNum != REG_STK); - // If fldVar is not enregistered then we will need a scratch register - // - if (!isEnregistered) - { - // We will need either an int register or a byte register - // If we are not requesting a byte register we will request an int register - // - if (type != TYP_BYTE) - type = TYP_INT; - predictReg = PREDICT_SCRATCH_REG; - } - } - } - } - } - else - { - regMaskTP preferReg = rpPredictRegMask(predictReg, type); - if (preferReg != 0) - { - if ((genTypeStSz(type) == 1) || (genCountBits(preferReg) <= genTypeStSz(type))) - { - varDsc->addPrefReg(preferReg, this); - } - } - } - break; /* end of case GT_LCL_VAR */ - - case GT_JMP: - tree->gtUsedRegs = RBM_NONE; - regMask = RBM_NONE; - -#if defined(_TARGET_ARM_) && defined(PROFILING_SUPPORTED) - // Mark the registers required to emit a tailcall profiler callback - if (compIsProfilerHookNeeded()) - { - tree->gtUsedRegs |= RBM_PROFILER_JMP_USED; - } -#endif - goto RETURN_CHECK; - - default: - break; - } /* end of switch (oper) */ - - /* If we don't need to evaluate to register, regmask is the empty set */ - /* Otherwise we grab a temp for the local variable */ - - if (predictReg == PREDICT_NONE) - regMask = RBM_NONE; - else - { - regMask = rpPredictRegPick(type, predictReg, lockedRegs | rsvdRegs | enregMask); - - if ((oper == GT_LCL_VAR) && (tree->TypeGet() == TYP_STRUCT)) - { - /* We need to sign or zero extend a small type when pushing a struct */ - noway_assert((type == TYP_INT) || (type == TYP_BYTE)); - - varDsc = lvaTable + tree->gtLclVarCommon.gtLclNum; - noway_assert(varDsc->lvPromoted && !varDsc->lvAddrExposed); - - for (unsigned varNum = varDsc->lvFieldLclStart; varNum < varDsc->lvFieldLclStart + varDsc->lvFieldCnt; - varNum++) - { - LclVarDsc* fldVar = lvaTable + varNum; - if (fldVar->lvTracked) - { - VARSET_TP fldBit(VarSetOps::MakeSingleton(this, fldVar->lvVarIndex)); - rpRecordRegIntf(regMask, fldBit DEBUGARG( - "need scratch register when pushing a small field of a struct")); - } - } - } - } - - /* Update the set of lastUse variables that we encountered so far */ - if (lastUse) - { - VarSetOps::UnionD(this, rpLastUseVars, lastUseVarBits); - VARSET_TP varAsSet(VarSetOps::MakeCopy(this, lastUseVarBits)); - - /* - * Add interference from any previously locked temps into this last use variable. - */ - if (lockedRegs) - { - rpRecordRegIntf(lockedRegs, varAsSet DEBUGARG("last use Predict lockedRegs")); - } - /* - * Add interference from any reserved temps into this last use variable. - */ - if (rsvdRegs) - { - rpRecordRegIntf(rsvdRegs, varAsSet DEBUGARG("last use Predict rsvdRegs")); - } - /* - * For partially enregistered longs add an interference with the - * register return by rpPredictRegPick - */ - if ((type == TYP_INT) && (tree->TypeGet() == TYP_LONG)) - { - rpRecordRegIntf(regMask, varAsSet DEBUGARG("last use with partial enreg")); - } - } - - tree->gtUsedRegs = (regMaskSmall)regMask; - goto RETURN_CHECK; - } - - /* Is it a 'simple' unary/binary operator? */ - - if (kind & GTK_SMPOP) - { - GenTree* op1 = tree->gtOp.gtOp1; - GenTree* op2 = tree->gtGetOp2IfPresent(); - - GenTree* opsPtr[3]; - regMaskTP regsPtr[3]; - - VARSET_TP startAsgUseInPlaceVars(VarSetOps::UninitVal()); - - switch (oper) - { - case GT_ASG: - - /* Is the value being assigned into a LCL_VAR? */ - if (op1->gtOper == GT_LCL_VAR) - { - varDsc = lvaTable + op1->gtLclVarCommon.gtLclNum; - - /* Are we assigning a LCL_VAR the result of a call? */ - if (op2->gtOper == GT_CALL) - { - /* Set a preferred register for the LCL_VAR */ - if (isRegPairType(varDsc->TypeGet())) - varDsc->addPrefReg(RBM_LNGRET, this); - else if (!varTypeIsFloating(varDsc->TypeGet())) - varDsc->addPrefReg(RBM_INTRET, this); -#ifdef _TARGET_AMD64_ - else - varDsc->addPrefReg(RBM_FLOATRET, this); -#endif - /* - * When assigning the result of a call we don't - * bother trying to target the right side of the - * assignment, since we have a fixed calling convention. - */ - } - else if (varDsc->lvTracked) - { - // We interfere with uses in place - if (!VarSetOps::IsEmpty(this, rpUseInPlace)) - { - rpRecordVarIntf(varDsc->lvVarIndex, rpUseInPlace DEBUGARG("Assign UseInPlace conflict")); - } - - // Did we predict that this local will be fully enregistered? - // and the assignment type is the same as the expression type? - // and it is dead on the right side of the assignment? - // and we current have no other rpAsgVarNum active? - // - if ((varDsc->lvRegNum != REG_STK) && ((type != TYP_LONG) || (varDsc->lvOtherReg != REG_STK)) && - (type == op2->TypeGet()) && (op1->gtFlags & GTF_VAR_DEF) && (rpAsgVarNum == -1)) - { - // - // Yes, we should try to target the right side (op2) of this - // assignment into the (enregistered) tracked variable. - // - - op1PredictReg = PREDICT_NONE; /* really PREDICT_REG, but we've already done the check */ - op2PredictReg = rpGetPredictForVarIndex(varDsc->lvVarIndex); - - // Remember that this is a new use in place - - // We've added "new UseInPlace"; remove from the global set. - VarSetOps::RemoveElemD(this, rpUseInPlace, varDsc->lvVarIndex); - - // Note that later when we walk down to the leaf node for op2 - // if we decide to actually use the register for the 'varDsc' - // to enregister the operand, the we will set rpAsgVarNum to - // varDsc->lvVarIndex, by extracting this value using - // rpGetVarIndexForPredict() - // - // Also we reset rpAsgVarNum back to -1 after we have finished - // predicting the current GT_ASG node - // - goto ASG_COMMON; - } - } - } - else if (tree->OperIsBlkOp()) - { - interferingRegs |= rpPredictBlkAsgRegUse(tree, predictReg, lockedRegs, rsvdRegs); - regMask = 0; - goto RETURN_CHECK; - } - __fallthrough; - - case GT_CHS: - - case GT_ASG_OR: - case GT_ASG_XOR: - case GT_ASG_AND: - case GT_ASG_SUB: - case GT_ASG_ADD: - case GT_ASG_MUL: - case GT_ASG_DIV: - case GT_ASG_UDIV: - - /* We can't use "reg = addr" for TYP_LONG or if op2 is a short type */ - if ((type != TYP_LONG) && !varTypeIsSmall(op2->gtType)) - { - /* Is the value being assigned into an enregistered LCL_VAR? */ - /* For debug code we only allow a simple op2 to be assigned */ - if ((op1->gtOper == GT_LCL_VAR) && (!opts.compDbgCode || rpCanAsgOperWithoutReg(op2, false))) - { - varDsc = lvaTable + op1->gtLclVarCommon.gtLclNum; - /* Did we predict that this local will be enregistered? */ - if (varDsc->lvRegNum != REG_STK) - { - /* Yes, we can use "reg = addr" */ - - op1PredictReg = PREDICT_NONE; /* really PREDICT_REG, but we've already done the check */ - op2PredictReg = PREDICT_NONE; - - goto ASG_COMMON; - } - } - } - -#if CPU_LOAD_STORE_ARCH - if (oper != GT_ASG) - { - op1PredictReg = PREDICT_REG; - op2PredictReg = PREDICT_REG; - } - else -#endif - { - /* - * Otherwise, initialize the normal forcing of operands: - * "addr = reg" - */ - op1PredictReg = PREDICT_ADDR; - op2PredictReg = PREDICT_REG; - } - - ASG_COMMON: - -#if !CPU_LOAD_STORE_ARCH - if (op2PredictReg != PREDICT_NONE) - { - /* Is the value being assigned a simple one? */ - if (rpCanAsgOperWithoutReg(op2, false)) - op2PredictReg = PREDICT_NONE; - } -#endif - - bool simpleAssignment; - simpleAssignment = false; - - if ((oper == GT_ASG) && (op1->gtOper == GT_LCL_VAR)) - { - // Add a variable interference from the assign target - // to each of the last use variables - if (!VarSetOps::IsEmpty(this, rpLastUseVars)) - { - varDsc = lvaTable + op1->gtLclVarCommon.gtLclNum; - - if (varDsc->lvTracked) - { - unsigned varIndex = varDsc->lvVarIndex; - - rpRecordVarIntf(varIndex, rpLastUseVars DEBUGARG("Assign conflict")); - } - } - - /* Record whether this tree is a simple assignment to a local */ - - simpleAssignment = ((type != TYP_LONG) || !opts.compDbgCode); - } - - bool requireByteReg; - requireByteReg = false; - -#if CPU_HAS_BYTE_REGS - /* Byte-assignments need the byte registers, unless op1 is an enregistered local */ - - if (varTypeIsByte(type) && - ((op1->gtOper != GT_LCL_VAR) || (lvaTable[op1->gtLclVarCommon.gtLclNum].lvRegNum == REG_STK))) - - { - // Byte-assignments typically need a byte register - requireByteReg = true; - - if (op1->gtOper == GT_LCL_VAR) - { - varDsc = lvaTable + op1->gtLclVar.gtLclNum; - - // Did we predict that this local will be enregistered? - if (varDsc->lvTracked && (varDsc->lvRegNum != REG_STK) && (oper != GT_CHS)) - { - // We don't require a byte register when op1 is an enregistered local */ - requireByteReg = false; - } - - // Is op1 part of an Assign-Op or is the RHS a simple memory indirection? - if ((oper != GT_ASG) || (op2->gtOper == GT_IND) || (op2->gtOper == GT_CLS_VAR)) - { - // We should try to put op1 in an byte register - varDsc->addPrefReg(RBM_BYTE_REG_FLAG, this); - } - } - } -#endif - - VarSetOps::Assign(this, startAsgUseInPlaceVars, rpUseInPlace); - - bool isWriteBarrierAsgNode; - isWriteBarrierAsgNode = codeGen->gcInfo.gcIsWriteBarrierAsgNode(tree); -#ifdef DEBUG - GCInfo::WriteBarrierForm wbf; - if (isWriteBarrierAsgNode) - wbf = codeGen->gcInfo.gcIsWriteBarrierCandidate(tree->gtOp.gtOp1, tree->gtOp.gtOp2); - else - wbf = GCInfo::WBF_NoBarrier; -#endif // DEBUG - - regMaskTP wbaLockedRegs; - wbaLockedRegs = lockedRegs; - if (isWriteBarrierAsgNode) - { -#if defined(_TARGET_X86_) && NOGC_WRITE_BARRIERS -#ifdef DEBUG - if (wbf != GCInfo::WBF_NoBarrier_CheckNotHeapInDebug) - { -#endif // DEBUG - wbaLockedRegs |= RBM_WRITE_BARRIER; - op1->gtRsvdRegs |= RBM_WRITE_BARRIER; // This will steer op2 away from REG_WRITE_BARRIER - assert(REG_WRITE_BARRIER == REG_EDX); - op1PredictReg = PREDICT_REG_EDX; -#ifdef DEBUG - } - else -#endif // DEBUG -#endif // defined(_TARGET_X86_) && NOGC_WRITE_BARRIERS - -#if defined(DEBUG) || !(defined(_TARGET_X86_) && NOGC_WRITE_BARRIERS) - { -#ifdef _TARGET_X86_ - op1PredictReg = PREDICT_REG_ECX; - op2PredictReg = PREDICT_REG_EDX; -#elif defined(_TARGET_ARM_) - op1PredictReg = PREDICT_REG_R0; - op2PredictReg = PREDICT_REG_R1; - - // This is my best guess as to what the previous code meant by checking "gtRngChkLen() == NULL". - if ((op1->OperGet() == GT_IND) && (op1->gtOp.gtOp1->OperGet() != GT_ARR_BOUNDS_CHECK)) - { - op1 = op1->gtOp.gtOp1; - } -#else // !_TARGET_X86_ && !_TARGET_ARM_ -#error "Non-ARM or x86 _TARGET_ in RegPredict for WriteBarrierAsg" -#endif - } -#endif - } - - /* Are we supposed to evaluate RHS first? */ - - if (tree->gtFlags & GTF_REVERSE_OPS) - { - op2Mask = rpPredictTreeRegUse(op2, op2PredictReg, lockedRegs, rsvdRegs | op1->gtRsvdRegs); - -#if CPU_HAS_BYTE_REGS - // Should we insure that op2 gets evaluated into a byte register? - if (requireByteReg && ((op2Mask & RBM_BYTE_REGS) == 0)) - { - // We need to grab a byte-able register, (i.e. EAX, EDX, ECX, EBX) - // and we can't select one that is already reserved (i.e. lockedRegs) - // - op2Mask |= rpPredictRegPick(type, PREDICT_SCRATCH_REG, (lockedRegs | RBM_NON_BYTE_REGS)); - op2->gtUsedRegs |= op2Mask; - - // No longer a simple assignment because we're using extra registers and might - // have interference between op1 and op2. See DevDiv #136681 - simpleAssignment = false; - } -#endif - /* - * For a simple assignment we don't want the op2Mask to be - * marked as interferring with the LCL_VAR, since it is likely - * that we will want to enregister the LCL_VAR in exactly - * the register that is used to compute op2 - */ - tmpMask = lockedRegs; - - if (!simpleAssignment) - tmpMask |= op2Mask; - - regMask = rpPredictTreeRegUse(op1, op1PredictReg, tmpMask, RBM_NONE); - - // Did we relax the register prediction for op1 and op2 above ? - // - because we are depending upon op1 being enregistered - // - if ((op1PredictReg == PREDICT_NONE) && - ((op2PredictReg == PREDICT_NONE) || rpHasVarIndexForPredict(op2PredictReg))) - { - /* We must be assigning into an enregistered LCL_VAR */ - noway_assert(op1->gtOper == GT_LCL_VAR); - varDsc = lvaTable + op1->gtLclVar.gtLclNum; - noway_assert(varDsc->lvRegNum != REG_STK); - - /* We need to set lvDependReg, in case we lose the enregistration of op1 */ - varDsc->lvDependReg = true; - } - } - else - { - // For the case of simpleAssignments op2 should always be evaluated first - noway_assert(!simpleAssignment); - - regMask = rpPredictTreeRegUse(op1, op1PredictReg, lockedRegs, rsvdRegs | op2->gtRsvdRegs); - if (isWriteBarrierAsgNode) - { - wbaLockedRegs |= op1->gtUsedRegs; - } - op2Mask = rpPredictTreeRegUse(op2, op2PredictReg, wbaLockedRegs | regMask, RBM_NONE); - -#if CPU_HAS_BYTE_REGS - // Should we insure that op2 gets evaluated into a byte register? - if (requireByteReg && ((op2Mask & RBM_BYTE_REGS) == 0)) - { - // We need to grab a byte-able register, (i.e. EAX, EDX, ECX, EBX) - // and we can't select one that is already reserved (i.e. lockedRegs or regMask) - // - op2Mask |= - rpPredictRegPick(type, PREDICT_SCRATCH_REG, (lockedRegs | regMask | RBM_NON_BYTE_REGS)); - op2->gtUsedRegs |= op2Mask; - } -#endif - } - - if (rpHasVarIndexForPredict(op2PredictReg)) - { - rpAsgVarNum = -1; - } - - if (isWriteBarrierAsgNode) - { -#if NOGC_WRITE_BARRIERS -#ifdef DEBUG - if (wbf != GCInfo::WBF_NoBarrier_CheckNotHeapInDebug) - { -#endif // DEBUG - - /* Steer computation away from REG_WRITE_BARRIER as the pointer is - passed to the write-barrier call in REG_WRITE_BARRIER */ - - regMask = op2Mask; - - if (op1->gtOper == GT_IND) - { - GenTree* rv1; - GenTree* rv2; - unsigned mul, cns; - bool rev; - - /* Special handling of indirect assigns for write barrier */ - - bool yes = codeGen->genCreateAddrMode(op1->gtOp.gtOp1, -1, true, RBM_NONE, &rev, &rv1, &rv2, - &mul, &cns); - - /* Check address mode for enregisterable locals */ - - if (yes) - { - if (rv1 != NULL && rv1->gtOper == GT_LCL_VAR) - { - rpPredictRefAssign(rv1->gtLclVarCommon.gtLclNum); - } - if (rv2 != NULL && rv2->gtOper == GT_LCL_VAR) - { - rpPredictRefAssign(rv2->gtLclVarCommon.gtLclNum); - } - } - } - - if (op2->gtOper == GT_LCL_VAR) - { - rpPredictRefAssign(op2->gtLclVarCommon.gtLclNum); - } - - // Add a register interference for REG_WRITE_BARRIER to each of the last use variables - if (!VarSetOps::IsEmpty(this, rpLastUseVars)) - { - rpRecordRegIntf(RBM_WRITE_BARRIER, - rpLastUseVars DEBUGARG("WriteBarrier and rpLastUseVars conflict")); - } - tree->gtUsedRegs |= RBM_WRITE_BARRIER; -#ifdef DEBUG - } - else -#endif // DEBUG -#endif // NOGC_WRITE_BARRIERS - -#if defined(DEBUG) || !NOGC_WRITE_BARRIERS - { -#ifdef _TARGET_ARM_ -#ifdef DEBUG - if (verbose) - printf("Adding interference with RBM_CALLEE_TRASH_NOGC for NoGC WriteBarrierAsg\n"); -#endif - // - // For the ARM target we have an optimized JIT Helper - // that only trashes a subset of the callee saved registers - // - - // NOTE: Adding it to the gtUsedRegs will cause the interference to - // be added appropriately - - // the RBM_CALLEE_TRASH_NOGC set is killed. We will record this in interferingRegs - // instead of gtUsedRegs, because the latter will be modified later, but we need - // to remember to add the interference. - - interferingRegs |= RBM_CALLEE_TRASH_NOGC; - - op1->gtUsedRegs |= RBM_R0; - op2->gtUsedRegs |= RBM_R1; -#else // _TARGET_ARM_ - -#ifdef DEBUG - if (verbose) - printf("Adding interference with RBM_CALLEE_TRASH for NoGC WriteBarrierAsg\n"); -#endif - // We have to call a normal JIT helper to perform the Write Barrier Assignment - // It will trash the callee saved registers - - tree->gtUsedRegs |= RBM_CALLEE_TRASH; -#endif // _TARGET_ARM_ - } -#endif // defined(DEBUG) || !NOGC_WRITE_BARRIERS - } - - if (simpleAssignment) - { - /* - * Consider a simple assignment to a local: - * - * lcl = expr; - * - * Since the "=" node is visited after the variable - * is marked live (assuming it's live after the - * assignment), we don't want to use the register - * use mask of the "=" node but rather that of the - * variable itself. - */ - tree->gtUsedRegs = op1->gtUsedRegs; - } - else - { - tree->gtUsedRegs = op1->gtUsedRegs | op2->gtUsedRegs; - } - VarSetOps::Assign(this, rpUseInPlace, startAsgUseInPlaceVars); - goto RETURN_CHECK; - - case GT_ASG_LSH: - case GT_ASG_RSH: - case GT_ASG_RSZ: - /* assigning shift operators */ - - noway_assert(type != TYP_LONG); - -#if CPU_LOAD_STORE_ARCH - predictReg = PREDICT_ADDR; -#else - predictReg = PREDICT_NONE; -#endif - - /* shift count is handled same as ordinary shift */ - goto HANDLE_SHIFT_COUNT; - - case GT_ADDR: - regMask = rpPredictTreeRegUse(op1, PREDICT_ADDR, lockedRegs, RBM_LASTUSE); - - if ((regMask == RBM_NONE) && (predictReg >= PREDICT_REG)) - { - // We need a scratch register for the LEA instruction - regMask = rpPredictRegPick(TYP_INT, predictReg, lockedRegs | rsvdRegs); - } - - tree->gtUsedRegs = op1->gtUsedRegs | (regMaskSmall)regMask; - goto RETURN_CHECK; - - case GT_CAST: - - /* Cannot cast to VOID */ - noway_assert(type != TYP_VOID); - - /* cast to long is special */ - if (type == TYP_LONG && op1->gtType <= TYP_INT) - { - noway_assert(tree->gtCast.gtCastType == TYP_LONG || tree->gtCast.gtCastType == TYP_ULONG); -#if CPU_LONG_USES_REGPAIR - rpPredictReg predictRegHi = PREDICT_SCRATCH_REG; - - if (rpHasVarIndexForPredict(predictReg)) - { - unsigned tgtIndex = rpGetVarIndexForPredict(predictReg); - rpAsgVarNum = tgtIndex; - - // We don't need any register as we plan on writing to the rpAsgVarNum register - predictReg = PREDICT_NONE; - - LclVarDsc* tgtVar = lvaTable + lvaTrackedToVarNum[tgtIndex]; - tgtVar->lvDependReg = true; - - if (tgtVar->lvOtherReg != REG_STK) - { - predictRegHi = PREDICT_NONE; - } - } - else -#endif - if (predictReg == PREDICT_NONE) - { - predictReg = PREDICT_SCRATCH_REG; - } -#ifdef _TARGET_ARM_ - // If we are widening an int into a long using a targeted register pair we - // should retarget so that the low part get loaded into the appropriate register - else if (predictReg == PREDICT_PAIR_R0R1) - { - predictReg = PREDICT_REG_R0; - predictRegHi = PREDICT_REG_R1; - } - else if (predictReg == PREDICT_PAIR_R2R3) - { - predictReg = PREDICT_REG_R2; - predictRegHi = PREDICT_REG_R3; - } -#endif -#ifdef _TARGET_X86_ - // If we are widening an int into a long using a targeted register pair we - // should retarget so that the low part get loaded into the appropriate register - else if (predictReg == PREDICT_PAIR_EAXEDX) - { - predictReg = PREDICT_REG_EAX; - predictRegHi = PREDICT_REG_EDX; - } - else if (predictReg == PREDICT_PAIR_ECXEBX) - { - predictReg = PREDICT_REG_ECX; - predictRegHi = PREDICT_REG_EBX; - } -#endif - - regMask = rpPredictTreeRegUse(op1, predictReg, lockedRegs, rsvdRegs); - -#if CPU_LONG_USES_REGPAIR - if (predictRegHi != PREDICT_NONE) - { - // Now get one more reg for the upper part - regMask |= rpPredictRegPick(TYP_INT, predictRegHi, lockedRegs | rsvdRegs | regMask); - } -#endif - tree->gtUsedRegs = op1->gtUsedRegs | (regMaskSmall)regMask; - goto RETURN_CHECK; - } - - /* cast from long is special - it frees a register */ - if (type <= TYP_INT // nice. this presumably is intended to mean "signed int and shorter types" - && op1->gtType == TYP_LONG) - { - if ((predictReg == PREDICT_NONE) || rpHasVarIndexForPredict(predictReg)) - predictReg = PREDICT_REG; - - regMask = rpPredictTreeRegUse(op1, predictReg, lockedRegs, rsvdRegs); - - // If we have 2 or more regs, free one of them - if (!genMaxOneBit(regMask)) - { - /* Clear the 2nd lowest bit in regMask */ - /* First set tmpMask to the lowest bit in regMask */ - tmpMask = genFindLowestBit(regMask); - /* Next find the second lowest bit in regMask */ - tmpMask = genFindLowestBit(regMask & ~tmpMask); - /* Clear this bit from regmask */ - regMask &= ~tmpMask; - } - tree->gtUsedRegs = op1->gtUsedRegs; - goto RETURN_CHECK; - } - -#if CPU_HAS_BYTE_REGS - /* cast from signed-byte is special - it uses byteable registers */ - if (type == TYP_INT) - { - var_types smallType; - - if (genTypeSize(tree->gtCast.CastOp()->TypeGet()) < genTypeSize(tree->gtCast.gtCastType)) - smallType = tree->gtCast.CastOp()->TypeGet(); - else - smallType = tree->gtCast.gtCastType; - - if (smallType == TYP_BYTE) - { - regMask = rpPredictTreeRegUse(op1, predictReg, lockedRegs, rsvdRegs); - - if ((regMask & RBM_BYTE_REGS) == 0) - regMask = rpPredictRegPick(type, PREDICT_SCRATCH_REG, RBM_NON_BYTE_REGS); - - tree->gtUsedRegs = (regMaskSmall)regMask; - goto RETURN_CHECK; - } - } -#endif - -#if FEATURE_STACK_FP_X87 - /* cast to float/double is special */ - if (varTypeIsFloating(type)) - { - switch (op1->TypeGet()) - { - /* uses fild, so don't need to be loaded to reg */ - case TYP_INT: - case TYP_LONG: - rpPredictTreeRegUse(op1, PREDICT_NONE, lockedRegs, rsvdRegs); - tree->gtUsedRegs = op1->gtUsedRegs; - regMask = 0; - goto RETURN_CHECK; - default: - break; - } - } - - /* Casting from integral type to floating type is special */ - if (!varTypeIsFloating(type) && varTypeIsFloating(op1->TypeGet())) - { - if (opts.compCanUseSSE2) - { - // predict for SSE2 based casting - if (predictReg <= PREDICT_REG) - predictReg = PREDICT_SCRATCH_REG; - regMask = rpPredictTreeRegUse(op1, predictReg, lockedRegs, rsvdRegs); - - // Get one more int reg to hold cast result - regMask |= rpPredictRegPick(TYP_INT, PREDICT_SCRATCH_REG, lockedRegs | rsvdRegs | regMask); - tree->gtUsedRegs = op1->gtUsedRegs | (regMaskSmall)regMask; - goto RETURN_CHECK; - } - } -#endif - -#if FEATURE_FP_REGALLOC - // Are we casting between int to float or float to int - // Fix 388428 ARM JitStress WP7 - if (varTypeIsFloating(type) != varTypeIsFloating(op1->TypeGet())) - { - // op1 needs to go into a register - regMask = rpPredictTreeRegUse(op1, PREDICT_REG, lockedRegs, rsvdRegs); - -#ifdef _TARGET_ARM_ - if (varTypeIsFloating(op1->TypeGet())) - { - // We also need a fp scratch register for the convert operation - regMask |= rpPredictRegPick((genTypeStSz(type) == 1) ? TYP_FLOAT : TYP_DOUBLE, - PREDICT_SCRATCH_REG, regMask | lockedRegs | rsvdRegs); - } -#endif - // We also need a register to hold the result - regMask |= rpPredictRegPick(type, PREDICT_SCRATCH_REG, regMask | lockedRegs | rsvdRegs); - tree->gtUsedRegs = op1->gtUsedRegs | (regMaskSmall)regMask; - goto RETURN_CHECK; - } -#endif - - /* otherwise must load op1 into a register */ - goto GENERIC_UNARY; - - case GT_INTRINSIC: - -#ifdef _TARGET_XARCH_ - if (tree->gtIntrinsic.gtIntrinsicId == CORINFO_INTRINSIC_Round && tree->TypeGet() == TYP_INT) - { - // This is a special case to handle the following - // optimization: conv.i4(round.d(d)) -> round.i(d) - // if flowgraph 3186 - - if (predictReg <= PREDICT_REG) - predictReg = PREDICT_SCRATCH_REG; - - rpPredictTreeRegUse(op1, predictReg, lockedRegs, rsvdRegs); - - regMask = rpPredictRegPick(TYP_INT, predictReg, lockedRegs | rsvdRegs); - - tree->gtUsedRegs = op1->gtUsedRegs | (regMaskSmall)regMask; - goto RETURN_CHECK; - } -#endif - __fallthrough; - - case GT_NEG: -#ifdef _TARGET_ARM_ - if (tree->TypeGet() == TYP_LONG) - { - // On ARM this consumes an extra register for the '0' value - if (predictReg <= PREDICT_REG) - predictReg = PREDICT_SCRATCH_REG; - - regMaskTP op1Mask = rpPredictTreeRegUse(op1, predictReg, lockedRegs, rsvdRegs); - - regMask = rpPredictRegPick(TYP_INT, predictReg, lockedRegs | op1Mask | rsvdRegs); - - tree->gtUsedRegs = op1->gtUsedRegs | (regMaskSmall)regMask; - goto RETURN_CHECK; - } -#endif // _TARGET_ARM_ - - __fallthrough; - - case GT_NOT: - // these unary operators will write new values - // and thus will need a scratch register - GENERIC_UNARY: - /* generic unary operators */ - - if (predictReg <= PREDICT_REG) - predictReg = PREDICT_SCRATCH_REG; - - __fallthrough; - - case GT_NOP: - // these unary operators do not write new values - // and thus won't need a scratch register - CLANG_FORMAT_COMMENT_ANCHOR; - -#if OPT_BOOL_OPS - if (!op1) - { - tree->gtUsedRegs = 0; - regMask = 0; - goto RETURN_CHECK; - } -#endif - regMask = rpPredictTreeRegUse(op1, predictReg, lockedRegs, rsvdRegs); - tree->gtUsedRegs = op1->gtUsedRegs; - goto RETURN_CHECK; - - case GT_IND: - case GT_NULLCHECK: // At this point, nullcheck is just like an IND... - { - bool intoReg = true; - VARSET_TP startIndUseInPlaceVars(VarSetOps::MakeCopy(this, rpUseInPlace)); - - if (fgIsIndirOfAddrOfLocal(tree) != NULL) - { - compUpdateLifeVar(tree); - } - - if (predictReg == PREDICT_ADDR) - { - intoReg = false; - } - else if (predictReg == PREDICT_NONE) - { - if (type != TYP_LONG) - { - intoReg = false; - } - else - { - predictReg = PREDICT_REG; - } - } - - /* forcing to register? */ - if (intoReg && (type != TYP_LONG)) - { - rsvdRegs |= RBM_LASTUSE; - } - - GenTree* lenCSE; - lenCSE = NULL; - - /* check for address mode */ - regMask = rpPredictAddressMode(op1, type, lockedRegs, rsvdRegs, lenCSE); - tmpMask = RBM_NONE; - -#if CPU_LOAD_STORE_ARCH - // We may need a scratch register for loading a long - if (type == TYP_LONG) - { - /* This scratch register immediately dies */ - tmpMask = rpPredictRegPick(TYP_BYREF, PREDICT_REG, op1->gtUsedRegs | lockedRegs | rsvdRegs); - } -#endif // CPU_LOAD_STORE_ARCH - -#ifdef _TARGET_ARM_ - // Unaligned loads/stores for floating point values must first be loaded into integer register(s) - // - if ((tree->gtFlags & GTF_IND_UNALIGNED) && varTypeIsFloating(type)) - { - /* These integer register(s) immediately die */ - tmpMask = rpPredictRegPick(TYP_INT, PREDICT_REG, op1->gtUsedRegs | lockedRegs | rsvdRegs); - // Two integer registers are required for a TYP_DOUBLE - if (type == TYP_DOUBLE) - tmpMask |= - rpPredictRegPick(TYP_INT, PREDICT_REG, op1->gtUsedRegs | lockedRegs | rsvdRegs | tmpMask); - } -#endif - - /* forcing to register? */ - if (intoReg) - { - regMaskTP lockedMask = lockedRegs | rsvdRegs; - tmpMask |= regMask; - - // We will compute a new regMask that holds the register(s) - // that we will load the indirection into. - // - CLANG_FORMAT_COMMENT_ANCHOR; - -#ifndef _TARGET_64BIT_ - if (type == TYP_LONG) - { - // We need to use multiple load instructions here: - // For the first register we can not choose - // any registers that are being used in place or - // any register in the current regMask - // - regMask = rpPredictRegPick(TYP_INT, predictReg, regMask | lockedMask); - - // For the second register we can choose a register that was - // used in place or any register in the old now overwritten regMask - // but not the same register that we picked above in 'regMask' - // - VarSetOps::Assign(this, rpUseInPlace, startIndUseInPlaceVars); - regMask |= rpPredictRegPick(TYP_INT, predictReg, regMask | lockedMask); - } - else -#endif - { - // We will use one load instruction here: - // The load target register can be a register that was used in place - // or one of the register from the orginal regMask. - // - VarSetOps::Assign(this, rpUseInPlace, startIndUseInPlaceVars); - regMask = rpPredictRegPick(type, predictReg, lockedMask); - } - } - else if (predictReg != PREDICT_ADDR) - { - /* Unless the caller specified PREDICT_ADDR */ - /* we don't return the temp registers used */ - /* to form the address */ - regMask = RBM_NONE; - } - } - - tree->gtUsedRegs = (regMaskSmall)(regMask | tmpMask); - - goto RETURN_CHECK; - - case GT_EQ: - case GT_NE: - case GT_LT: - case GT_LE: - case GT_GE: - case GT_GT: - -#ifdef _TARGET_X86_ - /* Floating point comparison uses EAX for flags */ - if (varTypeIsFloating(op1->TypeGet())) - { - regMask = RBM_EAX; - } - else -#endif - if (!(tree->gtFlags & GTF_RELOP_JMP_USED)) - { - // Some comparisons are converted to ?: - noway_assert(!fgMorphRelopToQmark(op1)); - - if (predictReg <= PREDICT_REG) - predictReg = PREDICT_SCRATCH_REG; - - // The set instructions need a byte register - regMask = rpPredictRegPick(TYP_BYTE, predictReg, lockedRegs | rsvdRegs); - } - else - { - regMask = RBM_NONE; -#ifdef _TARGET_XARCH_ - tmpMask = RBM_NONE; - // Optimize the compare with a constant cases for xarch - if (op1->gtOper == GT_CNS_INT) - { - if (op2->gtOper == GT_CNS_INT) - tmpMask = - rpPredictTreeRegUse(op1, PREDICT_SCRATCH_REG, lockedRegs, rsvdRegs | op2->gtRsvdRegs); - rpPredictTreeRegUse(op2, PREDICT_NONE, lockedRegs | tmpMask, RBM_LASTUSE); - tree->gtUsedRegs = op2->gtUsedRegs; - goto RETURN_CHECK; - } - else if (op2->gtOper == GT_CNS_INT) - { - rpPredictTreeRegUse(op1, PREDICT_NONE, lockedRegs, rsvdRegs); - tree->gtUsedRegs = op1->gtUsedRegs; - goto RETURN_CHECK; - } - else if (op2->gtOper == GT_CNS_LNG) - { - regMaskTP op1Mask = rpPredictTreeRegUse(op1, PREDICT_ADDR, lockedRegs, rsvdRegs); -#ifdef _TARGET_X86_ - // We also need one extra register to read values from - tmpMask = rpPredictRegPick(TYP_INT, PREDICT_SCRATCH_REG, lockedRegs | op1Mask | rsvdRegs); -#endif // _TARGET_X86_ - tree->gtUsedRegs = (regMaskSmall)tmpMask | op1->gtUsedRegs; - goto RETURN_CHECK; - } -#endif // _TARGET_XARCH_ - } - - unsigned op1TypeSize; - unsigned op2TypeSize; - - op1TypeSize = genTypeSize(op1->TypeGet()); - op2TypeSize = genTypeSize(op2->TypeGet()); - - op1PredictReg = PREDICT_REG; - op2PredictReg = PREDICT_REG; - - if (tree->gtFlags & GTF_REVERSE_OPS) - { -#ifdef _TARGET_XARCH_ - if (op1TypeSize == sizeof(int)) - op1PredictReg = PREDICT_NONE; -#endif - - tmpMask = rpPredictTreeRegUse(op2, op2PredictReg, lockedRegs, rsvdRegs | op1->gtRsvdRegs); - rpPredictTreeRegUse(op1, op1PredictReg, lockedRegs | tmpMask, RBM_LASTUSE); - } - else - { -#ifdef _TARGET_XARCH_ - // For full DWORD compares we can have - // - // op1 is an address mode and op2 is a register - // or - // op1 is a register and op2 is an address mode - // - if ((op2TypeSize == sizeof(int)) && (op1TypeSize == op2TypeSize)) - { - if (op2->gtOper == GT_LCL_VAR) - { - unsigned lclNum = op2->gtLclVar.gtLclNum; - varDsc = lvaTable + lclNum; - /* Did we predict that this local will be enregistered? */ - if (varDsc->lvTracked && (varDsc->lvRegNum != REG_STK)) - { - op1PredictReg = PREDICT_ADDR; - } - } - } - // Codegen will generate cmp reg,[mem] for 4 or 8-byte types, but not for 1 or 2 byte types - if ((op1PredictReg != PREDICT_ADDR) && (op2TypeSize >= sizeof(int))) - op2PredictReg = PREDICT_ADDR; -#endif // _TARGET_XARCH_ - - tmpMask = rpPredictTreeRegUse(op1, op1PredictReg, lockedRegs, rsvdRegs | op2->gtRsvdRegs); -#ifdef _TARGET_ARM_ - if ((op2->gtOper != GT_CNS_INT) || !codeGen->validImmForAlu(op2->gtIntCon.gtIconVal)) -#endif - { - rpPredictTreeRegUse(op2, op2PredictReg, lockedRegs | tmpMask, RBM_LASTUSE); - } - } - -#ifdef _TARGET_XARCH_ - // In some cases in genCondSetFlags(), we need to use a temporary register (via rsPickReg()) - // to generate a sign/zero extension before doing a compare. Save a register for this purpose - // if one of the registers is small and the types aren't equal. - - if (regMask == RBM_NONE) - { - rpPredictReg op1xPredictReg, op2xPredictReg; - GenTree* op1x; - GenTree* op2x; - if (tree->gtFlags & GTF_REVERSE_OPS) // TODO: do we really need to handle this case? - { - op1xPredictReg = op2PredictReg; - op2xPredictReg = op1PredictReg; - op1x = op2; - op2x = op1; - } - else - { - op1xPredictReg = op1PredictReg; - op2xPredictReg = op2PredictReg; - op1x = op1; - op2x = op2; - } - if ((op1xPredictReg < PREDICT_REG) && // op1 doesn't get a register (probably an indir) - (op2xPredictReg >= PREDICT_REG) && // op2 gets a register - varTypeIsSmall(op1x->TypeGet())) // op1 is smaller than an int - { - bool needTmp = false; - - // If op1x is a byte, and op2x is not a byteable register, we'll need a temp. - // We could predict a byteable register for op2x, but what if we don't get it? - // So, be conservative and always ask for a temp. There are a couple small CQ losses as a - // result. - if (varTypeIsByte(op1x->TypeGet())) - { - needTmp = true; - } - else - { - if (op2x->gtOper == GT_LCL_VAR) // this will be a GT_REG_VAR during code generation - { - if (genActualType(op1x->TypeGet()) != lvaGetActualType(op2x->gtLclVar.gtLclNum)) - needTmp = true; - } - else - { - if (op1x->TypeGet() != op2x->TypeGet()) - needTmp = true; - } - } - if (needTmp) - { - regMask = rpPredictRegPick(TYP_INT, PREDICT_SCRATCH_REG, lockedRegs | rsvdRegs); - } - } - } -#endif // _TARGET_XARCH_ - - tree->gtUsedRegs = (regMaskSmall)regMask | op1->gtUsedRegs | op2->gtUsedRegs; - goto RETURN_CHECK; - - case GT_MUL: - -#ifndef _TARGET_AMD64_ - if (type == TYP_LONG) - { - assert(tree->gtIsValid64RsltMul()); - - /* Strip out the cast nodes */ - - noway_assert(op1->gtOper == GT_CAST && op2->gtOper == GT_CAST); - op1 = op1->gtCast.CastOp(); - op2 = op2->gtCast.CastOp(); -#else - if (false) - { -#endif // !_TARGET_AMD64_ - USE_MULT_EAX: - -#if defined(_TARGET_X86_) - // This will done by a 64-bit imul "imul eax, reg" - // (i.e. EDX:EAX = EAX * reg) - - /* Are we supposed to evaluate op2 first? */ - if (tree->gtFlags & GTF_REVERSE_OPS) - { - rpPredictTreeRegUse(op2, PREDICT_PAIR_TMP_LO, lockedRegs, rsvdRegs | op1->gtRsvdRegs); - rpPredictTreeRegUse(op1, PREDICT_REG, lockedRegs | RBM_PAIR_TMP_LO, RBM_LASTUSE); - } - else - { - rpPredictTreeRegUse(op1, PREDICT_PAIR_TMP_LO, lockedRegs, rsvdRegs | op2->gtRsvdRegs); - rpPredictTreeRegUse(op2, PREDICT_REG, lockedRegs | RBM_PAIR_TMP_LO, RBM_LASTUSE); - } - - /* set gtUsedRegs to EAX, EDX and the registers needed by op1 and op2 */ - - tree->gtUsedRegs = RBM_PAIR_TMP | op1->gtUsedRegs | op2->gtUsedRegs; - - /* set regMask to the set of held registers */ - - regMask = RBM_PAIR_TMP_LO; - - if (type == TYP_LONG) - regMask |= RBM_PAIR_TMP_HI; - -#elif defined(_TARGET_ARM_) - // This will done by a 4 operand multiply - - // Are we supposed to evaluate op2 first? - if (tree->gtFlags & GTF_REVERSE_OPS) - { - rpPredictTreeRegUse(op2, PREDICT_REG, lockedRegs, rsvdRegs | op1->gtRsvdRegs); - rpPredictTreeRegUse(op1, PREDICT_REG, lockedRegs, RBM_LASTUSE); - } - else - { - rpPredictTreeRegUse(op1, PREDICT_REG, lockedRegs, rsvdRegs | op2->gtRsvdRegs); - rpPredictTreeRegUse(op2, PREDICT_REG, lockedRegs, RBM_LASTUSE); - } - - // set regMask to the set of held registers, - // the two scratch register we need to compute the mul result - - regMask = rpPredictRegPick(TYP_LONG, PREDICT_SCRATCH_REG, lockedRegs | rsvdRegs); - - // set gtUsedRegs toregMask and the registers needed by op1 and op2 - - tree->gtUsedRegs = regMask | op1->gtUsedRegs | op2->gtUsedRegs; - -#else // !_TARGET_X86_ && !_TARGET_ARM_ -#error "Non-ARM or x86 _TARGET_ in RegPredict for 64-bit imul" -#endif - - goto RETURN_CHECK; - } - else - { - /* We use imulEAX for most unsigned multiply operations */ - if (tree->gtOverflow()) - { - if ((tree->gtFlags & GTF_UNSIGNED) || varTypeIsSmall(tree->TypeGet())) - { - goto USE_MULT_EAX; - } - } - } - - __fallthrough; - - case GT_OR: - case GT_XOR: - case GT_AND: - - case GT_SUB: - case GT_ADD: - tree->gtUsedRegs = 0; - - if (predictReg <= PREDICT_REG) - predictReg = PREDICT_SCRATCH_REG; - - GENERIC_BINARY: - - noway_assert(op2); - if (tree->gtFlags & GTF_REVERSE_OPS) - { - op1PredictReg = PREDICT_REG; -#if !CPU_LOAD_STORE_ARCH - if (genTypeSize(op1->gtType) >= sizeof(int)) - op1PredictReg = PREDICT_NONE; -#endif - regMask = rpPredictTreeRegUse(op2, predictReg, lockedRegs, rsvdRegs | op1->gtRsvdRegs); - rpPredictTreeRegUse(op1, op1PredictReg, lockedRegs | regMask, RBM_LASTUSE); - } - else - { - op2PredictReg = PREDICT_REG; -#if !CPU_LOAD_STORE_ARCH - if (genTypeSize(op2->gtType) >= sizeof(int)) - op2PredictReg = PREDICT_NONE; -#endif - regMask = rpPredictTreeRegUse(op1, predictReg, lockedRegs, rsvdRegs | op2->gtRsvdRegs); -#ifdef _TARGET_ARM_ - // For most ALU operations we can generate a single instruction that encodes - // a small immediate integer constant value. (except for multiply) - // - if ((op2->gtOper == GT_CNS_INT) && (oper != GT_MUL)) - { - ssize_t ival = op2->gtIntCon.gtIconVal; - if (codeGen->validImmForAlu(ival)) - { - op2PredictReg = PREDICT_NONE; - } - else if (codeGen->validImmForAdd(ival, INS_FLAGS_DONT_CARE) && - ((oper == GT_ADD) || (oper == GT_SUB))) - { - op2PredictReg = PREDICT_NONE; - } - } - if (op2PredictReg == PREDICT_NONE) - { - op2->gtUsedRegs = RBM_NONE; - } - else -#endif - { - rpPredictTreeRegUse(op2, op2PredictReg, lockedRegs | regMask, RBM_LASTUSE); - } - } - tree->gtUsedRegs = (regMaskSmall)regMask | op1->gtUsedRegs | op2->gtUsedRegs; - -#if CPU_HAS_BYTE_REGS - /* We have special register requirements for byte operations */ - - if (varTypeIsByte(tree->TypeGet())) - { - /* For 8 bit arithmetic, one operands has to be in a - byte-addressable register, and the other has to be - in a byte-addrble reg or in memory. Assume its in a reg */ - - regMaskTP regByteMask = 0; - regMaskTP op1ByteMask = op1->gtUsedRegs; - - if (!(op1->gtUsedRegs & RBM_BYTE_REGS)) - { - // Pick a Byte register to use for op1 - regByteMask = rpPredictRegPick(TYP_BYTE, PREDICT_REG, lockedRegs | rsvdRegs); - op1ByteMask = regByteMask; - } - - if (!(op2->gtUsedRegs & RBM_BYTE_REGS)) - { - // Pick a Byte register to use for op2, avoiding the one used by op1 - regByteMask |= rpPredictRegPick(TYP_BYTE, PREDICT_REG, lockedRegs | rsvdRegs | op1ByteMask); - } - - if (regByteMask) - { - tree->gtUsedRegs |= regByteMask; - regMask = regByteMask; - } - } -#endif - goto RETURN_CHECK; - - case GT_DIV: - case GT_MOD: - - case GT_UDIV: - case GT_UMOD: - - /* non-integer division handled in generic way */ - if (!varTypeIsIntegral(type)) - { - tree->gtUsedRegs = 0; - if (predictReg <= PREDICT_REG) - predictReg = PREDICT_SCRATCH_REG; - goto GENERIC_BINARY; - } - -#ifndef _TARGET_64BIT_ - - if (type == TYP_LONG && (oper == GT_MOD || oper == GT_UMOD)) - { - /* Special case: a mod with an int op2 is done inline using idiv or div - to avoid a costly call to the helper */ - - noway_assert((op2->gtOper == GT_CNS_LNG) && - (op2->gtLngCon.gtLconVal == int(op2->gtLngCon.gtLconVal))); - -#if defined(_TARGET_X86_) || defined(_TARGET_ARM_) - if (tree->gtFlags & GTF_REVERSE_OPS) - { - tmpMask = rpPredictTreeRegUse(op2, PREDICT_REG, lockedRegs | RBM_PAIR_TMP, - rsvdRegs | op1->gtRsvdRegs); - tmpMask |= rpPredictTreeRegUse(op1, PREDICT_PAIR_TMP, lockedRegs | tmpMask, RBM_LASTUSE); - } - else - { - tmpMask = rpPredictTreeRegUse(op1, PREDICT_PAIR_TMP, lockedRegs, rsvdRegs | op2->gtRsvdRegs); - tmpMask |= - rpPredictTreeRegUse(op2, PREDICT_REG, lockedRegs | tmpMask | RBM_PAIR_TMP, RBM_LASTUSE); - } - regMask = RBM_PAIR_TMP; -#else // !_TARGET_X86_ && !_TARGET_ARM_ -#error "Non-ARM or x86 _TARGET_ in RegPredict for 64-bit MOD" -#endif // !_TARGET_X86_ && !_TARGET_ARM_ - - tree->gtUsedRegs = - (regMaskSmall)(regMask | op1->gtUsedRegs | op2->gtUsedRegs | - rpPredictRegPick(TYP_INT, PREDICT_SCRATCH_REG, regMask | tmpMask)); - - goto RETURN_CHECK; - } -#endif // _TARGET_64BIT_ - - /* no divide immediate, so force integer constant which is not - * a power of two to register - */ - - if (op2->OperKind() & GTK_CONST) - { - ssize_t ival = op2->gtIntConCommon.IconValue(); - - /* Is the divisor a power of 2 ? */ - - if (ival > 0 && genMaxOneBit(size_t(ival))) - { - goto GENERIC_UNARY; - } - else - op2PredictReg = PREDICT_SCRATCH_REG; - } - else - { - /* Non integer constant also must be enregistered */ - op2PredictReg = PREDICT_REG; - } - - regMaskTP trashedMask; - trashedMask = DUMMY_INIT(RBM_ILLEGAL); - regMaskTP op1ExcludeMask; - op1ExcludeMask = DUMMY_INIT(RBM_ILLEGAL); - regMaskTP op2ExcludeMask; - op2ExcludeMask = DUMMY_INIT(RBM_ILLEGAL); - -#ifdef _TARGET_XARCH_ - /* Consider the case "a / b" - we'll need to trash EDX (via "CDQ") before - * we can safely allow the "b" value to die. Unfortunately, if we simply - * mark the node "b" as using EDX, this will not work if "b" is a register - * variable that dies with this particular reference. Thus, if we want to - * avoid this situation (where we would have to spill the variable from - * EDX to someplace else), we need to explicitly mark the interference - * of the variable at this point. - */ - - if (op2->gtOper == GT_LCL_VAR) - { - unsigned lclNum = op2->gtLclVarCommon.gtLclNum; - varDsc = lvaTable + lclNum; - if (varDsc->lvTracked) - { -#ifdef DEBUG - if (verbose) - { - if (!VarSetOps::IsMember(this, raLclRegIntf[REG_EAX], varDsc->lvVarIndex)) - printf("Record interference between V%02u,T%02u and EAX -- int divide\n", lclNum, - varDsc->lvVarIndex); - if (!VarSetOps::IsMember(this, raLclRegIntf[REG_EDX], varDsc->lvVarIndex)) - printf("Record interference between V%02u,T%02u and EDX -- int divide\n", lclNum, - varDsc->lvVarIndex); - } -#endif - VarSetOps::AddElemD(this, raLclRegIntf[REG_EAX], varDsc->lvVarIndex); - VarSetOps::AddElemD(this, raLclRegIntf[REG_EDX], varDsc->lvVarIndex); - } - } - - /* set the held register based on opcode */ - if (oper == GT_DIV || oper == GT_UDIV) - regMask = RBM_EAX; - else - regMask = RBM_EDX; - trashedMask = (RBM_EAX | RBM_EDX); - op1ExcludeMask = 0; - op2ExcludeMask = (RBM_EAX | RBM_EDX); - -#endif // _TARGET_XARCH_ - -#ifdef _TARGET_ARM_ - trashedMask = RBM_NONE; - op1ExcludeMask = RBM_NONE; - op2ExcludeMask = RBM_NONE; -#endif - - /* set the lvPref reg if possible */ - GenTree* dest; - /* - * Walking the gtNext link twice from here should get us back - * to our parent node, if this is an simple assignment tree. - */ - dest = tree->gtNext; - if (dest && (dest->gtOper == GT_LCL_VAR) && dest->gtNext && (dest->gtNext->OperKind() & GTK_ASGOP) && - dest->gtNext->gtOp.gtOp2 == tree) - { - varDsc = lvaTable + dest->gtLclVarCommon.gtLclNum; - varDsc->addPrefReg(regMask, this); - } -#ifdef _TARGET_XARCH_ - op1PredictReg = PREDICT_REG_EDX; /* Normally target op1 into EDX */ -#else - op1PredictReg = PREDICT_SCRATCH_REG; -#endif - - /* are we supposed to evaluate op2 first? */ - if (tree->gtFlags & GTF_REVERSE_OPS) - { - tmpMask = rpPredictTreeRegUse(op2, op2PredictReg, lockedRegs | op2ExcludeMask, - rsvdRegs | op1->gtRsvdRegs); - rpPredictTreeRegUse(op1, op1PredictReg, lockedRegs | tmpMask | op1ExcludeMask, RBM_LASTUSE); - } - else - { - tmpMask = rpPredictTreeRegUse(op1, op1PredictReg, lockedRegs | op1ExcludeMask, - rsvdRegs | op2->gtRsvdRegs); - rpPredictTreeRegUse(op2, op2PredictReg, tmpMask | lockedRegs | op2ExcludeMask, RBM_LASTUSE); - } -#ifdef _TARGET_ARM_ - regMask = tmpMask; -#endif - /* grab EAX, EDX for this tree node */ - tree->gtUsedRegs = (regMaskSmall)trashedMask | op1->gtUsedRegs | op2->gtUsedRegs; - - goto RETURN_CHECK; - - case GT_LSH: - case GT_RSH: - case GT_RSZ: - - if (predictReg <= PREDICT_REG) - predictReg = PREDICT_SCRATCH_REG; - -#ifndef _TARGET_64BIT_ - if (type == TYP_LONG) - { - if (op2->IsCnsIntOrI()) - { - regMask = rpPredictTreeRegUse(op1, predictReg, lockedRegs, rsvdRegs); - // no register used by op2 - op2->gtUsedRegs = 0; - tree->gtUsedRegs = op1->gtUsedRegs; - } - else - { - // since RBM_LNGARG_0 and RBM_SHIFT_LNG are hardwired we can't have them in the locked registers - tmpMask = lockedRegs; - tmpMask &= ~RBM_LNGARG_0; - tmpMask &= ~RBM_SHIFT_LNG; - - // op2 goes to RBM_SHIFT, op1 to the RBM_LNGARG_0 pair - if (tree->gtFlags & GTF_REVERSE_OPS) - { - rpPredictTreeRegUse(op2, PREDICT_REG_SHIFT_LNG, tmpMask, RBM_NONE); - tmpMask |= RBM_SHIFT_LNG; - // Ensure that the RBM_SHIFT_LNG register interfere with op2's compCurLife - // Fix 383843 X86/ARM ILGEN - rpRecordRegIntf(RBM_SHIFT_LNG, compCurLife DEBUGARG("SHIFT_LNG arg setup")); - rpPredictTreeRegUse(op1, PREDICT_PAIR_LNGARG_0, tmpMask, RBM_LASTUSE); - } - else - { - rpPredictTreeRegUse(op1, PREDICT_PAIR_LNGARG_0, tmpMask, RBM_NONE); - tmpMask |= RBM_LNGARG_0; - // Ensure that the RBM_LNGARG_0 registers interfere with op1's compCurLife - // Fix 383839 ARM ILGEN - rpRecordRegIntf(RBM_LNGARG_0, compCurLife DEBUGARG("LNGARG_0 arg setup")); - rpPredictTreeRegUse(op2, PREDICT_REG_SHIFT_LNG, tmpMask, RBM_LASTUSE); - } - regMask = RBM_LNGRET; // function return registers - op1->gtUsedRegs |= RBM_LNGARG_0; - op2->gtUsedRegs |= RBM_SHIFT_LNG; - - tree->gtUsedRegs = op1->gtUsedRegs | op2->gtUsedRegs; - - // We are using a helper function to do shift: - // - tree->gtUsedRegs |= RBM_CALLEE_TRASH; - } - } - else -#endif // _TARGET_64BIT_ - { -#ifdef _TARGET_XARCH_ - if (!op2->IsCnsIntOrI()) - predictReg = PREDICT_NOT_REG_ECX; -#endif - - HANDLE_SHIFT_COUNT: - // Note that this code is also used by assigning shift operators (i.e. GT_ASG_LSH) - - regMaskTP tmpRsvdRegs; - - if ((tree->gtFlags & GTF_REVERSE_OPS) == 0) - { - regMask = rpPredictTreeRegUse(op1, predictReg, lockedRegs, rsvdRegs | op2->gtRsvdRegs); - rsvdRegs = RBM_LASTUSE; - tmpRsvdRegs = RBM_NONE; - } - else - { - regMask = RBM_NONE; - // Special case op1 of a constant - if (op1->IsCnsIntOrI()) - tmpRsvdRegs = RBM_LASTUSE; // Allow a last use to occur in op2; See - // System.Xml.Schema.BitSet:Get(int):bool - else - tmpRsvdRegs = op1->gtRsvdRegs; - } - - op2Mask = RBM_NONE; - if (!op2->IsCnsIntOrI()) - { - if ((REG_SHIFT != REG_NA) && ((RBM_SHIFT & tmpRsvdRegs) == 0)) - { - op2PredictReg = PREDICT_REG_SHIFT; - } - else - { - op2PredictReg = PREDICT_REG; - } - - /* evaluate shift count into a register, likely the PREDICT_REG_SHIFT register */ - op2Mask = rpPredictTreeRegUse(op2, op2PredictReg, lockedRegs | regMask, tmpRsvdRegs); - - // If our target arch has a REG_SHIFT register then - // we set the PrefReg when we have a LclVar for op2 - // we add an interference with REG_SHIFT for any other LclVars alive at op2 - if (REG_SHIFT != REG_NA) - { - VARSET_TP liveSet(VarSetOps::MakeCopy(this, compCurLife)); - - while (op2->gtOper == GT_COMMA) - { - op2 = op2->gtOp.gtOp2; - } - - if (op2->gtOper == GT_LCL_VAR) - { - varDsc = lvaTable + op2->gtLclVarCommon.gtLclNum; - varDsc->setPrefReg(REG_SHIFT, this); - if (varDsc->lvTracked) - { - VarSetOps::RemoveElemD(this, liveSet, varDsc->lvVarIndex); - } - } - - // Ensure that we have a register interference with the LclVar in tree's LiveSet, - // excluding the LclVar that was used for the shift amount as it is read-only - // and can be kept alive through the shift operation - // - rpRecordRegIntf(RBM_SHIFT, liveSet DEBUGARG("Variable Shift Register")); - // In case op2Mask doesn't contain the required shift register, - // we will or it in now. - op2Mask |= RBM_SHIFT; - } - } - - if (tree->gtFlags & GTF_REVERSE_OPS) - { - assert(regMask == RBM_NONE); - regMask = rpPredictTreeRegUse(op1, predictReg, lockedRegs | op2Mask, rsvdRegs | RBM_LASTUSE); - } - -#if CPU_HAS_BYTE_REGS - if (varTypeIsByte(type)) - { - // Fix 383789 X86 ILGEN - // Fix 383813 X86 ILGEN - // Fix 383828 X86 ILGEN - if (op1->gtOper == GT_LCL_VAR) - { - varDsc = lvaTable + op1->gtLclVar.gtLclNum; - if (varDsc->lvTracked) - { - VARSET_TP op1VarBit(VarSetOps::MakeSingleton(this, varDsc->lvVarIndex)); - - // Ensure that we don't assign a Non-Byteable register for op1's LCL_VAR - rpRecordRegIntf(RBM_NON_BYTE_REGS, op1VarBit DEBUGARG("Non Byte Register")); - } - } - if ((regMask & RBM_BYTE_REGS) == 0) - { - // We need to grab a byte-able register, (i.e. EAX, EDX, ECX, EBX) - // and we can't select one that is already reserved (i.e. lockedRegs or regMask) - // - regMask |= - rpPredictRegPick(type, PREDICT_SCRATCH_REG, (lockedRegs | regMask | RBM_NON_BYTE_REGS)); - } - } -#endif - tree->gtUsedRegs = (regMaskSmall)(regMask | op2Mask); - } - - goto RETURN_CHECK; - - case GT_COMMA: - if (tree->gtFlags & GTF_REVERSE_OPS) - { - if (predictReg == PREDICT_NONE) - { - predictReg = PREDICT_REG; - } - else if (rpHasVarIndexForPredict(predictReg)) - { - /* Don't propagate the use of tgt reg use in a GT_COMMA */ - predictReg = PREDICT_SCRATCH_REG; - } - - regMask = rpPredictTreeRegUse(op2, predictReg, lockedRegs, rsvdRegs); - rpPredictTreeRegUse(op1, PREDICT_NONE, lockedRegs | regMask, RBM_LASTUSE); - } - else - { - rpPredictTreeRegUse(op1, PREDICT_NONE, lockedRegs, RBM_LASTUSE); - - /* CodeGen will enregister the op2 side of a GT_COMMA */ - if (predictReg == PREDICT_NONE) - { - predictReg = PREDICT_REG; - } - else if (rpHasVarIndexForPredict(predictReg)) - { - /* Don't propagate the use of tgt reg use in a GT_COMMA */ - predictReg = PREDICT_SCRATCH_REG; - } - - regMask = rpPredictTreeRegUse(op2, predictReg, lockedRegs, rsvdRegs); - } - // tree should only accumulate the used registers from the op2 side of the GT_COMMA - // - tree->gtUsedRegs = op2->gtUsedRegs; - if ((op2->gtOper == GT_LCL_VAR) && (rsvdRegs != 0)) - { - LclVarDsc* op2VarDsc = lvaTable + op2->gtLclVarCommon.gtLclNum; - - if (op2VarDsc->lvTracked) - { - VARSET_TP op2VarBit(VarSetOps::MakeSingleton(this, op2VarDsc->lvVarIndex)); - rpRecordRegIntf(rsvdRegs, op2VarBit DEBUGARG("comma use")); - } - } - goto RETURN_CHECK; - - case GT_QMARK: - { - noway_assert(op1 != NULL && op2 != NULL); - - /* - * If the gtUsedRegs conflicts with lockedRegs - * then we going to have to spill some registers - * into the non-trashed register set to keep it alive - */ - unsigned spillCnt; - spillCnt = 0; - regMaskTP spillRegs; - spillRegs = lockedRegs & tree->gtUsedRegs; - - while (spillRegs) - { - /* Find the next register that needs to be spilled */ - tmpMask = genFindLowestBit(spillRegs); - -#ifdef DEBUG - if (verbose) - { - printf("Predict spill of %s before: ", getRegName(genRegNumFromMask(tmpMask))); - gtDispTree(tree, 0, NULL, true); - } -#endif - /* In Codegen it will typically introduce a spill temp here */ - /* rather than relocating the register to a non trashed reg */ - rpPredictSpillCnt++; - spillCnt++; - - /* Remove it from the spillRegs and lockedRegs*/ - spillRegs &= ~tmpMask; - lockedRegs &= ~tmpMask; - } - { - VARSET_TP startQmarkCondUseInPlaceVars(VarSetOps::MakeCopy(this, rpUseInPlace)); - - /* Evaluate the subtree */ - rpPredictTreeRegUse(op1, PREDICT_NONE, lockedRegs, RBM_LASTUSE); - VarSetOps::Assign(this, rpUseInPlace, startQmarkCondUseInPlaceVars); - tree->gtUsedRegs = op1->gtUsedRegs; - - noway_assert(op2->gtOper == GT_COLON); - if (rpHasVarIndexForPredict(predictReg) && ((op2->gtFlags & (GTF_ASG | GTF_CALL)) != 0)) - { - // Don't try to target the register specified in predictReg when we have complex subtrees - // - predictReg = PREDICT_SCRATCH_REG; - } - GenTree* elseTree = op2->AsColon()->ElseNode(); - GenTree* thenTree = op2->AsColon()->ThenNode(); - - noway_assert(thenTree != NULL && elseTree != NULL); - - // Update compCurLife to only those vars live on the subtree - - VarSetOps::Assign(this, compCurLife, tree->gtQmark.gtThenLiveSet); - - if (type == TYP_VOID) - { - /* Evaluate the subtree */ - rpPredictTreeRegUse(thenTree, PREDICT_NONE, lockedRegs, RBM_LASTUSE); - regMask = RBM_NONE; - predictReg = PREDICT_NONE; - } - else - { - // A mask to use to force the predictor to choose low registers (to reduce code size) - regMaskTP avoidRegs = RBM_NONE; -#ifdef _TARGET_ARM_ - avoidRegs = (RBM_R12 | RBM_LR); -#endif - if (predictReg <= PREDICT_REG) - predictReg = PREDICT_SCRATCH_REG; - - /* Evaluate the subtree */ - regMask = - rpPredictTreeRegUse(thenTree, predictReg, lockedRegs, rsvdRegs | avoidRegs | RBM_LASTUSE); - - if (regMask) - { - rpPredictReg op1PredictReg = rpGetPredictForMask(regMask); - if (op1PredictReg != PREDICT_NONE) - predictReg = op1PredictReg; - } - } - - VarSetOps::Assign(this, rpUseInPlace, startQmarkCondUseInPlaceVars); - - /* Evaluate the subtree */ - // First record the post-then liveness, and reset the current liveness to the else - // branch liveness. - CLANG_FORMAT_COMMENT_ANCHOR; - -#ifdef DEBUG - VARSET_TP postThenLive(VarSetOps::MakeCopy(this, compCurLife)); -#endif - - VarSetOps::Assign(this, compCurLife, tree->gtQmark.gtElseLiveSet); - - rpPredictTreeRegUse(elseTree, predictReg, lockedRegs, rsvdRegs | RBM_LASTUSE); - tree->gtUsedRegs |= thenTree->gtUsedRegs | elseTree->gtUsedRegs; - - // The then and the else are "virtual basic blocks" that form a control-flow diamond. - // They each have only one successor, which they share. Their live-out sets must equal the - // live-in set of this virtual successor block, and thus must be the same. We can assert - // that equality here. - assert(VarSetOps::Equal(this, compCurLife, postThenLive)); - - if (spillCnt > 0) - { - regMaskTP reloadMask = RBM_NONE; - - while (spillCnt) - { - regMaskTP reloadReg; - - /* Get an extra register to hold it */ - reloadReg = rpPredictRegPick(TYP_INT, PREDICT_REG, lockedRegs | regMask | reloadMask); -#ifdef DEBUG - if (verbose) - { - printf("Predict reload into %s after : ", getRegName(genRegNumFromMask(reloadReg))); - gtDispTree(tree, 0, NULL, true); - } -#endif - reloadMask |= reloadReg; - - spillCnt--; - } - - /* update the gtUsedRegs mask */ - tree->gtUsedRegs |= reloadMask; - } - } - - goto RETURN_CHECK; - } - case GT_RETURN: - tree->gtUsedRegs = RBM_NONE; - regMask = RBM_NONE; - - /* Is there a return value? */ - if (op1 != NULL) - { -#if FEATURE_FP_REGALLOC - if (varTypeIsFloating(type)) - { - predictReg = PREDICT_FLTRET; - if (type == TYP_FLOAT) - regMask = RBM_FLOATRET; - else - regMask = RBM_DOUBLERET; - } - else -#endif - if (isRegPairType(type)) - { - predictReg = PREDICT_LNGRET; - regMask = RBM_LNGRET; - } - else - { - predictReg = PREDICT_INTRET; - regMask = RBM_INTRET; - } - if (info.compCallUnmanaged) - { - lockedRegs |= (RBM_PINVOKE_TCB | RBM_PINVOKE_FRAME); - } - rpPredictTreeRegUse(op1, predictReg, lockedRegs, RBM_LASTUSE); - tree->gtUsedRegs = op1->gtUsedRegs | (regMaskSmall)regMask; - } - -#if defined(_TARGET_ARM_) && defined(PROFILING_SUPPORTED) - // When on Arm under profiler, to emit Leave callback we would need RBM_PROFILER_RETURN_USED. - // We could optimize on registers based on int/long or no return value. But to - // keep it simple we will mark entire RBM_PROFILER_RETURN_USED as used regs here. - if (compIsProfilerHookNeeded()) - { - tree->gtUsedRegs |= RBM_PROFILER_RET_USED; - } - -#endif - goto RETURN_CHECK; - - case GT_RETFILT: - if (op1 != NULL) - { - rpPredictTreeRegUse(op1, PREDICT_NONE, lockedRegs, RBM_LASTUSE); - regMask = genReturnRegForTree(tree); - tree->gtUsedRegs = op1->gtUsedRegs | (regMaskSmall)regMask; - goto RETURN_CHECK; - } - tree->gtUsedRegs = 0; - regMask = 0; - - goto RETURN_CHECK; - - case GT_JTRUE: - /* This must be a test of a relational operator */ - - noway_assert(op1->OperIsCompare()); - - /* Only condition code set by this operation */ - - rpPredictTreeRegUse(op1, PREDICT_NONE, lockedRegs, RBM_NONE); - - tree->gtUsedRegs = op1->gtUsedRegs; - regMask = 0; - - goto RETURN_CHECK; - - case GT_SWITCH: - noway_assert(type <= TYP_INT); - noway_assert(compCurBB->bbJumpKind == BBJ_SWITCH); -#ifdef _TARGET_ARM_ - { - regMask = rpPredictTreeRegUse(op1, PREDICT_REG, lockedRegs, RBM_NONE); - unsigned jumpCnt = compCurBB->bbJumpSwt->bbsCount; - if (jumpCnt > 2) - { - // Table based switch requires an extra register for the table base - regMask |= rpPredictRegPick(TYP_INT, PREDICT_SCRATCH_REG, lockedRegs | regMask); - } - tree->gtUsedRegs = op1->gtUsedRegs | regMask; - } -#else // !_TARGET_ARM_ - rpPredictTreeRegUse(op1, PREDICT_REG, lockedRegs, RBM_NONE); - tree->gtUsedRegs = op1->gtUsedRegs; -#endif // _TARGET_ARM_ - regMask = 0; - goto RETURN_CHECK; - - case GT_CKFINITE: - if (predictReg <= PREDICT_REG) - predictReg = PREDICT_SCRATCH_REG; - - rpPredictTreeRegUse(op1, predictReg, lockedRegs, rsvdRegs); - // Need a reg to load exponent into - regMask = rpPredictRegPick(TYP_INT, PREDICT_SCRATCH_REG, lockedRegs | rsvdRegs); - tree->gtUsedRegs = (regMaskSmall)regMask | op1->gtUsedRegs; - goto RETURN_CHECK; - - case GT_LCLHEAP: - regMask = rpPredictTreeRegUse(op1, PREDICT_SCRATCH_REG, lockedRegs, rsvdRegs); - op2Mask = 0; - -#ifdef _TARGET_ARM_ - if (info.compInitMem) - { - // We zero out two registers in the ARM codegen path - op2Mask |= - rpPredictRegPick(TYP_INT, PREDICT_SCRATCH_REG, lockedRegs | rsvdRegs | regMask | op2Mask); - } -#endif - - op1->gtUsedRegs |= (regMaskSmall)regMask; - tree->gtUsedRegs = op1->gtUsedRegs | (regMaskSmall)op2Mask; - - // The result will be put in the reg we picked for the size - // regMask = - - goto RETURN_CHECK; - - case GT_OBJ: - { -#ifdef _TARGET_ARM_ - if (predictReg <= PREDICT_REG) - predictReg = PREDICT_SCRATCH_REG; - - regMaskTP avoidRegs = (RBM_R12 | RBM_LR); // A mask to use to force the predictor to choose low - // registers (to reduce code size) - regMask = RBM_NONE; - tmpMask = rpPredictTreeRegUse(op1, predictReg, lockedRegs, rsvdRegs | avoidRegs); -#endif - - if (fgIsIndirOfAddrOfLocal(tree) != NULL) - { - compUpdateLifeVar(tree); - } - -#ifdef _TARGET_ARM_ - unsigned objSize = info.compCompHnd->getClassSize(tree->gtObj.gtClass); - regMaskTP preferReg = rpPredictRegMask(predictReg, TYP_I_IMPL); - // If it has one bit set, and that's an arg reg... - if (preferReg != RBM_NONE && genMaxOneBit(preferReg) && ((preferReg & RBM_ARG_REGS) != 0)) - { - // We are passing the 'obj' in the argument registers - // - regNumber rn = genRegNumFromMask(preferReg); - - // Add the registers used to pass the 'obj' to regMask. - for (unsigned i = 0; i < objSize / 4; i++) - { - if (rn == MAX_REG_ARG) - break; - // Otherwise... - regMask |= genRegMask(rn); - rn = genRegArgNext(rn); - } - } - else - { - // We are passing the 'obj' in the outgoing arg space - // We will need one register to load into unless the 'obj' size is 4 or less. - // - if (objSize > 4) - { - regMask = rpPredictRegPick(TYP_INT, PREDICT_SCRATCH_REG, lockedRegs | tmpMask | avoidRegs); - } - } - tree->gtUsedRegs = (regMaskSmall)(regMask | tmpMask); - goto RETURN_CHECK; -#else // !_TARGET_ARM_ - goto GENERIC_UNARY; -#endif // _TARGET_ARM_ - } - - case GT_MKREFANY: - { -#ifdef _TARGET_ARM_ - regMaskTP preferReg = rpPredictRegMask(predictReg, TYP_I_IMPL); - regMask = RBM_NONE; - if ((((preferReg - 1) & preferReg) == 0) && ((preferReg & RBM_ARG_REGS) != 0)) - { - // A MKREFANY takes up two registers. - regNumber rn = genRegNumFromMask(preferReg); - regMask = RBM_NONE; - if (rn < MAX_REG_ARG) - { - regMask |= genRegMask(rn); - rn = genRegArgNext(rn); - if (rn < MAX_REG_ARG) - regMask |= genRegMask(rn); - } - } - if (regMask != RBM_NONE) - { - // Condensation of GENERIC_BINARY path. - assert((tree->gtFlags & GTF_REVERSE_OPS) == 0); - op2PredictReg = PREDICT_REG; - regMaskTP regMaskOp1 = rpPredictTreeRegUse(op1, predictReg, lockedRegs, rsvdRegs | op2->gtRsvdRegs); - rpPredictTreeRegUse(op2, op2PredictReg, lockedRegs | regMaskOp1, RBM_LASTUSE); - regMask |= op1->gtUsedRegs | op2->gtUsedRegs; - tree->gtUsedRegs = (regMaskSmall)regMask; - goto RETURN_CHECK; - } - tree->gtUsedRegs = op1->gtUsedRegs; -#endif // _TARGET_ARM_ - goto GENERIC_BINARY; - } - - case GT_BOX: - goto GENERIC_UNARY; - - case GT_LOCKADD: - goto GENERIC_BINARY; - - case GT_XADD: - case GT_XCHG: - // Ensure we can write to op2. op2 will hold the output. - if (predictReg < PREDICT_SCRATCH_REG) - predictReg = PREDICT_SCRATCH_REG; - - if (tree->gtFlags & GTF_REVERSE_OPS) - { - op2Mask = rpPredictTreeRegUse(op2, predictReg, lockedRegs, rsvdRegs); - regMask = rpPredictTreeRegUse(op1, PREDICT_REG, lockedRegs, rsvdRegs | op2Mask); - } - else - { - regMask = rpPredictTreeRegUse(op1, PREDICT_REG, lockedRegs, rsvdRegs); - op2Mask = rpPredictTreeRegUse(op2, PREDICT_SCRATCH_REG, lockedRegs, rsvdRegs | regMask); - } - tree->gtUsedRegs = (regMaskSmall)(regMask | op2Mask); - goto RETURN_CHECK; - - case GT_ARR_LENGTH: - goto GENERIC_UNARY; - - case GT_INIT_VAL: - // This unary operator simply passes through the value from its child (much like GT_NOP) - // and thus won't need a scratch register. - regMask = rpPredictTreeRegUse(op1, predictReg, lockedRegs, rsvdRegs); - tree->gtUsedRegs = op1->gtUsedRegs; - goto RETURN_CHECK; - - default: -#ifdef DEBUG - gtDispTree(tree); -#endif - noway_assert(!"unexpected simple operator in reg use prediction"); - break; - } - } - - /* See what kind of a special operator we have here */ - - switch (oper) - { - GenTree* args; - GenTreeArgList* list; - regMaskTP keepMask; - unsigned regArgsNum; - int regIndex; - regMaskTP regArgMask; - regMaskTP curArgMask; - - case GT_CALL: - - { - - /* initialize so we can just or in various bits */ - tree->gtUsedRegs = RBM_NONE; - -#if GTF_CALL_REG_SAVE - /* - * Unless the GTF_CALL_REG_SAVE flag is set, - * we can't preserve the RBM_CALLEE_TRASH registers. - * (likewise we can't preserve the return registers) - * So we remove them from the lockedRegs set and - * record any of them in the keepMask - */ - - if (tree->gtFlags & GTF_CALL_REG_SAVE) - { - regMaskTP trashMask = genReturnRegForTree(tree); - - keepMask = lockedRegs & trashMask; - lockedRegs &= ~trashMask; - } - else -#endif - { - keepMask = lockedRegs & RBM_CALLEE_TRASH; - lockedRegs &= ~RBM_CALLEE_TRASH; - } - - regArgsNum = 0; - regIndex = 0; - - /* Is there an object pointer? */ - if (tree->gtCall.gtCallObjp) - { - /* Evaluate the instance pointer first */ - - args = tree->gtCall.gtCallObjp; - - /* the objPtr always goes to an integer register (through temp or directly) */ - noway_assert(regArgsNum == 0); - regArgsNum++; - - /* Must be passed in a register */ - - noway_assert(args->gtFlags & GTF_LATE_ARG); - - /* Must be either a deferred reg arg node or a GT_ASG node */ - - noway_assert(args->IsArgPlaceHolderNode() || args->IsNothingNode() || (args->gtOper == GT_ASG) || - args->OperIsCopyBlkOp() || (args->gtOper == GT_COMMA)); - - if (!args->IsArgPlaceHolderNode()) - { - rpPredictTreeRegUse(args, PREDICT_NONE, lockedRegs, RBM_LASTUSE); - } - } - VARSET_TP startArgUseInPlaceVars(VarSetOps::UninitVal()); - VarSetOps::Assign(this, startArgUseInPlaceVars, rpUseInPlace); - - /* process argument list */ - for (list = tree->gtCall.gtCallArgs; list; list = list->Rest()) - { - args = list->Current(); - - if (args->gtFlags & GTF_LATE_ARG) - { - /* Must be either a Placeholder/NOP node or a GT_ASG node */ - - noway_assert(args->IsArgPlaceHolderNode() || args->IsNothingNode() || (args->gtOper == GT_ASG) || - args->OperIsCopyBlkOp() || (args->gtOper == GT_COMMA)); - - if (!args->IsArgPlaceHolderNode()) - { - rpPredictTreeRegUse(args, PREDICT_NONE, lockedRegs, RBM_LASTUSE); - } - - regArgsNum++; - } - else - { -#ifdef FEATURE_FIXED_OUT_ARGS - // We'll store this argument into the outgoing argument area - // It needs to be in a register to be stored. - // - predictReg = PREDICT_REG; - -#else // !FEATURE_FIXED_OUT_ARGS - // We'll generate a push for this argument - // - predictReg = PREDICT_NONE; - if (varTypeIsSmall(args->TypeGet())) - { - /* We may need to sign or zero extend a small type using a register */ - predictReg = PREDICT_SCRATCH_REG; - } -#endif - - rpPredictTreeRegUse(args, predictReg, lockedRegs, RBM_LASTUSE); - } - VarSetOps::Assign(this, rpUseInPlace, startArgUseInPlaceVars); - tree->gtUsedRegs |= args->gtUsedRegs; - } - - /* Is there a late argument list */ - - regIndex = 0; - regArgMask = RBM_NONE; // Set of argument registers that have already been setup. - args = NULL; - - /* process the late argument list */ - for (list = tree->gtCall.gtCallLateArgs; list; regIndex++) - { - // If the current argument being copied is a promoted struct local, set this pointer to its description. - LclVarDsc* promotedStructLocal = NULL; - - curArgMask = RBM_NONE; // Set of argument registers that are going to be setup by this arg - tmpMask = RBM_NONE; // Set of additional temp registers that are need only to setup the current arg - - assert(list->OperIsList()); - - args = list->Current(); - list = list->Rest(); - - assert(!args->IsArgPlaceHolderNode()); // No place holders nodes are in gtCallLateArgs; - - fgArgTabEntry* curArgTabEntry = gtArgEntryByNode(tree->AsCall(), args); - assert(curArgTabEntry); - - regNumber regNum = curArgTabEntry->regNum; // first register use to pass this argument - unsigned numSlots = - curArgTabEntry->numSlots; // number of outgoing arg stack slots used by this argument - - rpPredictReg argPredictReg; - regMaskTP avoidReg = RBM_NONE; - - if (regNum != REG_STK) - { - argPredictReg = rpGetPredictForReg(regNum); - curArgMask |= genRegMask(regNum); - } - else - { - assert(numSlots > 0); - argPredictReg = PREDICT_NONE; -#ifdef _TARGET_ARM_ - // Force the predictor to choose a low register when regNum is REG_STK to reduce code bloat - avoidReg = (RBM_R12 | RBM_LR); -#endif - } - -#ifdef _TARGET_ARM_ - // For TYP_LONG or TYP_DOUBLE register arguments we need to add the second argument register - // - if ((regNum != REG_STK) && ((args->TypeGet() == TYP_LONG) || (args->TypeGet() == TYP_DOUBLE))) - { - // 64-bit longs and doubles require 2 consecutive argument registers - curArgMask |= genRegMask(REG_NEXT(regNum)); - } - else if (args->TypeGet() == TYP_STRUCT) - { - GenTree* argx = args; - GenTree* lclVarTree = NULL; - - /* The GT_OBJ may be be a child of a GT_COMMA */ - while (argx->gtOper == GT_COMMA) - { - argx = argx->gtOp.gtOp2; - } - unsigned originalSize = 0; - - if (argx->gtOper == GT_OBJ) - { - originalSize = info.compCompHnd->getClassSize(argx->gtObj.gtClass); - - // Is it the address of a promoted struct local? - if (argx->gtObj.gtOp1->gtOper == GT_ADDR && argx->gtObj.gtOp1->gtOp.gtOp1->gtOper == GT_LCL_VAR) - { - lclVarTree = argx->gtObj.gtOp1->gtOp.gtOp1; - LclVarDsc* varDsc = &lvaTable[lclVarTree->gtLclVarCommon.gtLclNum]; - if (varDsc->lvPromoted) - promotedStructLocal = varDsc; - } - } - else if (argx->gtOper == GT_LCL_VAR) - { - varDsc = lvaTable + argx->gtLclVarCommon.gtLclNum; - originalSize = varDsc->lvSize(); - - // Is it a promoted struct local? - if (varDsc->lvPromoted) - promotedStructLocal = varDsc; - } - else if (argx->gtOper == GT_MKREFANY) - { - originalSize = 2 * TARGET_POINTER_SIZE; - } - else - { - noway_assert(!"Can't predict unsupported TYP_STRUCT arg kind"); - } - - // We only pass arguments differently if it a struct local "independently" promoted, which - // allows the field locals can be independently enregistered. - if (promotedStructLocal != NULL) - { - if (lvaGetPromotionType(promotedStructLocal) != PROMOTION_TYPE_INDEPENDENT) - promotedStructLocal = NULL; - } - - unsigned slots = ((unsigned)(roundUp(originalSize, TARGET_POINTER_SIZE))) / REGSIZE_BYTES; - - // Are we passing a TYP_STRUCT in multiple integer registers? - // if so set up curArgMask to reflect this - // Also slots is updated to reflect the number of outgoing arg slots that we will write - if (regNum != REG_STK) - { - regNumber regLast = (curArgTabEntry->isHfaRegArg) ? LAST_FP_ARGREG : REG_ARG_LAST; - assert(genIsValidReg(regNum)); - regNumber nextReg = REG_NEXT(regNum); - slots--; - while (slots > 0 && nextReg <= regLast) - { - curArgMask |= genRegMask(nextReg); - nextReg = REG_NEXT(nextReg); - slots--; - } - } - - if ((promotedStructLocal != NULL) && (curArgMask != RBM_NONE)) - { - // All or a portion of this struct will be placed in the argument registers indicated by - // "curArgMask". We build in knowledge of the order in which the code is generated here, so - // that the second arg to be evaluated interferes with the reg for the first, the third with - // the regs for the first and second, etc. But since we always place the stack slots before - // placing the register slots we do not add inteferences for any part of the struct that gets - // passed on the stack. - - argPredictReg = - PREDICT_NONE; // We will target the indivual fields into registers but not the whole struct - regMaskTP prevArgMask = RBM_NONE; - for (unsigned i = 0; i < promotedStructLocal->lvFieldCnt; i++) - { - LclVarDsc* fieldVarDsc = &lvaTable[promotedStructLocal->lvFieldLclStart + i]; - if (fieldVarDsc->lvTracked) - { - assert(lclVarTree != NULL); - if (prevArgMask != RBM_NONE) - { - rpRecordRegIntf(prevArgMask, VarSetOps::MakeSingleton(this, fieldVarDsc->lvVarIndex) - DEBUGARG("fieldVar/argReg")); - } - } - // Now see many registers this uses up. - unsigned firstRegOffset = fieldVarDsc->lvFldOffset / TARGET_POINTER_SIZE; - unsigned nextAfterLastRegOffset = - (fieldVarDsc->lvFldOffset + fieldVarDsc->lvExactSize + TARGET_POINTER_SIZE - 1) / - TARGET_POINTER_SIZE; - unsigned nextAfterLastArgRegOffset = - min(nextAfterLastRegOffset, - genIsValidIntReg(regNum) ? REG_NEXT(REG_ARG_LAST) : REG_NEXT(LAST_FP_ARGREG)); - - for (unsigned regOffset = firstRegOffset; regOffset < nextAfterLastArgRegOffset; - regOffset++) - { - prevArgMask |= genRegMask(regNumber(regNum + regOffset)); - } - - if (nextAfterLastRegOffset > nextAfterLastArgRegOffset) - { - break; - } - - if ((fieldVarDsc->lvFldOffset % TARGET_POINTER_SIZE) == 0) - { - // Add the argument register used here as a preferred register for this fieldVarDsc - // - regNumber firstRegUsed = regNumber(regNum + firstRegOffset); - fieldVarDsc->setPrefReg(firstRegUsed, this); - } - } - compUpdateLifeVar(argx); - } - - // If slots is greater than zero then part or all of this TYP_STRUCT - // argument is passed in the outgoing argument area. (except HFA arg) - // - if ((slots > 0) && !curArgTabEntry->isHfaRegArg) - { - // We will need a register to address the TYP_STRUCT - // Note that we can use an argument register in curArgMask as in - // codegen we pass the stack portion of the argument before we - // setup the register part. - // - - // Force the predictor to choose a LOW_REG here to reduce code bloat - avoidReg = (RBM_R12 | RBM_LR); - - assert(tmpMask == RBM_NONE); - tmpMask = rpPredictRegPick(TYP_INT, PREDICT_SCRATCH_REG, lockedRegs | regArgMask | avoidReg); - - // If slots > 1 then we will need a second register to perform the load/store into the outgoing - // arg area - if (slots > 1) - { - tmpMask |= rpPredictRegPick(TYP_INT, PREDICT_SCRATCH_REG, - lockedRegs | regArgMask | tmpMask | avoidReg); - } - } - } // (args->TypeGet() == TYP_STRUCT) -#endif // _TARGET_ARM_ - - // If we have a promotedStructLocal we don't need to call rpPredictTreeRegUse(args, ... - // as we have already calculated the correct tmpMask and curArgMask values and - // by calling rpPredictTreeRegUse we would just add unnecessary register inteferences. - // - if (promotedStructLocal == NULL) - { - /* Target the appropriate argument register */ - tmpMask |= rpPredictTreeRegUse(args, argPredictReg, lockedRegs | regArgMask, RBM_LASTUSE); - } - - // We mark OBJ(ADDR(LOCAL)) with GTF_VAR_DEATH since the local is required to live - // for the duration of the OBJ. - if (args->OperGet() == GT_OBJ && (args->gtFlags & GTF_VAR_DEATH)) - { - GenTree* lclVarTree = fgIsIndirOfAddrOfLocal(args); - assert(lclVarTree != NULL); // Or else would not be marked with GTF_VAR_DEATH. - compUpdateLifeVar(lclVarTree); - } - - regArgMask |= curArgMask; - args->gtUsedRegs |= (tmpMask | regArgMask); - tree->gtUsedRegs |= args->gtUsedRegs; - tree->gtCall.gtCallLateArgs->gtUsedRegs |= args->gtUsedRegs; - - if (args->gtUsedRegs != RBM_NONE) - { - // Add register interference with the set of registers used or in use when we evaluated - // the current arg, with whatever is alive after the current arg - // - rpRecordRegIntf(args->gtUsedRegs, compCurLife DEBUGARG("register arg setup")); - } - VarSetOps::Assign(this, rpUseInPlace, startArgUseInPlaceVars); - } - assert(list == NULL); - -#ifdef LEGACY_BACKEND -#if CPU_LOAD_STORE_ARCH -#ifdef FEATURE_READYTORUN_COMPILER - if (tree->gtCall.IsR2RRelativeIndir()) - { - tree->gtUsedRegs |= RBM_R2R_INDIRECT_PARAM; - } -#endif // FEATURE_READYTORUN_COMPILER -#endif // CPU_LOAD_STORE_ARCH -#endif // LEGACY_BACKEND - - regMaskTP callAddrMask; - callAddrMask = RBM_NONE; -#if CPU_LOAD_STORE_ARCH - predictReg = PREDICT_SCRATCH_REG; -#else - predictReg = PREDICT_NONE; -#endif - - switch (tree->gtFlags & GTF_CALL_VIRT_KIND_MASK) - { - case GTF_CALL_VIRT_STUB: - - // We only want to record an interference between the virtual stub - // param reg and anything that's live AFTER the call, but we've not - // yet processed the indirect target. So add virtualStubParamInfo.regMask - // to interferingRegs. - interferingRegs |= virtualStubParamInfo->GetRegMask(); -#ifdef DEBUG - if (verbose) - printf("Adding interference with Virtual Stub Param\n"); -#endif - codeGen->regSet.rsSetRegsModified(virtualStubParamInfo->GetRegMask()); - - if (tree->gtCall.gtCallType == CT_INDIRECT) - { - predictReg = virtualStubParamInfo->GetPredict(); - } - break; - - case GTF_CALL_VIRT_VTABLE: - predictReg = PREDICT_SCRATCH_REG; - break; - - case GTF_CALL_NONVIRT: - predictReg = PREDICT_SCRATCH_REG; - break; - } - - if (tree->gtCall.gtCallType == CT_INDIRECT) - { -#if defined(_TARGET_ARM_) || defined(_TARGET_AMD64_) - if (tree->gtCall.gtCallCookie) - { - codeGen->regSet.rsSetRegsModified(RBM_PINVOKE_COOKIE_PARAM | RBM_PINVOKE_TARGET_PARAM); - - callAddrMask |= rpPredictTreeRegUse(tree->gtCall.gtCallCookie, PREDICT_REG_PINVOKE_COOKIE_PARAM, - lockedRegs | regArgMask, RBM_LASTUSE); - - // Just in case we predict some other registers, force interference with our two special - // parameters: PINVOKE_COOKIE_PARAM & PINVOKE_TARGET_PARAM - callAddrMask |= (RBM_PINVOKE_COOKIE_PARAM | RBM_PINVOKE_TARGET_PARAM); - - predictReg = PREDICT_REG_PINVOKE_TARGET_PARAM; - } -#endif - callAddrMask |= - rpPredictTreeRegUse(tree->gtCall.gtCallAddr, predictReg, lockedRegs | regArgMask, RBM_LASTUSE); - } - else if (predictReg != PREDICT_NONE) - { - callAddrMask |= rpPredictRegPick(TYP_I_IMPL, predictReg, lockedRegs | regArgMask); - } - - if (tree->gtFlags & GTF_CALL_UNMANAGED) - { - // Need a register for tcbReg - callAddrMask |= - rpPredictRegPick(TYP_I_IMPL, PREDICT_SCRATCH_REG, lockedRegs | regArgMask | callAddrMask); -#if CPU_LOAD_STORE_ARCH - // Need an extra register for tmpReg - callAddrMask |= - rpPredictRegPick(TYP_I_IMPL, PREDICT_SCRATCH_REG, lockedRegs | regArgMask | callAddrMask); -#endif - } - - tree->gtUsedRegs |= callAddrMask; - - /* After the call restore the orginal value of lockedRegs */ - lockedRegs |= keepMask; - - /* set the return register */ - regMask = genReturnRegForTree(tree); - - if (regMask & rsvdRegs) - { - // We will need to relocate the return register value - regMaskTP intRegMask = (regMask & RBM_ALLINT); -#if FEATURE_FP_REGALLOC - regMaskTP floatRegMask = (regMask & RBM_ALLFLOAT); -#endif - regMask = RBM_NONE; - - if (intRegMask) - { - if (intRegMask == RBM_INTRET) - { - regMask |= rpPredictRegPick(TYP_INT, PREDICT_SCRATCH_REG, rsvdRegs | regMask); - } - else if (intRegMask == RBM_LNGRET) - { - regMask |= rpPredictRegPick(TYP_LONG, PREDICT_SCRATCH_REG, rsvdRegs | regMask); - } - else - { - noway_assert(!"unexpected return regMask"); - } - } - -#if FEATURE_FP_REGALLOC - if (floatRegMask) - { - if (floatRegMask == RBM_FLOATRET) - { - regMask |= rpPredictRegPick(TYP_FLOAT, PREDICT_SCRATCH_REG, rsvdRegs | regMask); - } - else if (floatRegMask == RBM_DOUBLERET) - { - regMask |= rpPredictRegPick(TYP_DOUBLE, PREDICT_SCRATCH_REG, rsvdRegs | regMask); - } - else // HFA return case - { - for (unsigned f = 0; f < genCountBits(floatRegMask); f++) - { - regMask |= rpPredictRegPick(TYP_FLOAT, PREDICT_SCRATCH_REG, rsvdRegs | regMask); - } - } - } -#endif - } - - /* the return registers (if any) are killed */ - tree->gtUsedRegs |= regMask; - -#if GTF_CALL_REG_SAVE - if (!(tree->gtFlags & GTF_CALL_REG_SAVE)) -#endif - { - /* the RBM_CALLEE_TRASH set are killed (i.e. EAX,ECX,EDX) */ - tree->gtUsedRegs |= RBM_CALLEE_TRASH; - } - } - -#if defined(_TARGET_ARM_) && defined(PROFILING_SUPPORTED) - // Mark required registers for emitting tailcall profiler callback as used - if (compIsProfilerHookNeeded() && tree->gtCall.IsTailCall() && (tree->gtCall.gtCallType == CT_USER_FUNC)) - { - tree->gtUsedRegs |= RBM_PROFILER_TAIL_USED; - } -#endif - break; - - case GT_ARR_ELEM: - - // Figure out which registers can't be touched - unsigned dim; - for (dim = 0; dim < tree->gtArrElem.gtArrRank; dim++) - rsvdRegs |= tree->gtArrElem.gtArrInds[dim]->gtRsvdRegs; - - regMask = rpPredictTreeRegUse(tree->gtArrElem.gtArrObj, PREDICT_REG, lockedRegs, rsvdRegs); - - regMaskTP dimsMask; - dimsMask = 0; - -#if CPU_LOAD_STORE_ARCH - // We need a register to load the bounds of the MD array - regMask |= rpPredictRegPick(TYP_INT, PREDICT_SCRATCH_REG, lockedRegs | regMask); -#endif - - for (dim = 0; dim < tree->gtArrElem.gtArrRank; dim++) - { - /* We need scratch registers to compute index-lower_bound. - Also, gtArrInds[0]'s register will be used as the second - addressability register (besides gtArrObj's) */ - - regMaskTP dimMask = rpPredictTreeRegUse(tree->gtArrElem.gtArrInds[dim], PREDICT_SCRATCH_REG, - lockedRegs | regMask | dimsMask, rsvdRegs); - if (dim == 0) - regMask |= dimMask; - - dimsMask |= dimMask; - } -#ifdef _TARGET_XARCH_ - // INS_imul doesnt have an immediate constant. - if (!jitIsScaleIndexMul(tree->gtArrElem.gtArrElemSize)) - regMask |= rpPredictRegPick(TYP_INT, PREDICT_SCRATCH_REG, lockedRegs | regMask | dimsMask); -#endif - tree->gtUsedRegs = (regMaskSmall)(regMask | dimsMask); - break; - - case GT_CMPXCHG: - { -#ifdef _TARGET_XARCH_ - rsvdRegs |= RBM_EAX; -#endif - if (tree->gtCmpXchg.gtOpLocation->OperGet() == GT_LCL_VAR) - { - regMask = rpPredictTreeRegUse(tree->gtCmpXchg.gtOpLocation, PREDICT_REG, lockedRegs, rsvdRegs); - } - else - { - regMask = rpPredictTreeRegUse(tree->gtCmpXchg.gtOpLocation, PREDICT_ADDR, lockedRegs, rsvdRegs); - } - op2Mask = rpPredictTreeRegUse(tree->gtCmpXchg.gtOpValue, PREDICT_REG, lockedRegs, rsvdRegs | regMask); - -#ifdef _TARGET_XARCH_ - rsvdRegs &= ~RBM_EAX; - tmpMask = rpPredictTreeRegUse(tree->gtCmpXchg.gtOpComparand, PREDICT_REG_EAX, lockedRegs, - rsvdRegs | regMask | op2Mask); - tree->gtUsedRegs = (regMaskSmall)(RBM_EAX | regMask | op2Mask | tmpMask); - predictReg = PREDICT_REG_EAX; // When this is done the result is always in EAX. -#else - tmpMask = 0; - tree->gtUsedRegs = (regMaskSmall)(regMask | op2Mask | tmpMask); -#endif - } - break; - - case GT_ARR_BOUNDS_CHECK: - { - regMaskTP opArrLenRsvd = rsvdRegs | tree->gtBoundsChk.gtIndex->gtRsvdRegs; - regMask = rpPredictTreeRegUse(tree->gtBoundsChk.gtArrLen, PREDICT_REG, lockedRegs, opArrLenRsvd); - rpPredictTreeRegUse(tree->gtBoundsChk.gtIndex, PREDICT_REG, lockedRegs | regMask, RBM_LASTUSE); - - tree->gtUsedRegs = - (regMaskSmall)regMask | tree->gtBoundsChk.gtArrLen->gtUsedRegs | tree->gtBoundsChk.gtIndex->gtUsedRegs; - } - break; - - default: - NO_WAY("unexpected special operator in reg use prediction"); - break; - } - -RETURN_CHECK: - -#ifdef DEBUG - /* make sure we set them to something reasonable */ - if (tree->gtUsedRegs & RBM_ILLEGAL) - noway_assert(!"used regs not set properly in reg use prediction"); - - if (regMask & RBM_ILLEGAL) - noway_assert(!"return value not set propery in reg use prediction"); - -#endif - - /* - * If the gtUsedRegs conflicts with lockedRegs - * then we going to have to spill some registers - * into the non-trashed register set to keep it alive - */ - regMaskTP spillMask; - spillMask = tree->gtUsedRegs & lockedRegs; - - if (spillMask) - { - while (spillMask) - { - /* Find the next register that needs to be spilled */ - tmpMask = genFindLowestBit(spillMask); - -#ifdef DEBUG - if (verbose) - { - printf("Predict spill of %s before: ", getRegName(genRegNumFromMask(tmpMask))); - gtDispTree(tree, 0, NULL, true); - if ((tmpMask & regMask) == 0) - { - printf("Predict reload of %s after : ", getRegName(genRegNumFromMask(tmpMask))); - gtDispTree(tree, 0, NULL, true); - } - } -#endif - /* In Codegen it will typically introduce a spill temp here */ - /* rather than relocating the register to a non trashed reg */ - rpPredictSpillCnt++; - - /* Remove it from the spillMask */ - spillMask &= ~tmpMask; - } - } - - /* - * If the return registers in regMask conflicts with the lockedRegs - * then we allocate extra registers for the reload of the conflicting - * registers. - * - * Set spillMask to the set of locked registers that have to be reloaded here. - * reloadMask is set to the extra registers that are used to reload - * the spilled lockedRegs. - */ - - noway_assert(regMask != DUMMY_INIT(RBM_ILLEGAL)); - spillMask = lockedRegs & regMask; - - if (spillMask) - { - /* Remove the spillMask from regMask */ - regMask &= ~spillMask; - - regMaskTP reloadMask = RBM_NONE; - while (spillMask) - { - /* Get an extra register to hold it */ - regMaskTP reloadReg = rpPredictRegPick(TYP_INT, PREDICT_REG, lockedRegs | regMask | reloadMask); -#ifdef DEBUG - if (verbose) - { - printf("Predict reload into %s after : ", getRegName(genRegNumFromMask(reloadReg))); - gtDispTree(tree, 0, NULL, true); - } -#endif - reloadMask |= reloadReg; - - /* Remove it from the spillMask */ - spillMask &= ~genFindLowestBit(spillMask); - } - - /* Update regMask to use the reloadMask */ - regMask |= reloadMask; - - /* update the gtUsedRegs mask */ - tree->gtUsedRegs |= (regMaskSmall)regMask; - } - - regMaskTP regUse = tree->gtUsedRegs; - regUse |= interferingRegs; - - if (!VarSetOps::IsEmpty(this, compCurLife)) - { - // Add interference between the current set of live variables and - // the set of temporary registers need to evaluate the sub tree - if (regUse) - { - rpRecordRegIntf(regUse, compCurLife DEBUGARG("tmp use")); - } - } - - if (rpAsgVarNum != -1) - { - // Add interference between the registers used (if any) - // and the assignment target variable - if (regUse) - { - rpRecordRegIntf(regUse, VarSetOps::MakeSingleton(this, rpAsgVarNum) DEBUGARG("tgt var tmp use")); - } - - // Add a variable interference from rpAsgVarNum (i.e. the enregistered left hand - // side of the assignment passed here using PREDICT_REG_VAR_Txx) - // to the set of currently live variables. This new interference will prevent us - // from using the register value used here for enregistering different live variable - // - if (!VarSetOps::IsEmpty(this, compCurLife)) - { - rpRecordVarIntf(rpAsgVarNum, compCurLife DEBUGARG("asg tgt live conflict")); - } - } - - /* Do we need to resore the oldLastUseVars value */ - if (restoreLastUseVars) - { - /* If we used a GT_ASG targeted register then we need to add - * a variable interference between any new last use variables - * and the GT_ASG targeted register - */ - if (!VarSetOps::Equal(this, rpLastUseVars, oldLastUseVars) && rpAsgVarNum != -1) - { - rpRecordVarIntf(rpAsgVarNum, VarSetOps::Diff(this, rpLastUseVars, oldLastUseVars) - DEBUGARG("asgn tgt last use conflict")); - } - VarSetOps::Assign(this, rpLastUseVars, oldLastUseVars); - } - - return regMask; -} -#ifdef _PREFAST_ -#pragma warning(pop) -#endif - -#endif // LEGACY_BACKEND - -/****************************************************************************/ -/* Returns true when we must create an EBP frame - This is used to force most managed methods to have EBP based frames - which allows the ETW kernel stackwalker to walk the stacks of managed code - this allows the kernel to perform light weight profiling - */ -bool Compiler::rpMustCreateEBPFrame(INDEBUG(const char** wbReason)) -{ - bool result = false; -#ifdef DEBUG - const char* reason = nullptr; -#endif - -#if ETW_EBP_FRAMED - if (!result && (opts.MinOpts() || opts.compDbgCode)) - { - INDEBUG(reason = "Debug Code"); - result = true; - } - if (!result && (info.compMethodInfo->ILCodeSize > DEFAULT_MAX_INLINE_SIZE)) - { - INDEBUG(reason = "IL Code Size"); - result = true; - } - if (!result && (fgBBcount > 3)) - { - INDEBUG(reason = "BasicBlock Count"); - result = true; - } - if (!result && fgHasLoops) - { - INDEBUG(reason = "Method has Loops"); - result = true; - } - if (!result && (optCallCount >= 2)) - { - INDEBUG(reason = "Call Count"); - result = true; - } - if (!result && (optIndirectCallCount >= 1)) - { - INDEBUG(reason = "Indirect Call"); - result = true; - } -#endif // ETW_EBP_FRAMED - - // VM wants to identify the containing frame of an InlinedCallFrame always - // via the frame register never the stack register so we need a frame. - if (!result && (optNativeCallCount != 0)) - { - INDEBUG(reason = "Uses PInvoke"); - result = true; - } - -#ifdef _TARGET_ARM64_ - // TODO-ARM64-NYI: This is temporary: force a frame pointer-based frame until genFnProlog can handle non-frame - // pointer frames. - if (!result) - { - INDEBUG(reason = "Temporary ARM64 force frame pointer"); - result = true; - } -#endif // _TARGET_ARM64_ - -#ifdef DEBUG - if ((result == true) && (wbReason != nullptr)) - { - *wbReason = reason; - } -#endif - - return result; -} - -#ifdef LEGACY_BACKEND // We don't use any of the old register allocator functions when LSRA is used instead. - -/***************************************************************************** - * - * Predict which variables will be assigned to registers - * This is x86 specific and only predicts the integer registers and - * must be conservative, any register that is predicted to be enregister - * must end up being enregistered. - * - * The rpPredictTreeRegUse takes advantage of the LCL_VARS that are - * predicted to be enregistered to minimize calls to rpPredictRegPick. - * - */ - -#ifdef _PREFAST_ -#pragma warning(push) -#pragma warning(disable : 21000) // Suppress PREFast warning about overly large function -#endif -regMaskTP Compiler::rpPredictAssignRegVars(regMaskTP regAvail) -{ - unsigned regInx; - - if (rpPasses <= rpPassesPessimize) - { - // Assume that we won't have to reverse EBP enregistration - rpReverseEBPenreg = false; - - // Set the default rpFrameType based upon codeGen->isFramePointerRequired() - if (codeGen->isFramePointerRequired() || codeGen->isFrameRequired()) - rpFrameType = FT_EBP_FRAME; - else - rpFrameType = FT_ESP_FRAME; - } - -#if !ETW_EBP_FRAMED - // If we are using FPBASE as the frame register, we cannot also use it for - // a local var - if (rpFrameType == FT_EBP_FRAME) - { - regAvail &= ~RBM_FPBASE; - } -#endif // !ETW_EBP_FRAMED - - rpStkPredict = 0; - rpPredictAssignMask = regAvail; - - raSetupArgMasks(&codeGen->intRegState); -#if !FEATURE_STACK_FP_X87 - raSetupArgMasks(&codeGen->floatRegState); -#endif - - // If there is a secret stub param, it is also live in - if (info.compPublishStubParam) - { - codeGen->intRegState.rsCalleeRegArgMaskLiveIn |= RBM_SECRET_STUB_PARAM; - } - - if (regAvail == RBM_NONE) - { - unsigned lclNum; - LclVarDsc* varDsc; - - for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++) - { -#if FEATURE_STACK_FP_X87 - if (!varDsc->IsFloatRegType()) -#endif - { - varDsc->lvRegNum = REG_STK; - if (isRegPairType(varDsc->lvType)) - varDsc->lvOtherReg = REG_STK; - } - } - } - -#ifdef DEBUG - if (verbose) - { - printf("\nCompiler::rpPredictAssignRegVars pass #%d", rpPasses); - printf("\n Available registers = "); - dspRegMask(regAvail); - printf("\n"); - } -#endif - - if (regAvail == RBM_NONE) - { - return RBM_NONE; - } - - /* We cannot change the lvVarIndexes at this point, so we */ - /* can only re-order the existing set of tracked variables */ - /* Which will change the order in which we select the */ - /* locals for enregistering. */ - - assert(lvaTrackedFixed); // We should have already set this to prevent us from adding any new tracked variables. - - // Should not be set unless optimizing - noway_assert((lvaSortAgain == false) || (opts.MinOpts() == false)); - - if (lvaSortAgain) - lvaSortOnly(); - -#ifdef DEBUG - fgDebugCheckBBlist(); -#endif - - /* Initialize the weighted count of variables that could have */ - /* been enregistered but weren't */ - unsigned refCntStk = 0; // sum of ref counts for all stack based variables - unsigned refCntEBP = 0; // sum of ref counts for EBP enregistered variables - unsigned refCntWtdEBP = 0; // sum of wtd ref counts for EBP enregistered variables -#if DOUBLE_ALIGN - unsigned refCntStkParam; // sum of ref counts for all stack based parameters - unsigned refCntWtdStkDbl; // sum of wtd ref counts for stack based doubles - -#if FEATURE_STACK_FP_X87 - refCntStkParam = raCntStkParamDblStackFP; - refCntWtdStkDbl = raCntWtdStkDblStackFP; - refCntStk = raCntStkStackFP; -#else - refCntStkParam = 0; - refCntWtdStkDbl = 0; - refCntStk = 0; -#endif // FEATURE_STACK_FP_X87 - -#endif // DOUBLE_ALIGN - - /* Set of registers used to enregister variables in the predition */ - regMaskTP regUsed = RBM_NONE; - - /*------------------------------------------------------------------------- - * - * Predict/Assign the enregistered locals in ref-count order - * - */ - - VARSET_TP unprocessedVars(VarSetOps::MakeFull(this)); - - unsigned FPRegVarLiveInCnt; - FPRegVarLiveInCnt = 0; // How many enregistered doubles are live on entry to the method - - LclVarDsc* varDsc; - for (unsigned sortNum = 0; sortNum < lvaCount; sortNum++) - { - bool notWorthy = false; - - unsigned varIndex; - bool isDouble; - regMaskTP regAvailForType; - var_types regType; - regMaskTP avoidReg; - unsigned customVarOrderSize; - regNumber customVarOrder[MAX_VAR_ORDER_SIZE]; - bool firstHalf; - regNumber saveOtherReg; - - varDsc = lvaRefSorted[sortNum]; - -#if FEATURE_STACK_FP_X87 - if (varTypeIsFloating(varDsc->TypeGet())) - { -#ifdef DEBUG - if (lvaIsFieldOfDependentlyPromotedStruct(varDsc)) - { - // Field local of a PROMOTION_TYPE_DEPENDENT struct should not - // be en-registered. - noway_assert(!varDsc->lvRegister); - } -#endif - continue; - } -#endif - - /* Check the set of invariant things that would prevent enregistration */ - - /* Ignore the variable if it's not tracked */ - if (!varDsc->lvTracked) - goto CANT_REG; - - /* Get hold of the index and the interference mask for the variable */ - varIndex = varDsc->lvVarIndex; - - // Remove 'varIndex' from unprocessedVars - VarSetOps::RemoveElemD(this, unprocessedVars, varIndex); - - // Skip the variable if it's marked as DoNotEnregister. - - if (varDsc->lvDoNotEnregister) - goto CANT_REG; - - /* TODO: For now if we have JMP all register args go to stack - * TODO: Later consider extending the life of the argument or make a copy of it */ - - if (compJmpOpUsed && varDsc->lvIsRegArg) - goto CANT_REG; - - /* Skip the variable if the ref count is zero */ - - if (varDsc->lvRefCnt == 0) - goto CANT_REG; - - /* Ignore field of PROMOTION_TYPE_DEPENDENT type of promoted struct */ - - if (lvaIsFieldOfDependentlyPromotedStruct(varDsc)) - { - goto CANT_REG; - } - - /* Is the unweighted ref count too low to be interesting? */ - - if (!varDsc->lvIsStructField && // We do encourage enregistering field locals. - (varDsc->lvRefCnt <= 1)) - { - /* Sometimes it's useful to enregister a variable with only one use */ - /* arguments referenced in loops are one example */ - - if (varDsc->lvIsParam && varDsc->lvRefCntWtd > BB_UNITY_WEIGHT) - goto OK_TO_ENREGISTER; - - /* If the variable has a preferred register set it may be useful to put it there */ - if (varDsc->lvPrefReg && varDsc->lvIsRegArg) - goto OK_TO_ENREGISTER; - - /* Keep going; the table is sorted by "weighted" ref count */ - goto CANT_REG; - } - - OK_TO_ENREGISTER: - - if (varTypeIsFloating(varDsc->TypeGet())) - { - regType = varDsc->TypeGet(); - regAvailForType = regAvail & RBM_ALLFLOAT; - } - else - { - regType = TYP_INT; - regAvailForType = regAvail & RBM_ALLINT; - } - -#ifdef _TARGET_ARM_ - isDouble = (varDsc->TypeGet() == TYP_DOUBLE); - - if (isDouble) - { - regAvailForType &= RBM_DBL_REGS; // We must restrict the set to the double registers - } -#endif - - /* If we don't have any registers available then skip the enregistration attempt */ - if (regAvailForType == RBM_NONE) - goto NO_REG; - - // On the pessimize passes don't even try to enregister LONGS - if (isRegPairType(varDsc->lvType)) - { - if (rpPasses > rpPassesPessimize) - goto NO_REG; - else if (rpLostEnreg && (rpPasses == rpPassesPessimize)) - goto NO_REG; - } - - // Set of registers to avoid when performing register allocation - avoidReg = RBM_NONE; - - if (!varDsc->lvIsRegArg) - { - /* For local variables, - * avoid the incoming arguments, - * but only if you conflict with them */ - - if (raAvoidArgRegMask != 0) - { - LclVarDsc* argDsc; - LclVarDsc* argsEnd = lvaTable + info.compArgsCount; - - for (argDsc = lvaTable; argDsc < argsEnd; argDsc++) - { - if (!argDsc->lvIsRegArg) - continue; - - bool isFloat = argDsc->IsFloatRegType(); - regNumber inArgReg = argDsc->lvArgReg; - regMaskTP inArgBit = genRegMask(inArgReg); - - // Is this inArgReg in the raAvoidArgRegMask set? - - if (!(raAvoidArgRegMask & inArgBit)) - continue; - - noway_assert(argDsc->lvIsParam); - noway_assert(inArgBit & (isFloat ? RBM_FLTARG_REGS : RBM_ARG_REGS)); - - unsigned locVarIndex = varDsc->lvVarIndex; - unsigned argVarIndex = argDsc->lvVarIndex; - - /* Does this variable interfere with the arg variable ? */ - if (VarSetOps::IsMember(this, lvaVarIntf[locVarIndex], argVarIndex)) - { - noway_assert(VarSetOps::IsMember(this, lvaVarIntf[argVarIndex], locVarIndex)); - /* Yes, so try to avoid the incoming arg reg */ - avoidReg |= inArgBit; - } - else - { - noway_assert(!VarSetOps::IsMember(this, lvaVarIntf[argVarIndex], locVarIndex)); - } - } - } - } - - // Now we will try to predict which register the variable - // could be enregistered in - - customVarOrderSize = MAX_VAR_ORDER_SIZE; - - raSetRegVarOrder(regType, customVarOrder, &customVarOrderSize, varDsc->lvPrefReg, avoidReg); - - firstHalf = false; - saveOtherReg = DUMMY_INIT(REG_NA); - - for (regInx = 0; regInx < customVarOrderSize; regInx++) - { - regNumber regNum = customVarOrder[regInx]; - regMaskTP regBits = genRegMask(regNum); - - /* Skip this register if it isn't available */ - if ((regAvailForType & regBits) == 0) - continue; - - /* Skip this register if it interferes with the variable */ - - if (VarSetOps::IsMember(this, raLclRegIntf[regNum], varIndex)) - continue; - - if (varTypeIsFloating(regType)) - { -#ifdef _TARGET_ARM_ - if (isDouble) - { - regNumber regNext = REG_NEXT(regNum); - regBits |= genRegMask(regNext); - - /* Skip if regNext interferes with the variable */ - if (VarSetOps::IsMember(this, raLclRegIntf[regNext], varIndex)) - continue; - } -#endif - } - - bool firstUseOfReg = ((regBits & (regUsed | codeGen->regSet.rsGetModifiedRegsMask())) == 0); - bool lessThanTwoRefWtd = (varDsc->lvRefCntWtd < (2 * BB_UNITY_WEIGHT)); - bool calleeSavedReg = ((regBits & RBM_CALLEE_SAVED) != 0); - - /* Skip this register if the weighted ref count is less than two - and we are considering a unused callee saved register */ - - if (lessThanTwoRefWtd && // less than two references (weighted) - firstUseOfReg && // first use of this register - calleeSavedReg) // callee saved register - { - unsigned int totalRefCntWtd = varDsc->lvRefCntWtd; - - // psc is abbeviation for possibleSameColor - VARSET_TP pscVarSet(VarSetOps::Diff(this, unprocessedVars, lvaVarIntf[varIndex])); - - VarSetOps::Iter pscIndexIter(this, pscVarSet); - unsigned pscIndex = 0; - while (pscIndexIter.NextElem(&pscIndex)) - { - LclVarDsc* pscVar = lvaTable + lvaTrackedToVarNum[pscIndex]; - totalRefCntWtd += pscVar->lvRefCntWtd; - if (totalRefCntWtd > (2 * BB_UNITY_WEIGHT)) - break; - } - - if (totalRefCntWtd <= (2 * BB_UNITY_WEIGHT)) - { - notWorthy = true; - continue; // not worth spilling a callee saved register - } - // otherwise we will spill this callee saved registers, - // because its uses when combined with the uses of - // other yet to be processed candidates exceed our threshold. - // totalRefCntWtd = totalRefCntWtd; - } - - /* Looks good - mark the variable as living in the register */ - - if (isRegPairType(varDsc->lvType)) - { - if (firstHalf == false) - { - /* Enregister the first half of the long */ - varDsc->lvRegNum = regNum; - saveOtherReg = varDsc->lvOtherReg; - varDsc->lvOtherReg = REG_STK; - firstHalf = true; - } - else - { - /* Ensure 'well-formed' register pairs */ - /* (those returned by gen[Pick|Grab]RegPair) */ - - if (regNum < varDsc->lvRegNum) - { - varDsc->lvOtherReg = varDsc->lvRegNum; - varDsc->lvRegNum = regNum; - } - else - { - varDsc->lvOtherReg = regNum; - } - firstHalf = false; - } - } - else - { - varDsc->lvRegNum = regNum; -#ifdef _TARGET_ARM_ - if (isDouble) - { - varDsc->lvOtherReg = REG_NEXT(regNum); - } -#endif - } - - if (regNum == REG_FPBASE) - { - refCntEBP += varDsc->lvRefCnt; - refCntWtdEBP += varDsc->lvRefCntWtd; -#if DOUBLE_ALIGN - if (varDsc->lvIsParam) - { - refCntStkParam += varDsc->lvRefCnt; - } -#endif - } - - /* Record this register in the regUsed set */ - regUsed |= regBits; - - /* The register is now ineligible for all interfering variables */ - - VarSetOps::UnionD(this, raLclRegIntf[regNum], lvaVarIntf[varIndex]); - -#ifdef _TARGET_ARM_ - if (isDouble) - { - regNumber secondHalf = REG_NEXT(regNum); - VarSetOps::Iter iter(this, lvaVarIntf[varIndex]); - unsigned intfIndex = 0; - while (iter.NextElem(&intfIndex)) - { - VarSetOps::AddElemD(this, raLclRegIntf[secondHalf], intfIndex); - } - } -#endif - - /* If a register argument, remove its incoming register - * from the "avoid" list */ - - if (varDsc->lvIsRegArg) - { - raAvoidArgRegMask &= ~genRegMask(varDsc->lvArgReg); -#ifdef _TARGET_ARM_ - if (isDouble) - { - raAvoidArgRegMask &= ~genRegMask(REG_NEXT(varDsc->lvArgReg)); - } -#endif - } - - /* A variable of TYP_LONG can take two registers */ - if (firstHalf) - continue; - - // Since we have successfully enregistered this variable it is - // now time to move on and consider the next variable - goto ENREG_VAR; - } - - if (firstHalf) - { - noway_assert(isRegPairType(varDsc->lvType)); - - /* This TYP_LONG is partially enregistered */ - - noway_assert(saveOtherReg != DUMMY_INIT(REG_NA)); - - if (varDsc->lvDependReg && (saveOtherReg != REG_STK)) - { - rpLostEnreg = true; - } - - raAddToStkPredict(varDsc->lvRefCntWtd); - goto ENREG_VAR; - } - - NO_REG:; - if (varDsc->lvDependReg) - { - rpLostEnreg = true; - } - - if (!notWorthy) - { - /* Weighted count of variables that could have been enregistered but weren't */ - raAddToStkPredict(varDsc->lvRefCntWtd); - - if (isRegPairType(varDsc->lvType) && (varDsc->lvOtherReg == REG_STK)) - raAddToStkPredict(varDsc->lvRefCntWtd); - } - - CANT_REG:; - varDsc->lvRegister = false; - - varDsc->lvRegNum = REG_STK; - if (isRegPairType(varDsc->lvType)) - varDsc->lvOtherReg = REG_STK; - - /* unweighted count of variables that were not enregistered */ - - refCntStk += varDsc->lvRefCnt; - -#if DOUBLE_ALIGN - if (varDsc->lvIsParam) - { - refCntStkParam += varDsc->lvRefCnt; - } - else - { - /* Is it a stack based double? */ - /* Note that double params are excluded since they can not be double aligned */ - if (varDsc->lvType == TYP_DOUBLE) - { - refCntWtdStkDbl += varDsc->lvRefCntWtd; - } - } -#endif -#ifdef DEBUG - if (verbose) - { - printf("; "); - gtDispLclVar((unsigned)(varDsc - lvaTable)); - if (varDsc->lvTracked) - printf("T%02u", varDsc->lvVarIndex); - else - printf(" "); - printf(" (refcnt=%2u,refwtd=%s) not enregistered", varDsc->lvRefCnt, refCntWtd2str(varDsc->lvRefCntWtd)); - if (varDsc->lvDoNotEnregister) - printf(", do-not-enregister"); - printf("\n"); - } -#endif - continue; - - ENREG_VAR:; - - varDsc->lvRegister = true; - - // Record the fact that we enregistered a stack arg when tail call is used. - if (compJmpOpUsed && !varDsc->lvIsRegArg) - { - rpMaskPInvokeEpilogIntf |= genRegMask(varDsc->lvRegNum); - if (isRegPairType(varDsc->lvType)) - { - rpMaskPInvokeEpilogIntf |= genRegMask(varDsc->lvOtherReg); - } - } - -#ifdef DEBUG - if (verbose) - { - printf("; "); - gtDispLclVar((unsigned)(varDsc - lvaTable)); - printf("T%02u (refcnt=%2u,refwtd=%s) predicted to be assigned to ", varIndex, varDsc->lvRefCnt, - refCntWtd2str(varDsc->lvRefCntWtd)); - varDsc->PrintVarReg(); -#ifdef _TARGET_ARM_ - if (isDouble) - { - printf(":%s", getRegName(varDsc->lvOtherReg)); - } -#endif - printf("\n"); - } -#endif - } - -#if ETW_EBP_FRAMED - noway_assert(refCntEBP == 0); -#endif - -#ifdef DEBUG - if (verbose) - { - if (refCntStk > 0) - printf("; refCntStk = %u\n", refCntStk); - if (refCntEBP > 0) - printf("; refCntEBP = %u\n", refCntEBP); - if (refCntWtdEBP > 0) - printf("; refCntWtdEBP = %u\n", refCntWtdEBP); -#if DOUBLE_ALIGN - if (refCntStkParam > 0) - printf("; refCntStkParam = %u\n", refCntStkParam); - if (refCntWtdStkDbl > 0) - printf("; refCntWtdStkDbl = %u\n", refCntWtdStkDbl); -#endif - } -#endif - - /* Determine how the EBP register should be used */ - CLANG_FORMAT_COMMENT_ANCHOR; - -#if DOUBLE_ALIGN - - if (!codeGen->isFramePointerRequired()) - { - noway_assert(getCanDoubleAlign() < COUNT_DOUBLE_ALIGN); - - /* - First let us decide if we should use EBP to create a - double-aligned frame, instead of enregistering variables - */ - - if (getCanDoubleAlign() == MUST_DOUBLE_ALIGN) - { - rpFrameType = FT_DOUBLE_ALIGN_FRAME; - goto REVERSE_EBP_ENREG; - } - - if (getCanDoubleAlign() == CAN_DOUBLE_ALIGN && (refCntWtdStkDbl > 0)) - { - if (shouldDoubleAlign(refCntStk, refCntEBP, refCntWtdEBP, refCntStkParam, refCntWtdStkDbl)) - { - rpFrameType = FT_DOUBLE_ALIGN_FRAME; - goto REVERSE_EBP_ENREG; - } - } - } - -#endif // DOUBLE_ALIGN - - if (!codeGen->isFramePointerRequired() && !codeGen->isFrameRequired()) - { -#ifdef _TARGET_XARCH_ -// clang-format off - /* If we are using EBP to enregister variables then - will we actually save bytes by setting up an EBP frame? - - Each stack reference is an extra byte of code if we use - an ESP frame. - - Here we measure the savings that we get by using EBP to - enregister variables vs. the cost in code size that we - pay when using an ESP based frame. - - We pay one byte of code for each refCntStk - but we save one byte (or more) for each refCntEBP. - - Our savings are the elimination of a stack memory read/write. - We use the loop weighted value of - refCntWtdEBP * mem_access_weight (0, 3, 6) - to represent this savings. - */ - - // We also pay 5 extra bytes for the MOV EBP,ESP and LEA ESP,[EBP-0x10] - // to set up an EBP frame in the prolog and epilog - #define EBP_FRAME_SETUP_SIZE 5 - // clang-format on - - if (refCntStk > (refCntEBP + EBP_FRAME_SETUP_SIZE)) - { - unsigned bytesSaved = refCntStk - (refCntEBP + EBP_FRAME_SETUP_SIZE); - unsigned mem_access_weight = 3; - - if (compCodeOpt() == SMALL_CODE) - mem_access_weight = 0; - else if (compCodeOpt() == FAST_CODE) - mem_access_weight *= 2; - - if (bytesSaved > ((refCntWtdEBP * mem_access_weight) / BB_UNITY_WEIGHT)) - { - /* It's not be a good idea to use EBP in our predictions */ - CLANG_FORMAT_COMMENT_ANCHOR; -#ifdef DEBUG - if (verbose && (refCntEBP > 0)) - printf("; Predicting that it's not worth using EBP to enregister variables\n"); -#endif - rpFrameType = FT_EBP_FRAME; - goto REVERSE_EBP_ENREG; - } - } -#endif // _TARGET_XARCH_ - - if ((rpFrameType == FT_NOT_SET) || (rpFrameType == FT_ESP_FRAME)) - { -#ifdef DEBUG - const char* reason; -#endif - if (rpMustCreateEBPCalled == false) - { - rpMustCreateEBPCalled = true; - if (rpMustCreateEBPFrame(INDEBUG(&reason))) - { -#ifdef DEBUG - if (verbose) - printf("; Decided to create an EBP based frame for ETW stackwalking (%s)\n", reason); -#endif - codeGen->setFrameRequired(true); - - rpFrameType = FT_EBP_FRAME; - goto REVERSE_EBP_ENREG; - } - } - } - } - - goto EXIT; - -REVERSE_EBP_ENREG: - - noway_assert(rpFrameType != FT_ESP_FRAME); - - rpReverseEBPenreg = true; - -#if !ETW_EBP_FRAMED - if (refCntEBP > 0) - { - noway_assert(regUsed & RBM_FPBASE); - - regUsed &= ~RBM_FPBASE; - - /* variables that were enregistered in EBP become stack based variables */ - raAddToStkPredict(refCntWtdEBP); - - unsigned lclNum; - - /* We're going to have to undo some predicted enregistered variables */ - for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++) - { - /* Is this a register variable? */ - if (varDsc->lvRegNum != REG_STK) - { - if (isRegPairType(varDsc->lvType)) - { - /* Only one can be EBP */ - if (varDsc->lvRegNum == REG_FPBASE || varDsc->lvOtherReg == REG_FPBASE) - { - if (varDsc->lvRegNum == REG_FPBASE) - varDsc->lvRegNum = varDsc->lvOtherReg; - - varDsc->lvOtherReg = REG_STK; - - if (varDsc->lvRegNum == REG_STK) - varDsc->lvRegister = false; - - if (varDsc->lvDependReg) - rpLostEnreg = true; -#ifdef DEBUG - if (verbose) - goto DUMP_MSG; -#endif - } - } - else - { - if ((varDsc->lvRegNum == REG_FPBASE) && (!varDsc->IsFloatRegType())) - { - varDsc->lvRegNum = REG_STK; - - varDsc->lvRegister = false; - - if (varDsc->lvDependReg) - rpLostEnreg = true; -#ifdef DEBUG - if (verbose) - { - DUMP_MSG: - printf("; reversing enregisteration of V%02u,T%02u (refcnt=%2u,refwtd=%4u%s)\n", lclNum, - varDsc->lvVarIndex, varDsc->lvRefCnt, varDsc->lvRefCntWtd / 2, - (varDsc->lvRefCntWtd & 1) ? ".5" : ""); - } -#endif - } - } - } - } - } -#endif // ETW_EBP_FRAMED - -EXIT:; - - unsigned lclNum; - for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++) - { - /* Clear the lvDependReg flag for next iteration of the predictor */ - varDsc->lvDependReg = false; - - // If we set rpLostEnreg and this is the first pessimize pass - // then reverse the enreg of all TYP_LONG - if (rpLostEnreg && isRegPairType(varDsc->lvType) && (rpPasses == rpPassesPessimize)) - { - varDsc->lvRegNum = REG_STK; - varDsc->lvOtherReg = REG_STK; - } - } - -#ifdef DEBUG - if (verbose && raNewBlocks) - { - printf("\nAdded FP register killing blocks:\n"); - fgDispBasicBlocks(); - printf("\n"); - } -#endif - noway_assert(rpFrameType != FT_NOT_SET); - - /* return the set of registers used to enregister variables */ - return regUsed; -} -#ifdef _PREFAST_ -#pragma warning(pop) -#endif - -/***************************************************************************** - * - * Predict register use for every tree in the function. Note that we do this - * at different times (not to mention in a totally different way) for x86 vs - * RISC targets. - */ -void Compiler::rpPredictRegUse() -{ -#ifdef DEBUG - if (verbose) - raDumpVarIntf(); -#endif - - // We might want to adjust the ref counts based on interference - raAdjustVarIntf(); - - regMaskTP allAcceptableRegs = RBM_ALLINT; - -#if FEATURE_FP_REGALLOC - allAcceptableRegs |= raConfigRestrictMaskFP(); -#endif - - allAcceptableRegs &= ~codeGen->regSet.rsMaskResvd; // Remove any register reserved for special purposes - - /* For debuggable code, genJumpToThrowHlpBlk() generates an inline call - to acdHelper(). This is done implicitly, without creating a GT_CALL - node. Hence, this interference is be handled implicitly by - restricting the registers used for enregistering variables */ - - if (opts.compDbgCode) - { - allAcceptableRegs &= RBM_CALLEE_SAVED; - } - - /* Compute the initial regmask to use for the first pass */ - regMaskTP regAvail = RBM_CALLEE_SAVED & allAcceptableRegs; - regMaskTP regUsed; - -#if CPU_USES_BLOCK_MOVE - /* If we might need to generate a rep mov instruction */ - /* remove ESI and EDI */ - if (compBlkOpUsed) - regAvail &= ~(RBM_ESI | RBM_EDI); -#endif - -#ifdef _TARGET_X86_ - /* If we using longs then we remove ESI to allow */ - /* ESI:EBX to be saved accross a call */ - if (compLongUsed) - regAvail &= ~(RBM_ESI); -#endif - -#ifdef _TARGET_ARM_ - // For the first register allocation pass we don't want to color using r4 - // as we want to allow it to be used to color the internal temps instead - // when r0,r1,r2,r3 are all in use. - // - regAvail &= ~(RBM_R4); -#endif - -#if ETW_EBP_FRAMED - // We never have EBP available when ETW_EBP_FRAME is defined - regAvail &= ~RBM_FPBASE; -#else - /* If a frame pointer is required then we remove EBP */ - if (codeGen->isFramePointerRequired() || codeGen->isFrameRequired()) - regAvail &= ~RBM_FPBASE; -#endif - -#ifdef DEBUG - BOOL fJitNoRegLoc = JitConfig.JitNoRegLoc(); - if (fJitNoRegLoc) - regAvail = RBM_NONE; -#endif - - if ((opts.compFlags & CLFLG_REGVAR) == 0) - regAvail = RBM_NONE; - -#if FEATURE_STACK_FP_X87 - VarSetOps::AssignNoCopy(this, optAllNonFPvars, VarSetOps::MakeEmpty(this)); - VarSetOps::AssignNoCopy(this, optAllFloatVars, VarSetOps::MakeEmpty(this)); - - // Calculate the set of all tracked FP/non-FP variables - // into optAllFloatVars and optAllNonFPvars - - unsigned lclNum; - LclVarDsc* varDsc; - - for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++) - { - /* Ignore the variable if it's not tracked */ - - if (!varDsc->lvTracked) - continue; - - /* Get hold of the index and the interference mask for the variable */ - - unsigned varNum = varDsc->lvVarIndex; - - /* add to the set of all tracked FP/non-FP variables */ - - if (varDsc->IsFloatRegType()) - VarSetOps::AddElemD(this, optAllFloatVars, varNum); - else - VarSetOps::AddElemD(this, optAllNonFPvars, varNum); - } -#endif - - for (unsigned i = 0; i < REG_COUNT; i++) - { - VarSetOps::AssignNoCopy(this, raLclRegIntf[i], VarSetOps::MakeEmpty(this)); - } - for (unsigned i = 0; i < lvaTrackedCount; i++) - { - VarSetOps::AssignNoCopy(this, lvaVarPref[i], VarSetOps::MakeEmpty(this)); + // OK we passed all of the benefit tests, so we'll predict a double aligned frame. + JITDUMP(" Predicting to create a double-aligned frame\n"); + doDoubleAlign = true; } + return doDoubleAlign; +} +#endif // DOUBLE_ALIGN - raNewBlocks = false; - rpPredictAssignAgain = false; - rpPasses = 0; - - bool mustPredict = true; - unsigned stmtNum = 0; - unsigned oldStkPredict = DUMMY_INIT(~0); - VARSET_TP oldLclRegIntf[REG_COUNT]; +// The code to set the regState for each arg is outlined for shared use +// by linear scan. (It is not shared for System V AMD64 platform.) +regNumber Compiler::raUpdateRegStateForArg(RegState* regState, LclVarDsc* argDsc) +{ + regNumber inArgReg = argDsc->lvArgReg; + regMaskTP inArgMask = genRegMask(inArgReg); - for (unsigned i = 0; i < REG_COUNT; i++) + if (regState->rsIsFloat) { - VarSetOps::AssignNoCopy(this, oldLclRegIntf[i], VarSetOps::MakeEmpty(this)); + noway_assert(inArgMask & RBM_FLTARG_REGS); } - - while (true) + else // regState is for the integer registers { - /* Assign registers to variables using the variable/register interference - graph (raLclRegIntf[]) calculated in the previous pass */ - regUsed = rpPredictAssignRegVars(regAvail); - - mustPredict |= rpLostEnreg; - -#ifdef _TARGET_ARM_ - // See if we previously reserved REG_R10 and try to make it available if we have a small frame now - if ((rpPasses == 0) && ((codeGen->regSet.rsMaskResvd & RBM_OPT_RSVD) != 0) && - !compRsvdRegCheck(REGALLOC_FRAME_LAYOUT)) - { - // We can release our reservation on R10 and use it to color registers - codeGen->regSet.rsMaskResvd &= ~RBM_OPT_RSVD; - allAcceptableRegs |= RBM_OPT_RSVD; - } -#endif - - /* Is our new prediction good enough?? */ - if (!mustPredict) + // This might be the fixed return buffer register argument (on ARM64) + // We check and allow inArgReg to be theFixedRetBuffReg + if (hasFixedRetBuffReg() && (inArgReg == theFixedRetBuffReg())) { - /* For small methods (less than 12 stmts), we add a */ - /* extra pass if we are predicting the use of some */ - /* of the caller saved registers. */ - /* This fixes RAID perf bug 43440 VB Ackerman function */ - - if ((rpPasses == 1) && (stmtNum <= 12) && (regUsed & RBM_CALLEE_SAVED)) - { - goto EXTRA_PASS; - } - - /* If every variable was fully enregistered then we're done */ - if (rpStkPredict == 0) - goto ALL_DONE; - - // This was a successful prediction. Record it, in case it turns out to be the best one. - rpRecordPrediction(); - - if (rpPasses > 1) - { - noway_assert(oldStkPredict != (unsigned)DUMMY_INIT(~0)); - - // Be careful about overflow - unsigned highStkPredict = (rpStkPredict * 2 < rpStkPredict) ? ULONG_MAX : rpStkPredict * 2; - if (oldStkPredict < highStkPredict) - goto ALL_DONE; - - if (rpStkPredict < rpPasses * 8) - goto ALL_DONE; - - if (rpPasses >= (rpPassesMax - 1)) - goto ALL_DONE; - } - - EXTRA_PASS: - /* We will do another pass */; + // We should have a TYP_BYREF or TYP_I_IMPL arg and not a TYP_STRUCT arg + noway_assert(argDsc->lvType == TYP_BYREF || argDsc->lvType == TYP_I_IMPL); + // We should have recorded the variable number for the return buffer arg + noway_assert(info.compRetBuffArg != BAD_VAR_NUM); } - -#ifdef DEBUG - if (JitConfig.JitAssertOnMaxRAPasses()) + else // we have a regular arg { - noway_assert(rpPasses < rpPassesMax && - "This may not a bug, but dev team should look and see what is happening"); + noway_assert(inArgMask & RBM_ARG_REGS); } -#endif + } - // The "64" here had been "VARSET_SZ". It is unclear why this number is connected with - // the (max) size of a VARSET. We've eliminated this constant, so I left this as a constant. We hope - // that we're phasing out this code, anyway, and this leaves the behavior the way that it was. - if (rpPasses > (rpPassesMax - rpPassesPessimize) + 64) - { - NO_WAY("we seem to be stuck in an infinite loop. breaking out"); - } + regState->rsCalleeRegArgMaskLiveIn |= inArgMask; -#ifdef DEBUG - if (verbose) +#ifdef _TARGET_ARM_ + if (argDsc->lvType == TYP_DOUBLE) + { + if (info.compIsVarArgs || opts.compUseSoftFP) { - if (rpPasses > 0) - { - if (rpLostEnreg) - printf("\n; Another pass due to rpLostEnreg"); - if (rpAddedVarIntf) - printf("\n; Another pass due to rpAddedVarIntf"); - if ((rpPasses == 1) && rpPredictAssignAgain) - printf("\n; Another pass due to rpPredictAssignAgain"); - } - printf("\n; Register predicting pass# %d\n", rpPasses + 1); + assert((inArgReg == REG_R0) || (inArgReg == REG_R2)); + assert(!regState->rsIsFloat); } -#endif - - /* Zero the variable/register interference graph */ - for (unsigned i = 0; i < REG_COUNT; i++) + else { - VarSetOps::ClearD(this, raLclRegIntf[i]); + assert(regState->rsIsFloat); + assert(emitter::isDoubleReg(inArgReg)); } + regState->rsCalleeRegArgMaskLiveIn |= genRegMask((regNumber)(inArgReg + 1)); + } + else if (argDsc->lvType == TYP_LONG) + { + assert((inArgReg == REG_R0) || (inArgReg == REG_R2)); + assert(!regState->rsIsFloat); + regState->rsCalleeRegArgMaskLiveIn |= genRegMask((regNumber)(inArgReg + 1)); + } +#endif // _TARGET_ARM_ - // if there are PInvoke calls and compLvFrameListRoot is enregistered, - // it must not be in a register trashed by the callee - if (info.compLvFrameListRoot != BAD_VAR_NUM) +#if FEATURE_MULTIREG_ARGS + if (varTypeIsStruct(argDsc->lvType)) + { + if (argDsc->lvIsHfaRegArg()) { - assert(!opts.ShouldUsePInvokeHelpers()); - noway_assert(info.compLvFrameListRoot < lvaCount); - - LclVarDsc* pinvokeVarDsc = &lvaTable[info.compLvFrameListRoot]; - - if (pinvokeVarDsc->lvTracked) - { - rpRecordRegIntf(RBM_CALLEE_TRASH, VarSetOps::MakeSingleton(this, pinvokeVarDsc->lvVarIndex) - DEBUGARG("compLvFrameListRoot")); - - // We would prefer to have this be enregister in the PINVOKE_TCB register - pinvokeVarDsc->addPrefReg(RBM_PINVOKE_TCB, this); - } - - // If we're using a single return block, the p/invoke epilog code trashes ESI and EDI (in the - // worst case). Make sure that the return value compiler temp that we create for the single - // return block knows about this interference. - if (genReturnLocal != BAD_VAR_NUM) + assert(regState->rsIsFloat); + unsigned cSlots = GetHfaCount(argDsc->lvVerTypeInfo.GetClassHandleForValueClass()); + for (unsigned i = 1; i < cSlots; i++) { - noway_assert(genReturnBB); - LclVarDsc* localTmp = &lvaTable[genReturnLocal]; - if (localTmp->lvTracked) - { - rpRecordRegIntf(RBM_PINVOKE_TCB | RBM_PINVOKE_FRAME, - VarSetOps::MakeSingleton(this, localTmp->lvVarIndex) DEBUGARG("genReturnLocal")); - } + assert(inArgReg + i <= LAST_FP_ARGREG); + regState->rsCalleeRegArgMaskLiveIn |= genRegMask(static_cast(inArgReg + i)); } } - -#ifdef _TARGET_ARM_ - if (compFloatingPointUsed) + else { - bool hasMustInitFloat = false; - - // if we have any must-init floating point LclVars then we will add register interferences - // for the arguments with RBM_SCRATCH - // this is so that if we need to reset the initReg to REG_SCRATCH in Compiler::genFnProlog() - // we won't home the arguments into REG_SCRATCH - - unsigned lclNum; - LclVarDsc* varDsc; - - for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++) + unsigned cSlots = argDsc->lvSize() / TARGET_POINTER_SIZE; + for (unsigned i = 1; i < cSlots; i++) { - if (varDsc->lvMustInit && varTypeIsFloating(varDsc->TypeGet())) + regNumber nextArgReg = (regNumber)(inArgReg + i); + if (nextArgReg > REG_ARG_LAST) { - hasMustInitFloat = true; break; } + assert(regState->rsIsFloat == false); + regState->rsCalleeRegArgMaskLiveIn |= genRegMask(nextArgReg); } - - if (hasMustInitFloat) - { - for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++) - { - // If is an incoming argument, that is tracked and not floating-point - if (varDsc->lvIsParam && varDsc->lvTracked && !varTypeIsFloating(varDsc->TypeGet())) - { - rpRecordRegIntf(RBM_SCRATCH, VarSetOps::MakeSingleton(this, varDsc->lvVarIndex) - DEBUGARG("arg home with must-init fp")); - } - } - } - } -#endif - - stmtNum = 0; - rpAddedVarIntf = false; - rpLostEnreg = false; - - /* Walk the basic blocks and predict reg use for each tree */ - - for (BasicBlock* block = fgFirstBB; block != NULL; block = block->bbNext) - { - GenTree* stmt; - compCurBB = block; - compCurLifeTree = NULL; - VarSetOps::Assign(this, compCurLife, block->bbLiveIn); - - compCurBB = block; - - for (stmt = block->FirstNonPhiDef(); stmt != NULL; stmt = stmt->gtNext) - { - noway_assert(stmt->gtOper == GT_STMT); - - rpPredictSpillCnt = 0; - VarSetOps::AssignNoCopy(this, rpLastUseVars, VarSetOps::MakeEmpty(this)); - VarSetOps::AssignNoCopy(this, rpUseInPlace, VarSetOps::MakeEmpty(this)); - - GenTree* tree = stmt->gtStmt.gtStmtExpr; - stmtNum++; -#ifdef DEBUG - if (verbose && 1) - { - printf("\nRegister predicting BB%02u, stmt %d\n", block->bbNum, stmtNum); - gtDispTree(tree); - printf("\n"); - } -#endif - rpPredictTreeRegUse(tree, PREDICT_NONE, RBM_NONE, RBM_NONE); - - noway_assert(rpAsgVarNum == -1); - - if (rpPredictSpillCnt > tmpIntSpillMax) - tmpIntSpillMax = rpPredictSpillCnt; - } - } - rpPasses++; - - /* Decide whether we need to set mustPredict */ - mustPredict = false; - -#ifdef _TARGET_ARM_ - // The spill count may be now high enough that we now need to reserve r10. If this is the case, we'll need to - // reserve r10, and if it was used, throw out the last prediction and repredict. - if (((codeGen->regSet.rsMaskResvd & RBM_OPT_RSVD) == 0) && compRsvdRegCheck(REGALLOC_FRAME_LAYOUT)) - { - codeGen->regSet.rsMaskResvd |= RBM_OPT_RSVD; - allAcceptableRegs &= ~RBM_OPT_RSVD; - if ((regUsed & RBM_OPT_RSVD) != 0) - { - mustPredict = true; - rpBestRecordedPrediction = nullptr; - } - } -#endif - - if (rpAddedVarIntf) - { - mustPredict = true; -#ifdef DEBUG - if (verbose) - raDumpVarIntf(); -#endif } + } +#endif // FEATURE_MULTIREG_ARGS - if (rpPasses == 1) - { - if ((opts.compFlags & CLFLG_REGVAR) == 0) - goto ALL_DONE; + return inArgReg; +} - if (rpPredictAssignAgain) - mustPredict = true; +/****************************************************************************/ +/* Returns true when we must create an EBP frame + This is used to force most managed methods to have EBP based frames + which allows the ETW kernel stackwalker to walk the stacks of managed code + this allows the kernel to perform light weight profiling + */ +bool Compiler::rpMustCreateEBPFrame(INDEBUG(const char** wbReason)) +{ + bool result = false; #ifdef DEBUG - if (fJitNoRegLoc) - goto ALL_DONE; + const char* reason = nullptr; #endif - } - - /* Calculate the new value to use for regAvail */ - - regAvail = allAcceptableRegs; - - /* If a frame pointer is required then we remove EBP */ - if (codeGen->isFramePointerRequired() || codeGen->isFrameRequired()) - regAvail &= ~RBM_FPBASE; #if ETW_EBP_FRAMED - // We never have EBP available when ETW_EBP_FRAME is defined - regAvail &= ~RBM_FPBASE; -#endif - - // If we have done n-passes then we must continue to pessimize the - // interference graph by or-ing the interferences from the previous pass - - if (rpPasses > rpPassesPessimize) - { - for (unsigned regInx = 0; regInx < REG_COUNT; regInx++) - VarSetOps::UnionD(this, raLclRegIntf[regInx], oldLclRegIntf[regInx]); - - /* If we reverse an EBP enregistration then keep it that way */ - if (rpReverseEBPenreg) - regAvail &= ~RBM_FPBASE; - } - -#ifdef DEBUG - if (verbose) - raDumpRegIntf(); -#endif - - /* Save the old variable/register interference graph */ - for (unsigned i = 0; i < REG_COUNT; i++) - { - VarSetOps::Assign(this, oldLclRegIntf[i], raLclRegIntf[i]); - } - oldStkPredict = rpStkPredict; - } // end of while (true) - -ALL_DONE:; - - // If we recorded a better feasible allocation than we ended up with, go back to using it. - rpUseRecordedPredictionIfBetter(); - -#if DOUBLE_ALIGN - codeGen->setDoubleAlign(false); -#endif - - switch (rpFrameType) + if (!result && (opts.MinOpts() || opts.compDbgCode)) { - default: - noway_assert(!"rpFrameType not set correctly!"); - break; - case FT_ESP_FRAME: - noway_assert(!codeGen->isFramePointerRequired()); - noway_assert(!codeGen->isFrameRequired()); - codeGen->setFramePointerUsed(false); - break; - case FT_EBP_FRAME: - noway_assert((regUsed & RBM_FPBASE) == 0); - codeGen->setFramePointerUsed(true); - break; -#if DOUBLE_ALIGN - case FT_DOUBLE_ALIGN_FRAME: - noway_assert((regUsed & RBM_FPBASE) == 0); - noway_assert(!codeGen->isFramePointerRequired()); - codeGen->setFramePointerUsed(false); - codeGen->setDoubleAlign(true); - break; -#endif + INDEBUG(reason = "Debug Code"); + result = true; } - - /* Record the set of registers that we need */ - codeGen->regSet.rsClearRegsModified(); - if (regUsed != RBM_NONE) + if (!result && (info.compMethodInfo->ILCodeSize > DEFAULT_MAX_INLINE_SIZE)) + { + INDEBUG(reason = "IL Code Size"); + result = true; + } + if (!result && (fgBBcount > 3)) + { + INDEBUG(reason = "BasicBlock Count"); + result = true; + } + if (!result && fgHasLoops) + { + INDEBUG(reason = "Method has Loops"); + result = true; + } + if (!result && (optCallCount >= 2)) + { + INDEBUG(reason = "Call Count"); + result = true; + } + if (!result && (optIndirectCallCount >= 1)) { - codeGen->regSet.rsSetRegsModified(regUsed); + INDEBUG(reason = "Indirect Call"); + result = true; } +#endif // ETW_EBP_FRAMED - /* We need genFullPtrRegMap if : - * The method is fully interruptible, or - * We are generating an EBP-less frame (for stack-pointer deltas) - */ + // VM wants to identify the containing frame of an InlinedCallFrame always + // via the frame register never the stack register so we need a frame. + if (!result && (optNativeCallCount != 0)) + { + INDEBUG(reason = "Uses PInvoke"); + result = true; + } - genFullPtrRegMap = (genInterruptible || !codeGen->isFramePointerUsed()); +#ifdef _TARGET_ARM64_ + // TODO-ARM64-NYI: This is temporary: force a frame pointer-based frame until genFnProlog can handle non-frame + // pointer frames. + if (!result) + { + INDEBUG(reason = "Temporary ARM64 force frame pointer"); + result = true; + } +#endif // _TARGET_ARM64_ - raMarkStkVars(); #ifdef DEBUG - if (verbose) + if ((result == true) && (wbReason != nullptr)) { - printf("# rpPasses was %u for %s\n", rpPasses, info.compFullName); - printf(" rpStkPredict was %u\n", rpStkPredict); + *wbReason = reason; } #endif - rpRegAllocDone = true; -} -#endif // LEGACY_BACKEND + return result; +} /***************************************************************************** * @@ -6619,12 +280,7 @@ void Compiler::raMarkStkVars() for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++) { - // For RyuJIT, lvOnFrame is set by LSRA, except in the case of zero-ref, which is set below. - CLANG_FORMAT_COMMENT_ANCHOR; - -#ifdef LEGACY_BACKEND - varDsc->lvOnFrame = false; -#endif // LEGACY_BACKEND + // lvOnFrame is set by LSRA, except in the case of zero-ref, which is set below. if (lvaIsFieldOfDependentlyPromotedStruct(varDsc)) { @@ -6636,17 +292,7 @@ void Compiler::raMarkStkVars() if (varDsc->lvRegister) { - if (!isRegPairType(varDsc->TypeGet())) - { - goto NOT_STK; - } - - /* For "large" variables make sure both halves are enregistered */ - - if (varDsc->lvRegNum != REG_STK && varDsc->lvOtherReg != REG_STK) - { - goto NOT_STK; - } + goto NOT_STK; } /* Unused variables typically don't get any frame space */ else if (varDsc->lvRefCnt == 0) @@ -6707,9 +353,7 @@ void Compiler::raMarkStkVars() } } -#ifndef LEGACY_BACKEND varDsc->lvOnFrame = needSlot; -#endif // !LEGACY_BACKEND if (!needSlot) { /* Clear the lvMustInit flag in case it is set */ @@ -6719,12 +363,10 @@ void Compiler::raMarkStkVars() } } -#ifndef LEGACY_BACKEND if (!varDsc->lvOnFrame) { goto NOT_STK; } -#endif // !LEGACY_BACKEND ON_STK: /* The variable (or part of it) lives on the stack frame */ @@ -6764,15 +406,8 @@ void Compiler::raMarkStkVars() noway_assert(varDsc->lvIsInReg() || varDsc->lvOnFrame || varDsc->lvRefCnt == 0); -#ifndef LEGACY_BACKEND - // We can't have both lvRegister and lvOnFrame for RyuJIT + // We can't have both lvRegister and lvOnFrame noway_assert(!varDsc->lvRegister || !varDsc->lvOnFrame); -#else // LEGACY_BACKEND - - /* If both lvRegister and lvOnFrame are set, it must be partially enregistered */ - noway_assert(!varDsc->lvRegister || !varDsc->lvOnFrame || - (varDsc->lvType == TYP_LONG && varDsc->lvOtherReg == REG_STK)); -#endif // LEGACY_BACKEND #ifdef DEBUG @@ -6795,43 +430,3 @@ void Compiler::raMarkStkVars() #endif } } - -#ifdef LEGACY_BACKEND -void Compiler::rpRecordPrediction() -{ - if (rpBestRecordedPrediction == NULL || rpStkPredict < rpBestRecordedStkPredict) - { - if (rpBestRecordedPrediction == NULL) - { - rpBestRecordedPrediction = - reinterpret_cast(compGetMemArray(lvaCount, sizeof(VarRegPrediction))); - } - for (unsigned k = 0; k < lvaCount; k++) - { - rpBestRecordedPrediction[k].m_isEnregistered = lvaTable[k].lvRegister; - rpBestRecordedPrediction[k].m_regNum = (regNumberSmall)lvaTable[k].GetRegNum(); - rpBestRecordedPrediction[k].m_otherReg = (regNumberSmall)lvaTable[k].GetOtherReg(); - } - rpBestRecordedStkPredict = rpStkPredict; - JITDUMP("Recorded a feasible reg prediction with weighted stack use count %d.\n", rpBestRecordedStkPredict); - } -} - -void Compiler::rpUseRecordedPredictionIfBetter() -{ - JITDUMP("rpStkPredict is %d; previous feasible reg prediction is %d.\n", rpStkPredict, - rpBestRecordedPrediction != NULL ? rpBestRecordedStkPredict : 0); - if (rpBestRecordedPrediction != NULL && rpStkPredict > rpBestRecordedStkPredict) - { - JITDUMP("Reverting to a previously-recorded feasible reg prediction with weighted stack use count %d.\n", - rpBestRecordedStkPredict); - - for (unsigned k = 0; k < lvaCount; k++) - { - lvaTable[k].lvRegister = rpBestRecordedPrediction[k].m_isEnregistered; - lvaTable[k].SetRegNum(static_cast(rpBestRecordedPrediction[k].m_regNum)); - lvaTable[k].SetOtherReg(static_cast(rpBestRecordedPrediction[k].m_otherReg)); - } - } -} -#endif // LEGACY_BACKEND diff --git a/src/jit/regalloc.h b/src/jit/regalloc.h index 5054b4568e..117e62f84c 100644 --- a/src/jit/regalloc.h +++ b/src/jit/regalloc.h @@ -5,8 +5,6 @@ #ifndef REGALLOC_H_ #define REGALLOC_H_ -// Some things that are used by both LSRA and regpredict allocators. - enum FrameType { FT_NOT_SET, @@ -29,83 +27,4 @@ enum CanDoubleAlign }; #endif -#ifdef LEGACY_BACKEND - -#include "varset.h" - -/*****************************************************************************/ -/*****************************************************************************/ - -// This enumeration specifies register restrictions for the predictor -enum rpPredictReg -{ - PREDICT_NONE, // any subtree - PREDICT_ADDR, // subtree is left side of an assignment - PREDICT_REG, // subtree must be any register - PREDICT_SCRATCH_REG, // subtree must be any writable register - -#if defined(_TARGET_ARM_) - PREDICT_PAIR_R0R1, // subtree will write R0 and R1 - PREDICT_PAIR_R2R3, // subtree will write R2 and R3 - -#elif defined(_TARGET_AMD64_) - - PREDICT_NOT_REG_EAX, // subtree must be any writable register, except EAX - PREDICT_NOT_REG_ECX, // subtree must be any writable register, except ECX - -#elif defined(_TARGET_X86_) - - PREDICT_NOT_REG_EAX, // subtree must be any writable register, except EAX - PREDICT_NOT_REG_ECX, // subtree must be any writable register, except ECX - - PREDICT_PAIR_EAXEDX, // subtree will write EAX and EDX - PREDICT_PAIR_ECXEBX, // subtree will write ECX and EBX - -#else -#error "Unknown Target!" -#endif // _TARGET_ - -#define REGDEF(name, rnum, mask, sname) PREDICT_REG_##name, -#include "register.h" - - // The following are use whenever we have a ASG node into a LCL_VAR that - // we predict to be enregistered. This flags indicates that we can expect - // to use the register that is being assigned into as the temporary to - // compute the right side of the ASGN node. - - PREDICT_REG_VAR_T00, // write the register used by tracked varable 00 - PREDICT_REG_VAR_MAX = PREDICT_REG_VAR_T00 + lclMAX_TRACKED - 1, - - PREDICT_COUNT = PREDICT_REG_VAR_T00, - -#define REGDEF(name, rnum, mask, sname) -#define REGALIAS(alias, realname) PREDICT_REG_##alias = PREDICT_REG_##realname, -#include "register.h" - -#if defined(_TARGET_ARM_) - - PREDICT_REG_FIRST = PREDICT_REG_R0, - PREDICT_INTRET = PREDICT_REG_R0, - PREDICT_LNGRET = PREDICT_PAIR_R0R1, - PREDICT_FLTRET = PREDICT_REG_F0, - -#elif defined(_TARGET_AMD64_) - - PREDICT_REG_FIRST = PREDICT_REG_RAX, - PREDICT_INTRET = PREDICT_REG_EAX, - PREDICT_LNGRET = PREDICT_REG_RAX, - -#elif defined(_TARGET_X86_) - - PREDICT_REG_FIRST = PREDICT_REG_EAX, - PREDICT_INTRET = PREDICT_REG_EAX, - PREDICT_LNGRET = PREDICT_PAIR_EAXEDX, - -#else -#error "Unknown _TARGET_" -#endif // _TARGET_ - -}; -#endif // LEGACY_BACKEND - #endif // REGALLOC_H_ diff --git a/src/jit/register.h b/src/jit/register.h index 9e351037fd..29f64de7ef 100644 --- a/src/jit/register.h +++ b/src/jit/register.h @@ -67,12 +67,6 @@ REGALIAS(EDI, RDI) #endif // !defined(_TARGET_X86_) -#ifdef LEGACY_BACKEND - -REGDEF(STK, 8, 0x00, "STK" ) - -#else // !LEGACY_BACKEND - #ifdef _TARGET_AMD64_ #define XMMBASE 16 #define XMMMASK(x) (__int64(1) << (x+XMMBASE)) @@ -104,8 +98,6 @@ REGDEF(XMM15, 15+XMMBASE, XMMMASK(15), "mm15" ) REGDEF(STK, 16+XMMBASE, 0x0000, "STK" ) #endif // !_TARGET_X86_ -#endif // !LEGACY_BACKEND - #elif defined(_TARGET_ARM_) #include "registerarm.h" diff --git a/src/jit/registerfp.cpp b/src/jit/registerfp.cpp deleted file mode 100644 index b2d0a6ee83..0000000000 --- a/src/jit/registerfp.cpp +++ /dev/null @@ -1,1515 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#include "jitpch.h" -#ifdef _MSC_VER -#pragma hdrstop -#endif - -#ifdef LEGACY_BACKEND // This file is NOT used for the RyuJIT backend that uses the linear scan register allocator. - -#include "compiler.h" -#include "emit.h" -#include "codegen.h" - -#ifndef _TARGET_ARM_ -#error "Non-ARM target for registerfp.cpp" -#endif // !_TARGET_ARM_ - -// get the next argument register which is aligned to 'alignment' # of bytes -regNumber alignFloatArgReg(regNumber argReg, int alignment) -{ - assert(isValidFloatArgReg(argReg)); - - int regsize_alignment = alignment /= REGSIZE_BYTES; - if (genMapFloatRegNumToRegArgNum(argReg) % regsize_alignment) - argReg = genRegArgNext(argReg); - - // technically the above should be a 'while' so make sure - // we never should have incremented more than once - assert(!(genMapFloatRegNumToRegArgNum(argReg) % regsize_alignment)); - - return argReg; -} - -// Instruction list -// N=normal, R=reverse, P=pop - -void CodeGen::genFloatConst(GenTree* tree, RegSet::RegisterPreference* pref) -{ - assert(tree->gtOper == GT_CNS_DBL); - var_types type = tree->gtType; - double constValue = tree->gtDblCon.gtDconVal; - size_t* cv = (size_t*)&constValue; - - regNumber dst = regSet.PickRegFloat(type, pref); - - if (type == TYP_FLOAT) - { - regNumber reg = regSet.rsPickReg(); - - float f = forceCastToFloat(constValue); - genSetRegToIcon(reg, *((int*)(&f))); - getEmitter()->emitIns_R_R(INS_vmov_i2f, EA_4BYTE, dst, reg); - } - else - { - assert(type == TYP_DOUBLE); - regNumber reg1 = regSet.rsPickReg(); - regNumber reg2 = regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(reg1)); - - genSetRegToIcon(reg1, cv[0]); - regSet.rsLockReg(genRegMask(reg1)); - genSetRegToIcon(reg2, cv[1]); - regSet.rsUnlockReg(genRegMask(reg1)); - - getEmitter()->emitIns_R_R_R(INS_vmov_i2d, EA_8BYTE, dst, reg1, reg2); - } - genMarkTreeInReg(tree, dst); - - return; -} - -void CodeGen::genFloatMath(GenTree* tree, RegSet::RegisterPreference* pref) -{ - assert(tree->OperGet() == GT_INTRINSIC); - - GenTree* op1 = tree->gtOp.gtOp1; - - // get tree into a register - genCodeForTreeFloat(op1, pref); - - instruction ins; - - switch (tree->gtIntrinsic.gtIntrinsicId) - { - case CORINFO_INTRINSIC_Sin: - ins = INS_invalid; - break; - case CORINFO_INTRINSIC_Cos: - ins = INS_invalid; - break; - case CORINFO_INTRINSIC_Sqrt: - ins = INS_vsqrt; - break; - case CORINFO_INTRINSIC_Abs: - ins = INS_vabs; - break; - case CORINFO_INTRINSIC_Round: - { - regNumber reg = regSet.PickRegFloat(tree->TypeGet(), pref); - genMarkTreeInReg(tree, reg); - // convert it to a long and back - inst_RV_RV(ins_FloatConv(TYP_LONG, tree->TypeGet()), reg, op1->gtRegNum, tree->TypeGet()); - inst_RV_RV(ins_FloatConv(tree->TypeGet(), TYP_LONG), reg, reg); - genCodeForTreeFloat_DONE(tree, op1->gtRegNum); - return; - } - break; - default: - unreached(); - } - - if (ins != INS_invalid) - { - regNumber reg = regSet.PickRegFloat(tree->TypeGet(), pref); - genMarkTreeInReg(tree, reg); - inst_RV_RV(ins, reg, op1->gtRegNum, tree->TypeGet()); - // mark register that holds tree - genCodeForTreeFloat_DONE(tree, reg); - } - else - { - unreached(); - // If unreached is removed, mark register that holds tree - // genCodeForTreeFloat_DONE(tree, op1->gtRegNum); - } - - return; -} - -void CodeGen::genFloatSimple(GenTree* tree, RegSet::RegisterPreference* pref) -{ - assert(tree->OperKind() & GTK_SMPOP); - var_types type = tree->TypeGet(); - - RegSet::RegisterPreference defaultPref(RBM_ALLFLOAT, RBM_NONE); - if (pref == NULL) - { - pref = &defaultPref; - } - - switch (tree->OperGet()) - { - // Assignment - case GT_ASG: - { - genFloatAssign(tree); - break; - } - - // Arithmetic binops - case GT_ADD: - case GT_SUB: - case GT_MUL: - case GT_DIV: - { - genFloatArith(tree, pref); - break; - } - - case GT_NEG: - { - GenTree* op1 = tree->gtOp.gtOp1; - - // get the tree into a register - genCodeForTreeFloat(op1, pref); - - // change the sign - regNumber reg = regSet.PickRegFloat(type, pref); - genMarkTreeInReg(tree, reg); - inst_RV_RV(ins_MathOp(tree->OperGet(), type), reg, op1->gtRegNum, type); - - // mark register that holds tree - genCodeForTreeFloat_DONE(tree, reg); - return; - } - - case GT_IND: - { - regMaskTP addrReg; - - // Make sure the address value is 'addressable' */ - addrReg = genMakeAddressable(tree, 0, RegSet::FREE_REG); - - // Load the value onto the FP stack - regNumber reg = regSet.PickRegFloat(type, pref); - genLoadFloat(tree, reg); - - genDoneAddressable(tree, addrReg, RegSet::FREE_REG); - - genCodeForTreeFloat_DONE(tree, reg); - - break; - } - case GT_CAST: - { - genCodeForTreeCastFloat(tree, pref); - break; - } - - // Asg-Arithmetic ops - case GT_ASG_ADD: - case GT_ASG_SUB: - case GT_ASG_MUL: - case GT_ASG_DIV: - { - genFloatAsgArith(tree); - break; - } - case GT_INTRINSIC: - genFloatMath(tree, pref); - break; - - case GT_RETURN: - { - GenTree* op1 = tree->gtOp.gtOp1; - assert(op1); - - pref->best = (type == TYP_DOUBLE) ? RBM_DOUBLERET : RBM_FLOATRET; - - // Compute the result - genCodeForTreeFloat(op1, pref); - - inst_RV_TT(ins_FloatConv(tree->TypeGet(), op1->TypeGet()), REG_FLOATRET, op1); - if (compiler->info.compIsVarArgs || compiler->opts.compUseSoftFP) - { - if (tree->TypeGet() == TYP_FLOAT) - { - inst_RV_RV(INS_vmov_f2i, REG_INTRET, REG_FLOATRET, TYP_FLOAT, EA_4BYTE); - } - else - { - assert(tree->TypeGet() == TYP_DOUBLE); - inst_RV_RV_RV(INS_vmov_d2i, REG_INTRET, REG_NEXT(REG_INTRET), REG_FLOATRET, EA_8BYTE); - } - } - break; - } - case GT_ARGPLACE: - break; - - case GT_COMMA: - { - GenTree* op1 = tree->gtOp.gtOp1; - GenTree* op2 = tree->gtGetOp2IfPresent(); - - if (tree->gtFlags & GTF_REVERSE_OPS) - { - genCodeForTreeFloat(op2, pref); - - regSet.SetUsedRegFloat(op2, true); - genEvalSideEffects(op1); - regSet.SetUsedRegFloat(op2, false); - } - else - { - genEvalSideEffects(op1); - genCodeForTreeFloat(op2, pref); - } - - genCodeForTreeFloat_DONE(tree, op2->gtRegNum); - break; - } - - case GT_CKFINITE: - genFloatCheckFinite(tree, pref); - break; - - default: - NYI("Unhandled register FP codegen"); - } -} - -// generate code for ckfinite tree/instruction -void CodeGen::genFloatCheckFinite(GenTree* tree, RegSet::RegisterPreference* pref) -{ - TempDsc* temp; - int offs; - - GenTree* op1 = tree->gtOp.gtOp1; - - // Offset of the DWord containing the exponent - offs = (op1->gtType == TYP_FLOAT) ? 0 : sizeof(int); - - // get tree into a register - genCodeForTreeFloat(op1, pref); - - regNumber reg = regSet.rsPickReg(); - - int expMask; - if (op1->gtType == TYP_FLOAT) - { - getEmitter()->emitIns_R_R(INS_vmov_f2i, EA_4BYTE, reg, op1->gtRegNum); - expMask = 0x7F800000; - } - else // double - { - assert(op1->gtType == TYP_DOUBLE); - getEmitter()->emitIns_R_R(INS_vmov_f2i, EA_4BYTE, reg, - REG_NEXT(op1->gtRegNum)); // the high 32 bits of the double register - expMask = 0x7FF00000; - } - regTracker.rsTrackRegTrash(reg); - - // Check if the exponent is all ones - inst_RV_IV(INS_and, reg, expMask, EA_4BYTE); - inst_RV_IV(INS_cmp, reg, expMask, EA_4BYTE); - - // If exponent was all 1's, we need to throw ArithExcep - emitJumpKind jmpEqual = genJumpKindForOper(GT_EQ, CK_SIGNED); - genJumpToThrowHlpBlk(jmpEqual, SCK_ARITH_EXCPN); - - genCodeForTreeFloat_DONE(tree, op1->gtRegNum); -} - -void CodeGen::genFloatAssign(GenTree* tree) -{ - var_types type = tree->TypeGet(); - GenTree* op1 = tree->gtGetOp1(); - GenTree* op2 = tree->gtGetOp2IfPresent(); - - regMaskTP needRegOp1 = RBM_ALLINT; - regMaskTP addrReg = RBM_NONE; - bool volat = false; // Is this a volatile store - bool unaligned = false; // Is this an unaligned store - regNumber op2reg = REG_NA; - - unsigned lclVarNum = compiler->lvaCount; - unsigned lclILoffs = DUMMY_INIT(0); - - noway_assert(tree->OperGet() == GT_ASG); - - // Is the target a floating-point local variable? - // possibly even an enregistered floating-point local variable? - // - switch (op1->gtOper) - { - unsigned varNum; - LclVarDsc* varDsc; - - case GT_LCL_FLD: - // Check for a misalignment on a Floating Point field - // - if (varTypeIsFloating(op1->TypeGet())) - { - if ((op1->gtLclFld.gtLclOffs % emitTypeSize(op1->TypeGet())) != 0) - { - unaligned = true; - } - } - break; - - case GT_LCL_VAR: - varNum = op1->gtLclVarCommon.gtLclNum; - noway_assert(varNum < compiler->lvaCount); - varDsc = compiler->lvaTable + varNum; - - // For non-debuggable code, every definition of a lcl-var has - // to be checked to see if we need to open a new scope for it. - // Remember the local var info to call siCheckVarScope - // AFTER code generation of the assignment. - // - if (compiler->opts.compScopeInfo && !compiler->opts.compDbgCode && (compiler->info.compVarScopesCount > 0)) - { - lclVarNum = varNum; - lclILoffs = op1->gtLclVar.gtLclILoffs; - } - - // Dead Store assert (with min opts we may have dead stores) - // - noway_assert(!varDsc->lvTracked || compiler->opts.MinOpts() || !(op1->gtFlags & GTF_VAR_DEATH)); - - // Does this variable live in a register? - // - if (genMarkLclVar(op1)) - { - noway_assert(!compiler->opts.compDbgCode); // We don't enregister any floats with debug codegen - - // Get hold of the target register - // - regNumber op1Reg = op1->gtRegVar.gtRegNum; - - // the variable being assigned should be dead in op2 - assert(!varDsc->lvTracked || - !VarSetOps::IsMember(compiler, genUpdateLiveSetForward(op2), varDsc->lvVarIndex)); - - // Setup register preferencing, so that we try to target the op1 enregistered variable - // - regMaskTP bestMask = genRegMask(op1Reg); - if (type == TYP_DOUBLE) - { - assert((bestMask & RBM_DBL_REGS) != 0); - bestMask |= genRegMask(REG_NEXT(op1Reg)); - } - RegSet::RegisterPreference pref(RBM_ALLFLOAT, bestMask); - - // Evaluate op2 into a floating point register - // - genCodeForTreeFloat(op2, &pref); - - noway_assert(op2->InReg()); - - // Make sure the value ends up in the right place ... - // For example if op2 is a call that returns a result - // in REG_F0, we will need to do a move instruction here - // - if ((op2->gtRegNum != op1Reg) || (op2->TypeGet() != type)) - { - regMaskTP spillRegs = regSet.rsMaskUsed & genRegMaskFloat(op1Reg, op1->TypeGet()); - if (spillRegs != 0) - regSet.rsSpillRegs(spillRegs); - - assert(type == op1->TypeGet()); - - inst_RV_RV(ins_FloatConv(type, op2->TypeGet()), op1Reg, op2->gtRegNum, type); - } - genUpdateLife(op1); - goto DONE_ASG; - } - break; - - case GT_CLS_VAR: - case GT_IND: - // Check for a volatile/unaligned store - // - assert((op1->OperGet() == GT_CLS_VAR) || - (op1->OperGet() == GT_IND)); // Required for GTF_IND_VOLATILE flag to be valid - if (op1->gtFlags & GTF_IND_VOLATILE) - volat = true; - if (op1->gtFlags & GTF_IND_UNALIGNED) - unaligned = true; - break; - - default: - break; - } - - // Is the value being assigned an enregistered floating-point local variable? - // - switch (op2->gtOper) - { - case GT_LCL_VAR: - - if (!genMarkLclVar(op2)) - break; - - __fallthrough; - - case GT_REG_VAR: - - // We must honor the order evalauation in case op1 reassigns our op2 register - // - if (tree->gtFlags & GTF_REVERSE_OPS) - break; - - // Is there an implicit conversion that we have to insert? - // Handle this case with the normal cases below. - // - if (type != op2->TypeGet()) - break; - - // Make the target addressable - // - addrReg = genMakeAddressable(op1, needRegOp1, RegSet::KEEP_REG, true); - - noway_assert(op2->InReg()); - noway_assert(op2->IsRegVar()); - - op2reg = op2->gtRegVar.gtRegNum; - genUpdateLife(op2); - - goto CHK_VOLAT_UNALIGN; - default: - break; - } - - // Is the op2 (RHS) more complex than op1 (LHS)? - // - if (tree->gtFlags & GTF_REVERSE_OPS) - { - regMaskTP bestRegs = regSet.rsNarrowHint(RBM_ALLFLOAT, ~op1->gtRsvdRegs); - RegSet::RegisterPreference pref(RBM_ALLFLOAT, bestRegs); - - // Generate op2 (RHS) into a floating point register - // - genCodeForTreeFloat(op2, &pref); - regSet.SetUsedRegFloat(op2, true); - - // Make the target addressable - // - addrReg = genMakeAddressable(op1, needRegOp1, RegSet::KEEP_REG, true); - - genRecoverReg(op2, RBM_ALLFLOAT, RegSet::KEEP_REG); - noway_assert(op2->InReg()); - regSet.SetUsedRegFloat(op2, false); - } - else - { - needRegOp1 = regSet.rsNarrowHint(needRegOp1, ~op2->gtRsvdRegs); - - // Make the target addressable - // - addrReg = genMakeAddressable(op1, needRegOp1, RegSet::KEEP_REG, true); - - // Generate the RHS into any floating point register - genCodeForTreeFloat(op2); - } - noway_assert(op2->InReg()); - - op2reg = op2->gtRegNum; - - // Is there an implicit conversion that we have to insert? - // - if (type != op2->TypeGet()) - { - regMaskTP bestMask = genRegMask(op2reg); - if (type == TYP_DOUBLE) - { - if (bestMask & RBM_DBL_REGS) - { - bestMask |= genRegMask(REG_NEXT(op2reg)); - } - else - { - bestMask |= genRegMask(REG_PREV(op2reg)); - } - } - RegSet::RegisterPreference op2Pref(RBM_ALLFLOAT, bestMask); - op2reg = regSet.PickRegFloat(type, &op2Pref); - - inst_RV_RV(ins_FloatConv(type, op2->TypeGet()), op2reg, op2->gtRegNum, type); - } - - // Make sure the LHS is still addressable - // - addrReg = genKeepAddressable(op1, addrReg); - -CHK_VOLAT_UNALIGN: - - regSet.rsLockUsedReg(addrReg); // Must prevent unaligned regSet.rsGrabReg from choosing an addrReg - - if (volat) - { - // Emit a memory barrier instruction before the store - instGen_MemoryBarrier(); - } - if (unaligned) - { - var_types storeType = op1->TypeGet(); - assert(storeType == TYP_DOUBLE || storeType == TYP_FLOAT); - - // Unaligned Floating-Point Stores must be done using the integer register(s) - regNumber intRegLo = regSet.rsGrabReg(RBM_ALLINT); - regNumber intRegHi = REG_NA; - regMaskTP tmpLockMask = genRegMask(intRegLo); - - if (storeType == TYP_DOUBLE) - { - intRegHi = regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(intRegLo)); - tmpLockMask |= genRegMask(intRegHi); - } - - // move the FP register over to the integer register(s) - // - if (storeType == TYP_DOUBLE) - { - getEmitter()->emitIns_R_R_R(INS_vmov_d2i, EA_8BYTE, intRegLo, intRegHi, op2reg); - regTracker.rsTrackRegTrash(intRegHi); - } - else - { - getEmitter()->emitIns_R_R(INS_vmov_f2i, EA_4BYTE, intRegLo, op2reg); - } - regTracker.rsTrackRegTrash(intRegLo); - - regSet.rsLockReg(tmpLockMask); // Temporarily lock the intRegs - op1->gtType = TYP_INT; // Temporarily change the type to TYP_INT - - inst_TT_RV(ins_Store(TYP_INT), op1, intRegLo); - - if (storeType == TYP_DOUBLE) - { - inst_TT_RV(ins_Store(TYP_INT), op1, intRegHi, 4); - } - - op1->gtType = storeType; // Change the type back to the floating point type - regSet.rsUnlockReg(tmpLockMask); // Unlock the intRegs - } - else - { - // Move the value into the target - // - inst_TT_RV(ins_Store(op1->TypeGet()), op1, op2reg); - } - - // Free up anything that was tied up by the LHS - // - regSet.rsUnlockUsedReg(addrReg); - genDoneAddressable(op1, addrReg, RegSet::KEEP_REG); - -DONE_ASG: - - genUpdateLife(tree); - - /* For non-debuggable code, every definition of a lcl-var has - * to be checked to see if we need to open a new scope for it. - */ - if (lclVarNum < compiler->lvaCount) - siCheckVarScope(lclVarNum, lclILoffs); -} - -void CodeGen::genCodeForTreeFloat(GenTree* tree, RegSet::RegisterPreference* pref) -{ - genTreeOps oper; - unsigned kind; - - assert(tree); - assert(tree->gtOper != GT_STMT); - - // What kind of node do we have? - oper = tree->OperGet(); - kind = tree->OperKind(); - - if (kind & GTK_CONST) - { - genFloatConst(tree, pref); - } - else if (kind & GTK_LEAF) - { - genFloatLeaf(tree, pref); - } - else if (kind & GTK_SMPOP) - { - genFloatSimple(tree, pref); - } - else - { - assert(oper == GT_CALL); - genCodeForCall(tree->AsCall(), true); - } -} - -void CodeGen::genFloatLeaf(GenTree* tree, RegSet::RegisterPreference* pref) -{ - regNumber reg = REG_NA; - - switch (tree->OperGet()) - { - case GT_LCL_VAR: - // Does the variable live in a register? - // - if (!genMarkLclVar(tree)) - goto MEM_LEAF; - __fallthrough; - - case GT_REG_VAR: - noway_assert(tree->InReg()); - reg = tree->gtRegVar.gtRegNum; - break; - - case GT_LCL_FLD: - // We only use GT_LCL_FLD for lvAddrTaken vars, so we don't have - // to worry about it being enregistered. - noway_assert(compiler->lvaTable[tree->gtLclFld.gtLclNum].lvRegister == 0); - __fallthrough; - - case GT_CLS_VAR: - - MEM_LEAF: - reg = regSet.PickRegFloat(tree->TypeGet(), pref); - genLoadFloat(tree, reg); - break; - - default: - DISPTREE(tree); - assert(!"unexpected leaf"); - } - - genCodeForTreeFloat_DONE(tree, reg); - return; -} - -void CodeGen::genLoadFloat(GenTree* tree, regNumber reg) -{ - if (tree->IsRegVar()) - { - // if it has been spilled, unspill it.% - LclVarDsc* varDsc = &compiler->lvaTable[tree->gtLclVarCommon.gtLclNum]; - if (varDsc->lvSpilled) - { - UnspillFloat(varDsc); - } - - inst_RV_RV(ins_FloatCopy(tree->TypeGet()), reg, tree->gtRegNum, tree->TypeGet()); - } - else - { - bool unalignedLoad = false; - switch (tree->OperGet()) - { - case GT_IND: - case GT_CLS_VAR: - if (tree->gtFlags & GTF_IND_UNALIGNED) - unalignedLoad = true; - break; - case GT_LCL_FLD: - // Check for a misalignment on a Floating Point field - // - if (varTypeIsFloating(tree->TypeGet())) - { - if ((tree->gtLclFld.gtLclOffs % emitTypeSize(tree->TypeGet())) != 0) - { - unalignedLoad = true; - } - } - break; - default: - break; - } - - if (unalignedLoad) - { - // Make the target addressable - // - regMaskTP addrReg = genMakeAddressable(tree, 0, RegSet::KEEP_REG, true); - regSet.rsLockUsedReg(addrReg); // Must prevent regSet.rsGrabReg from choosing an addrReg - - var_types loadType = tree->TypeGet(); - assert(loadType == TYP_DOUBLE || loadType == TYP_FLOAT); - - // Unaligned Floating-Point Loads must be loaded into integer register(s) - // and then moved over to the Floating-Point register - regNumber intRegLo = regSet.rsGrabReg(RBM_ALLINT); - regNumber intRegHi = REG_NA; - regMaskTP tmpLockMask = genRegMask(intRegLo); - - if (loadType == TYP_DOUBLE) - { - intRegHi = regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(intRegLo)); - tmpLockMask |= genRegMask(intRegHi); - } - - regSet.rsLockReg(tmpLockMask); // Temporarily lock the intRegs - tree->gtType = TYP_INT; // Temporarily change the type to TYP_INT - - inst_RV_TT(ins_Load(TYP_INT), intRegLo, tree); - regTracker.rsTrackRegTrash(intRegLo); - - if (loadType == TYP_DOUBLE) - { - inst_RV_TT(ins_Load(TYP_INT), intRegHi, tree, 4); - regTracker.rsTrackRegTrash(intRegHi); - } - - tree->gtType = loadType; // Change the type back to the floating point type - regSet.rsUnlockReg(tmpLockMask); // Unlock the intRegs - - // move the integer register(s) over to the FP register - // - if (loadType == TYP_DOUBLE) - getEmitter()->emitIns_R_R_R(INS_vmov_i2d, EA_8BYTE, reg, intRegLo, intRegHi); - else - getEmitter()->emitIns_R_R(INS_vmov_i2f, EA_4BYTE, reg, intRegLo); - - // Free up anything that was tied up by genMakeAddressable - // - regSet.rsUnlockUsedReg(addrReg); - genDoneAddressable(tree, addrReg, RegSet::KEEP_REG); - } - else - { - inst_RV_TT(ins_FloatLoad(tree->TypeGet()), reg, tree); - } - if (((tree->OperGet() == GT_CLS_VAR) || (tree->OperGet() == GT_IND)) && (tree->gtFlags & GTF_IND_VOLATILE)) - { - // Emit a memory barrier instruction after the load - instGen_MemoryBarrier(); - } - } -} - -void CodeGen::genCodeForTreeFloat_DONE(GenTree* tree, regNumber reg) -{ - return genCodeForTree_DONE(tree, reg); -} - -void CodeGen::genFloatAsgArith(GenTree* tree) -{ - // Set Flowgraph.cpp, line 13750 - // arm VFP has tons of regs, 3-op instructions, and no addressing modes - // so asg ops are kind of pointless - noway_assert(!"Not Reachable for _TARGET_ARM_"); -} - -regNumber CodeGen::genAssignArithFloat(genTreeOps oper, GenTree* dst, regNumber dstreg, GenTree* src, regNumber srcreg) -{ - regNumber result; - - // dst should be a regvar or memory - - if (dst->IsRegVar()) - { - regNumber reg = dst->gtRegNum; - - if (src->IsRegVar()) - { - inst_RV_RV(ins_MathOp(oper, dst->gtType), reg, src->gtRegNum, dst->gtType); - } - else - { - inst_RV_TT(ins_MathOp(oper, dst->gtType), reg, src, 0, EmitSize(dst)); - } - result = reg; - } - else // dst in memory - { - // since this is an asgop the ACTUAL destination is memory - // but it is also one of the sources and SSE ops do not allow mem dests - // so we have loaded it into a reg, and that is what dstreg represents - assert(dstreg != REG_NA); - - if ((src->InReg())) - { - inst_RV_RV(ins_MathOp(oper, dst->gtType), dstreg, src->gtRegNum, dst->gtType); - } - else - { - // mem mem operation - inst_RV_TT(ins_MathOp(oper, dst->gtType), dstreg, src, 0, EmitSize(dst)); - } - - dst->SetInReg(false); // ??? - - inst_TT_RV(ins_FloatStore(dst->gtType), dst, dstreg, 0, EmitSize(dst)); - - result = REG_NA; - } - - return result; -} - -void CodeGen::genFloatArith(GenTree* tree, RegSet::RegisterPreference* tgtPref) -{ - var_types type = tree->TypeGet(); - genTreeOps oper = tree->OperGet(); - GenTree* op1 = tree->gtGetOp1(); - GenTree* op2 = tree->gtGetOp2IfPresent(); - - regNumber tgtReg; - unsigned varNum; - LclVarDsc* varDsc; - VARSET_TP varBit; - - assert(oper == GT_ADD || oper == GT_SUB || oper == GT_MUL || oper == GT_DIV); - - RegSet::RegisterPreference defaultPref(RBM_ALLFLOAT, RBM_NONE); - if (tgtPref == NULL) - { - tgtPref = &defaultPref; - } - - // Is the op2 (RHS)more complex than op1 (LHS)? - // - if (tree->gtFlags & GTF_REVERSE_OPS) - { - regMaskTP bestRegs = regSet.rsNarrowHint(RBM_ALLFLOAT, ~op1->gtRsvdRegs); - RegSet::RegisterPreference pref(RBM_ALLFLOAT, bestRegs); - - // Evaluate op2 into a floating point register - // - genCodeForTreeFloat(op2, &pref); - regSet.SetUsedRegFloat(op2, true); - - // Evaluate op1 into any floating point register - // - genCodeForTreeFloat(op1); - regSet.SetUsedRegFloat(op1, true); - - regNumber op1Reg = op1->gtRegNum; - regMaskTP op1Mask = genRegMaskFloat(op1Reg, type); - - // Fix 388445 ARM JitStress WP7 - regSet.rsLockUsedReg(op1Mask); - genRecoverReg(op2, RBM_ALLFLOAT, RegSet::KEEP_REG); - noway_assert(op2->InReg()); - regSet.rsUnlockUsedReg(op1Mask); - - regSet.SetUsedRegFloat(op1, false); - regSet.SetUsedRegFloat(op2, false); - } - else - { - regMaskTP bestRegs = regSet.rsNarrowHint(RBM_ALLFLOAT, ~op2->gtRsvdRegs); - RegSet::RegisterPreference pref(RBM_ALLFLOAT, bestRegs); - - // Evaluate op1 into a floating point register - // - genCodeForTreeFloat(op1, &pref); - regSet.SetUsedRegFloat(op1, true); - - // Evaluate op2 into any floating point register - // - genCodeForTreeFloat(op2); - regSet.SetUsedRegFloat(op2, true); - - regNumber op2Reg = op2->gtRegNum; - regMaskTP op2Mask = genRegMaskFloat(op2Reg, type); - - // Fix 388445 ARM JitStress WP7 - regSet.rsLockUsedReg(op2Mask); - genRecoverReg(op1, RBM_ALLFLOAT, RegSet::KEEP_REG); - noway_assert(op1->InReg()); - regSet.rsUnlockUsedReg(op2Mask); - - regSet.SetUsedRegFloat(op2, false); - regSet.SetUsedRegFloat(op1, false); - } - - tgtReg = regSet.PickRegFloat(type, tgtPref, true); - - noway_assert(op1->InReg()); - noway_assert(op2->InReg()); - - inst_RV_RV_RV(ins_MathOp(oper, type), tgtReg, op1->gtRegNum, op2->gtRegNum, emitActualTypeSize(type)); - - genCodeForTreeFloat_DONE(tree, tgtReg); -} - -regNumber CodeGen::genArithmFloat( - genTreeOps oper, GenTree* dst, regNumber dstreg, GenTree* src, regNumber srcreg, bool bReverse) -{ - regNumber result = REG_NA; - - assert(dstreg != REG_NA); - - if (bReverse) - { - GenTree* temp = src; - regNumber tempreg = srcreg; - src = dst; - srcreg = dstreg; - dst = temp; - dstreg = tempreg; - } - - if (srcreg == REG_NA) - { - if (src->IsRegVar()) - { - inst_RV_RV(ins_MathOp(oper, dst->gtType), dst->gtRegNum, src->gtRegNum, dst->gtType); - } - else - { - inst_RV_TT(ins_MathOp(oper, dst->gtType), dst->gtRegNum, src); - } - } - else - { - inst_RV_RV(ins_MathOp(oper, dst->gtType), dstreg, srcreg, dst->gtType); - } - - result = dstreg; - - assert(result != REG_NA); - return result; -} - -void CodeGen::genKeepAddressableFloat(GenTree* tree, regMaskTP* regMaskIntPtr, regMaskTP* regMaskFltPtr) -{ - regMaskTP regMaskInt, regMaskFlt; - - regMaskInt = *regMaskIntPtr; - regMaskFlt = *regMaskFltPtr; - - *regMaskIntPtr = *regMaskFltPtr = 0; - - switch (tree->OperGet()) - { - case GT_REG_VAR: - // If register has been spilled, unspill it - if (tree->gtFlags & GTF_SPILLED) - { - UnspillFloat(&compiler->lvaTable[tree->gtLclVarCommon.gtLclNum]); - } - break; - - case GT_CNS_DBL: - if (tree->gtFlags & GTF_SPILLED) - { - UnspillFloat(tree); - } - *regMaskFltPtr = genRegMaskFloat(tree->gtRegNum, tree->TypeGet()); - break; - - case GT_LCL_FLD: - case GT_LCL_VAR: - case GT_CLS_VAR: - break; - - case GT_IND: - if (regMaskFlt == RBM_NONE) - { - *regMaskIntPtr = genKeepAddressable(tree, regMaskInt, 0); - *regMaskFltPtr = 0; - return; - } - __fallthrough; - - default: - *regMaskIntPtr = 0; - if (tree->gtFlags & GTF_SPILLED) - { - UnspillFloat(tree); - } - *regMaskFltPtr = genRegMaskFloat(tree->gtRegNum, tree->TypeGet()); - break; - } -} - -void CodeGen::genComputeAddressableFloat(GenTree* tree, - regMaskTP addrRegInt, - regMaskTP addrRegFlt, - RegSet::KeepReg keptReg, - regMaskTP needReg, - RegSet::KeepReg keepReg, - bool freeOnly /* = false */) -{ - noway_assert(genStillAddressable(tree)); - noway_assert(varTypeIsFloating(tree->TypeGet())); - - genDoneAddressableFloat(tree, addrRegInt, addrRegFlt, keptReg); - - regNumber reg; - if (tree->InReg()) - { - reg = tree->gtRegNum; - if (freeOnly && !(genRegMaskFloat(reg, tree->TypeGet()) & regSet.RegFreeFloat())) - { - goto LOAD_REG; - } - } - else - { - LOAD_REG: - RegSet::RegisterPreference pref(needReg, RBM_NONE); - reg = regSet.PickRegFloat(tree->TypeGet(), &pref); - genLoadFloat(tree, reg); - } - - genMarkTreeInReg(tree, reg); - - if (keepReg == RegSet::KEEP_REG) - { - regSet.SetUsedRegFloat(tree, true); - } -} - -void CodeGen::genDoneAddressableFloat(GenTree* tree, - regMaskTP addrRegInt, - regMaskTP addrRegFlt, - RegSet::KeepReg keptReg) -{ - assert(!(addrRegInt && addrRegFlt)); - - if (addrRegInt) - { - return genDoneAddressable(tree, addrRegInt, keptReg); - } - else if (addrRegFlt) - { - if (keptReg == RegSet::KEEP_REG) - { - for (regNumber r = REG_FP_FIRST; r != REG_NA; r = regNextOfType(r, tree->TypeGet())) - { - regMaskTP mask = genRegMaskFloat(r, tree->TypeGet()); - // some masks take up more than one bit - if ((mask & addrRegFlt) == mask) - { - regSet.SetUsedRegFloat(tree, false); - } - } - } - } -} - -GenTree* CodeGen::genMakeAddressableFloat(GenTree* tree, - regMaskTP* regMaskIntPtr, - regMaskTP* regMaskFltPtr, - bool bCollapseConstantDoubles) -{ - *regMaskIntPtr = *regMaskFltPtr = 0; - - switch (tree->OperGet()) - { - - case GT_LCL_VAR: - genMarkLclVar(tree); - __fallthrough; - - case GT_REG_VAR: - case GT_LCL_FLD: - case GT_CLS_VAR: - return tree; - - case GT_IND: - // Try to make the address directly addressable - - if (genMakeIndAddrMode(tree->gtOp.gtOp1, tree, false, RBM_ALLFLOAT, RegSet::KEEP_REG, regMaskIntPtr, false)) - { - genUpdateLife(tree); - return tree; - } - else - { - GenTree* addr = tree; - tree = tree->gtOp.gtOp1; - genCodeForTree(tree, 0); - regSet.rsMarkRegUsed(tree, addr); - - *regMaskIntPtr = genRegMask(tree->gtRegNum); - return addr; - } - - // fall through - - default: - genCodeForTreeFloat(tree); - regSet.SetUsedRegFloat(tree, true); - - // update mask - *regMaskFltPtr = genRegMaskFloat(tree->gtRegNum, tree->TypeGet()); - - return tree; - break; - } -} - -void CodeGen::genCodeForTreeCastFloat(GenTree* tree, RegSet::RegisterPreference* pref) -{ - GenTree* op1 = tree->gtOp.gtOp1; - var_types from = op1->gtType; - var_types to = tree->gtType; - - if (varTypeIsFloating(from)) - genCodeForTreeCastFromFloat(tree, pref); - else - genCodeForTreeCastToFloat(tree, pref); -} - -void CodeGen::genCodeForTreeCastFromFloat(GenTree* tree, RegSet::RegisterPreference* pref) -{ - GenTree* op1 = tree->gtOp.gtOp1; - var_types from = op1->gtType; - var_types final = tree->gtType; - var_types intermediate = tree->CastToType(); - - regNumber srcReg; - regNumber dstReg; - - assert(varTypeIsFloating(from)); - - // Evaluate op1 into a floating point register - // - if (varTypeIsFloating(final)) - { - genCodeForTreeFloat(op1, pref); - } - else - { - RegSet::RegisterPreference defaultPref(RBM_ALLFLOAT, RBM_NONE); - genCodeForTreeFloat(op1, &defaultPref); - } - - srcReg = op1->gtRegNum; - - if (varTypeIsFloating(final)) - { - // float => double or - // double => float - - dstReg = regSet.PickRegFloat(final, pref); - - instruction ins = ins_FloatConv(final, from); - if (!isMoveIns(ins) || (srcReg != dstReg)) - { - inst_RV_RV(ins, dstReg, srcReg, from); - } - } - else - { - // float => int or - // double => int - - dstReg = regSet.rsPickReg(pref->ok, pref->best); - - RegSet::RegisterPreference defaultPref(RBM_ALLFLOAT, genRegMask(srcReg)); - regNumber intermediateReg = regSet.PickRegFloat(TYP_FLOAT, &defaultPref); - - if ((intermediate == TYP_UINT) && (final == TYP_INT)) - { - // Perform the conversion using the FP unit - inst_RV_RV(ins_FloatConv(TYP_UINT, from), intermediateReg, srcReg, from); - - // Prevent the call to genIntegerCast - final = TYP_UINT; - } - else - { - // Perform the conversion using the FP unit - inst_RV_RV(ins_FloatConv(TYP_INT, from), intermediateReg, srcReg, from); - } - - // the integer result is now in the FP register, move it to the integer ones - getEmitter()->emitIns_R_R(INS_vmov_f2i, EA_4BYTE, dstReg, intermediateReg); - - regTracker.rsTrackRegTrash(dstReg); - - // handle things like int <- short <- double - if (final != intermediate) - { - // lie about the register so integer cast logic will finish the job - op1->gtRegNum = dstReg; - genIntegerCast(tree, pref->ok, pref->best); - } - } - - genUpdateLife(op1); - genCodeForTree_DONE(tree, dstReg); -} - -void CodeGen::genCodeForTreeCastToFloat(GenTree* tree, RegSet::RegisterPreference* pref) -{ - regNumber srcReg; - regNumber dstReg; - regNumber vmovReg; - - regMaskTP addrReg; - - GenTree* op1 = tree->gtOp.gtOp1; - op1 = genCodeForCommaTree(op1); // Trim off any comma expressions. - var_types from = op1->gtType; - var_types to = tree->gtType; - - switch (from) - { - case TYP_BOOL: - case TYP_BYTE: - case TYP_UBYTE: - case TYP_USHORT: - case TYP_SHORT: - // load it into a register - genCodeForTree(op1, 0); - - __fallthrough; - - case TYP_BYREF: - from = TYP_INT; - - __fallthrough; - - case TYP_INT: - { - if (op1->gtOper == GT_LCL_FLD) - { - genComputeReg(op1, 0, RegSet::ANY_REG, RegSet::FREE_REG); - addrReg = 0; - } - else - { - addrReg = genMakeAddressable(op1, 0, RegSet::FREE_REG); - } - - // Grab register for the cast - dstReg = regSet.PickRegFloat(to, pref); - - // float type that is same size as the int we are coming from - var_types vmovType = TYP_FLOAT; - regNumber vmovReg = regSet.PickRegFloat(vmovType); - - if (tree->gtFlags & GTF_UNSIGNED) - from = TYP_UINT; - - // Is the value a constant, or now sitting in a register? - if (op1->InReg() || op1->IsCnsIntOrI()) - { - if (op1->IsCnsIntOrI()) - { - srcReg = genGetRegSetToIcon(op1->AsIntConCommon()->IconValue(), RBM_NONE, op1->TypeGet()); - } - else - { - srcReg = op1->gtRegNum; - } - - // move the integer register value over to the FP register - getEmitter()->emitIns_R_R(INS_vmov_i2f, EA_4BYTE, vmovReg, srcReg); - // now perform the conversion to the proper floating point representation - inst_RV_RV(ins_FloatConv(to, from), dstReg, vmovReg, to); - } - else - { - // Load the value from its address - inst_RV_TT(ins_FloatLoad(vmovType), vmovReg, op1); - inst_RV_RV(ins_FloatConv(to, from), dstReg, vmovReg, to); - } - - if (addrReg) - { - genDoneAddressable(op1, addrReg, RegSet::FREE_REG); - } - genMarkTreeInReg(tree, dstReg); - - break; - } - case TYP_FLOAT: - case TYP_DOUBLE: - { - // This is a cast from float to double or double to float - - genCodeForTreeFloat(op1, pref); - - // Grab register for the cast - dstReg = regSet.PickRegFloat(to, pref); - - if ((from != to) || (dstReg != op1->gtRegNum)) - { - inst_RV_RV(ins_FloatConv(to, from), dstReg, op1->gtRegNum, to); - } - - // Assign reg to tree - genMarkTreeInReg(tree, dstReg); - - break; - } - default: - { - assert(!"unsupported cast"); - break; - } - } -} - -void CodeGen::genRoundFloatExpression(GenTree* op, var_types type) -{ - // Do nothing with memory resident opcodes - these are the right precision - if (type == TYP_UNDEF) - type = op->TypeGet(); - - switch (op->gtOper) - { - case GT_LCL_VAR: - genMarkLclVar(op); - __fallthrough; - - case GT_LCL_FLD: - case GT_CLS_VAR: - case GT_CNS_DBL: - case GT_IND: - if (type == op->TypeGet()) - return; - - default: - break; - } -} - -#ifdef DEBUG - -regMaskTP CodeGenInterface::genStressLockedMaskFloat() -{ - return 0; -} - -#endif // DEBUG - -/********************************************************************* - * Preserve used callee trashed registers across calls. - * - */ -void CodeGen::SpillForCallRegisterFP(regMaskTP noSpillMask) -{ - regMaskTP regBit = 1; - for (regNumber regNum = REG_FIRST; regNum < REG_COUNT; regNum = REG_NEXT(regNum), regBit <<= 1) - { - if (!(regBit & noSpillMask) && (regBit & RBM_FLT_CALLEE_TRASH) && regSet.rsUsedTree[regNum]) - { - SpillFloat(regNum, true); - } - } -} - -/********************************************************************* - * - * Spill the used floating point register or the enregistered var. - * If spilling for a call, then record so, so we can unspill the - * ones that were spilled for the call. - * - */ -void CodeGenInterface::SpillFloat(regNumber reg, bool bIsCall /* = false */) -{ - regSet.rsSpillReg(reg); -} - -void CodeGen::UnspillFloatMachineDep(RegSet::SpillDsc* spillDsc) -{ - // Do actual unspill - regNumber reg; - if (spillDsc->bEnregisteredVariable) - { - NYI("unspill enreg var"); - reg = regSet.PickRegFloat(); - } - else - { - UnspillFloatMachineDep(spillDsc, false); - } -} - -void CodeGen::UnspillFloatMachineDep(RegSet::SpillDsc* spillDsc, bool useSameReg) -{ - assert(!spillDsc->bEnregisteredVariable); - - assert(spillDsc->spillTree->gtFlags & GTF_SPILLED); - - spillDsc->spillTree->gtFlags &= ~GTF_SPILLED; - - var_types type = spillDsc->spillTree->TypeGet(); - regNumber reg; - if (useSameReg) - { - // Give register preference as the same register that the tree was originally using. - reg = spillDsc->spillTree->gtRegNum; - - regMaskTP maskPref = genRegMask(reg); - if (type == TYP_DOUBLE) - { - assert((maskPref & RBM_DBL_REGS) != 0); - maskPref |= genRegMask(REG_NEXT(reg)); - } - - RegSet::RegisterPreference pref(RBM_ALLFLOAT, maskPref); - reg = regSet.PickRegFloat(type, &pref); - } - else - { - reg = regSet.PickRegFloat(); - } - - // load from spilled spot - compiler->codeGen->reloadFloatReg(type, spillDsc->spillTemp, reg); - - compiler->codeGen->genMarkTreeInReg(spillDsc->spillTree, reg); - regSet.SetUsedRegFloat(spillDsc->spillTree, true); -} - -// -instruction genFloatJumpInstr(genTreeOps cmp, bool isUnordered) -{ - switch (cmp) - { - case GT_EQ: - return INS_beq; - case GT_NE: - return INS_bne; - case GT_LT: - return isUnordered ? INS_blt : INS_blo; - case GT_LE: - return isUnordered ? INS_ble : INS_bls; - case GT_GE: - return isUnordered ? INS_bpl : INS_bge; - case GT_GT: - return isUnordered ? INS_bhi : INS_bgt; - default: - unreached(); - } -} - -void CodeGen::genCondJumpFloat(GenTree* cond, BasicBlock* jumpTrue, BasicBlock* jumpFalse) -{ - assert(jumpTrue && jumpFalse); - assert(!(cond->gtFlags & GTF_REVERSE_OPS)); // Done in genCondJump() - assert(varTypeIsFloating(cond->gtOp.gtOp1->gtType)); - - GenTree* op1 = cond->gtOp.gtOp1; - GenTree* op2 = cond->gtOp.gtOp2; - genTreeOps cmp = cond->OperGet(); - bool isUnordered = cond->gtFlags & GTF_RELOP_NAN_UN ? true : false; - - regMaskTP bestRegs = regSet.rsNarrowHint(RBM_ALLFLOAT, ~op2->gtRsvdRegs); - RegSet::RegisterPreference pref(RBM_ALLFLOAT, bestRegs); - - // Prepare operands. - genCodeForTreeFloat(op1, &pref); - regSet.SetUsedRegFloat(op1, true); - - genCodeForTreeFloat(op2); - regSet.SetUsedRegFloat(op2, true); - - genRecoverReg(op1, RBM_ALLFLOAT, RegSet::KEEP_REG); - noway_assert(op1->InReg()); - - // cmp here - getEmitter()->emitIns_R_R(INS_vcmp, EmitSize(op1), op1->gtRegNum, op2->gtRegNum); - - // vmrs with register 0xf has special meaning of transferring flags - getEmitter()->emitIns_R(INS_vmrs, EA_4BYTE, REG_R15); - - regSet.SetUsedRegFloat(op2, false); - regSet.SetUsedRegFloat(op1, false); - - getEmitter()->emitIns_J(genFloatJumpInstr(cmp, isUnordered), jumpTrue); -} - -#endif // LEGACY_BACKEND diff --git a/src/jit/registerfp.h b/src/jit/registerfp.h deleted file mode 100644 index 4c3ecb6050..0000000000 --- a/src/jit/registerfp.h +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -/*****************************************************************************/ -/*****************************************************************************/ -#ifndef REGDEF -#error Must define REGDEF macro before including this file -#endif -/*****************************************************************************/ -/* The following is x86 specific */ -/*****************************************************************************/ -/* -REGDEF(name, rnum, mask, sname) */ -REGDEF(FPV0, 0, 0x01, "FPV0") -REGDEF(FPV1, 1, 0x02, "FPV1") -REGDEF(FPV2, 2, 0x04, "FPV2") -REGDEF(FPV3, 3, 0x08, "FPV3") -REGDEF(FPV4, 4, 0x10, "FPV4") -REGDEF(FPV5, 5, 0x20, "FPV5") -REGDEF(FPV6, 6, 0x40, "FPV6") -REGDEF(FPV7, 7, 0x80, "FPV7") - -/*****************************************************************************/ -#undef REGDEF -/*****************************************************************************/ diff --git a/src/jit/registerxmm.h b/src/jit/registerxmm.h deleted file mode 100644 index 4c34261ba8..0000000000 --- a/src/jit/registerxmm.h +++ /dev/null @@ -1,48 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// clang-format off -/*****************************************************************************/ -/*****************************************************************************/ -#ifndef REGDEF -#error Must define REGDEF macro before including this file -#endif - -#ifndef LEGACY_BACKEND -#error This file is only used for the LEGACY_BACKEND build. -#endif - -#if defined(_TARGET_XARCH_) - -#define XMMMASK(x) (unsigned(1) << (x-1)) - -/* -REGDEF(name, rnum, mask, sname) */ -REGDEF(XMM0, 0, XMMMASK(1), "xmm0" ) -REGDEF(XMM1, 1, XMMMASK(2), "xmm1" ) -REGDEF(XMM2, 2, XMMMASK(3), "xmm2" ) -REGDEF(XMM3, 3, XMMMASK(4), "xmm3" ) -REGDEF(XMM4, 4, XMMMASK(5), "xmm4" ) -REGDEF(XMM5, 5, XMMMASK(6), "xmm5" ) -REGDEF(XMM6, 6, XMMMASK(7), "xmm6" ) -REGDEF(XMM7, 7, XMMMASK(8), "xmm7" ) - -#ifdef _TARGET_AMD64_ -REGDEF(XMM8, 8, XMMMASK(9), "xmm8" ) -REGDEF(XMM9, 9, XMMMASK(10), "xmm9" ) -REGDEF(XMM10, 10, XMMMASK(11), "xmm10" ) -REGDEF(XMM11, 11, XMMMASK(12), "xmm11" ) -REGDEF(XMM12, 12, XMMMASK(13), "xmm12" ) -REGDEF(XMM13, 13, XMMMASK(14), "xmm13" ) -REGDEF(XMM14, 14, XMMMASK(15), "xmm14" ) -REGDEF(XMM15, 15, XMMMASK(16), "xmm15" ) -#endif - -#endif // _TARGET_* - -/*****************************************************************************/ -#undef REGDEF -/*****************************************************************************/ - -// clang-format on diff --git a/src/jit/regset.cpp b/src/jit/regset.cpp index f37c422646..3bef88aea9 100644 --- a/src/jit/regset.cpp +++ b/src/jit/regset.cpp @@ -36,13 +36,6 @@ const regMaskSmall regMasks[] = { }; #endif -#ifdef _TARGET_X86_ -const regMaskSmall regFPMasks[] = { -#define REGDEF(name, rnum, mask, sname) mask, -#include "registerfp.h" -}; -#endif // _TARGET_X86_ - /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX @@ -54,9 +47,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX void RegSet::rsClearRegsModified() { -#ifndef LEGACY_BACKEND assert(m_rsCompiler->lvaDoneFrameLayout < Compiler::FINAL_FRAME_LAYOUT); -#endif // !LEGACY_BACKEND #ifdef DEBUG if (m_rsCompiler->verbose) @@ -74,7 +65,6 @@ void RegSet::rsSetRegsModified(regMaskTP mask DEBUGARG(bool suppressDump)) assert(mask != RBM_NONE); assert(rsModifiedRegsMaskInitialized); -#ifndef LEGACY_BACKEND // We can't update the modified registers set after final frame layout (that is, during code // generation and after). Ignore prolog and epilog generation: they call register tracking to // modify rbp, for example, even in functions that use rbp as a frame pointer. Make sure normal @@ -84,7 +74,6 @@ void RegSet::rsSetRegsModified(regMaskTP mask DEBUGARG(bool suppressDump)) assert((m_rsCompiler->lvaDoneFrameLayout < Compiler::FINAL_FRAME_LAYOUT) || m_rsCompiler->compGeneratingProlog || m_rsCompiler->compGeneratingEpilog || (((rsModifiedRegsMask | mask) & RBM_CALLEE_SAVED) == (rsModifiedRegsMask & RBM_CALLEE_SAVED))); -#endif // !LEGACY_BACKEND #ifdef DEBUG if (m_rsCompiler->verbose && !suppressDump) @@ -110,12 +99,10 @@ void RegSet::rsRemoveRegsModified(regMaskTP mask) assert(mask != RBM_NONE); assert(rsModifiedRegsMaskInitialized); -#ifndef LEGACY_BACKEND // See comment in rsSetRegsModified(). assert((m_rsCompiler->lvaDoneFrameLayout < Compiler::FINAL_FRAME_LAYOUT) || m_rsCompiler->compGeneratingProlog || m_rsCompiler->compGeneratingEpilog || (((rsModifiedRegsMask & ~mask) & RBM_CALLEE_SAVED) == (rsModifiedRegsMask & RBM_CALLEE_SAVED))); -#endif // !LEGACY_BACKEND #ifdef DEBUG if (m_rsCompiler->verbose) @@ -166,3025 +153,443 @@ void RegSet::SetMaskVars(regMaskTP newMaskVars) _rsMaskVars = newMaskVars; } -#ifdef DEBUG - -RegSet::rsStressRegsType RegSet::rsStressRegs() -{ -#ifndef LEGACY_BACKEND - return RS_STRESS_NONE; -#else // LEGACY_BACKEND - rsStressRegsType val = (rsStressRegsType)JitConfig.JitStressRegs(); - if (val == RS_STRESS_NONE && m_rsCompiler->compStressCompile(Compiler::STRESS_REGS, 15)) - val = RS_PICK_BAD_REG; - return val; -#endif // LEGACY_BACKEND -} -#endif // DEBUG - -#ifdef LEGACY_BACKEND /***************************************************************************** - * Includes 'includeHint' if 'regs' is empty + * + * Trash the rsRegValues associated with a register */ -regMaskTP RegSet::rsUseIfZero(regMaskTP regs, regMaskTP includeHint) +// inline +void RegTracker::rsTrackRegTrash(regNumber reg) { - return regs ? regs : includeHint; + /* Keep track of which registers we ever touch */ + + regSet->rsSetRegsModified(genRegMask(reg)); } -/***************************************************************************** - * Excludes 'excludeHint' if it results in a non-empty mask - */ +/*****************************************************************************/ -regMaskTP RegSet::rsExcludeHint(regMaskTP regs, regMaskTP excludeHint) +// inline +void RegTracker::rsTrackRegIntCns(regNumber reg, ssize_t val) { - regMaskTP OKmask = regs & ~excludeHint; - return OKmask ? OKmask : regs; -} + assert(genIsValidIntReg(reg)); -/***************************************************************************** - * Narrows choice by 'narrowHint' if it results in a non-empty mask - */ + /* Keep track of which registers we ever touch */ -regMaskTP RegSet::rsNarrowHint(regMaskTP regs, regMaskTP narrowHint) -{ - regMaskTP narrowed = regs & narrowHint; - return narrowed ? narrowed : regs; + regSet->rsSetRegsModified(genRegMask(reg)); } -/***************************************************************************** - * Excludes 'exclude' from regs if non-zero, or from RBM_ALLINT - */ +/*****************************************************************************/ -regMaskTP RegSet::rsMustExclude(regMaskTP regs, regMaskTP exclude) +RegSet::RegSet(Compiler* compiler, GCInfo& gcInfo) : m_rsCompiler(compiler), m_rsGCInfo(gcInfo) { - // Try to exclude from current set - regMaskTP OKmask = regs & ~exclude; - - // If current set wont work, exclude from RBM_ALLINT - if (OKmask == RBM_NONE) - OKmask = (RBM_ALLINT & ~exclude); - - assert(OKmask); + /* Initialize the spill logic */ - return OKmask; -} + rsSpillInit(); -/***************************************************************************** - * - * The following returns a mask that yields all free registers. - */ + /* Initialize the argument register count */ + // TODO-Cleanup: Consider moving intRegState and floatRegState to RegSet. They used + // to be initialized here, but are now initialized in the CodeGen constructor. + // intRegState.rsCurRegArgNum = 0; + // loatRegState.rsCurRegArgNum = 0; -// inline -regMaskTP RegSet::rsRegMaskFree() -{ - /* Any register that is locked must also be marked as 'used' */ + rsMaskResvd = RBM_NONE; - assert((rsMaskUsed & rsMaskLock) == rsMaskLock); +#ifdef _TARGET_ARMARCH_ + rsMaskCalleeSaved = RBM_NONE; +#endif // _TARGET_ARMARCH_ - /* Any register that isn't used and doesn't hold a variable is free */ +#ifdef _TARGET_ARM_ + rsMaskPreSpillRegArg = RBM_NONE; + rsMaskPreSpillAlign = RBM_NONE; +#endif - return RBM_ALLINT & ~(rsMaskUsed | rsMaskVars | rsMaskResvd); +#ifdef DEBUG + rsModifiedRegsMaskInitialized = false; +#endif // DEBUG } /***************************************************************************** * - * The following returns a mask of registers that may be grabbed. + * Finds the SpillDsc corresponding to 'tree' assuming it was spilled from 'reg'. */ -// inline -regMaskTP RegSet::rsRegMaskCanGrab() +RegSet::SpillDsc* RegSet::rsGetSpillInfo(GenTree* tree, regNumber reg, SpillDsc** pPrevDsc) { - /* Any register that is locked must also be marked as 'used' */ - - assert((rsMaskUsed & rsMaskLock) == rsMaskLock); - - /* Any register that isn't locked and doesn't hold a var can be grabbed */ - - regMaskTP result = (RBM_ALLINT & ~(rsMaskLock | rsMaskVars)); - -#ifdef _TARGET_ARM_ + /* Normally, trees are unspilled in the order of being spilled due to + the post-order walking of trees during code-gen. However, this will + not be true for something like a GT_ARR_ELEM node */ - // On the ARM when we pass structs in registers we set the rsUsedTree[] - // to be the full TYP_STRUCT tree, which doesn't allow us to spill/unspill - // these argument registers. To fix JitStress issues that can occur - // when rsPickReg tries to spill one of these registers we just remove them - // from the set of registers that we can grab - // - regMaskTP structArgMask = RBM_NONE; - // Load all the variable arguments in registers back to their registers. - for (regNumber reg = REG_ARG_FIRST; reg <= REG_ARG_LAST; reg = REG_NEXT(reg)) + SpillDsc* prev; + SpillDsc* dsc; + for (prev = nullptr, dsc = rsSpillDesc[reg]; dsc != nullptr; prev = dsc, dsc = dsc->spillNext) { - GenTree* regHolds = rsUsedTree[reg]; - if ((regHolds != NULL) && (regHolds->TypeGet() == TYP_STRUCT)) + if (dsc->spillTree == tree) { - structArgMask |= genRegMask(reg); + break; } } - result &= ~structArgMask; -#endif - return result; + if (pPrevDsc) + { + *pPrevDsc = prev; + } + + return dsc; } /***************************************************************************** * - * Pick a free register. It is guaranteed that a register is available. - * Note that rsPickReg() can spill a register, whereas rsPickFreeReg() will not. + * Record the fact that the given register now contains the given local + * variable. Pointers are handled specially since reusing the register + * will extend the lifetime of a pointer register which is not a register + * variable. */ -// inline -regNumber RegSet::rsPickFreeReg(regMaskTP regMaskHint) +void RegTracker::rsTrackRegLclVar(regNumber reg, unsigned var) { - regMaskTP freeRegs = rsRegMaskFree(); - assert(freeRegs != RBM_NONE); - - regMaskTP regs = rsNarrowHint(freeRegs, regMaskHint); - - return rsGrabReg(regs); -} + LclVarDsc* varDsc = &compiler->lvaTable[var]; + assert(reg != REG_STK); +#if CPU_HAS_FP_SUPPORT + assert(varTypeIsFloating(varDsc->TypeGet()) == false); +#endif -/***************************************************************************** - * - * Mark the given set of registers as used and locked. - */ + if (compiler->lvaTable[var].lvAddrExposed) + { + return; + } -// inline -void RegSet::rsLockReg(regMaskTP regMask) -{ - /* Must not be already marked as either used or locked */ + /* Keep track of which registers we ever touch */ - assert((rsMaskUsed & regMask) == 0); - rsMaskUsed |= regMask; - assert((rsMaskLock & regMask) == 0); - rsMaskLock |= regMask; + regSet->rsSetRegsModified(genRegMask(reg)); } -/***************************************************************************** - * - * Mark an already used set of registers as locked. - */ +/*****************************************************************************/ -// inline -void RegSet::rsLockUsedReg(regMaskTP regMask) +void RegTracker::rsTrackRegCopy(regNumber reg1, regNumber reg2) { - /* Must not be already marked as locked. Must be already marked as used. */ + /* Keep track of which registers we ever touch */ - assert((rsMaskLock & regMask) == 0); - assert((rsMaskUsed & regMask) == regMask); + assert(reg1 < REG_COUNT); + assert(reg2 < REG_COUNT); - rsMaskLock |= regMask; + regSet->rsSetRegsModified(genRegMask(reg1)); } -/***************************************************************************** - * - * Mark the given set of registers as no longer used/locked. - */ - -// inline -void RegSet::rsUnlockReg(regMaskTP regMask) +//------------------------------------------------------------ +// rsSpillTree: Spill the tree held in 'reg'. +// +// Arguments: +// reg - Register of tree node that is to be spilled +// tree - GenTree node that is being spilled +// regIdx - Register index identifying the specific result +// register of a multi-reg call node. For single-reg +// producing tree nodes its value is zero. +// +// Return Value: +// None. +// +// Assumption: +// RyuJIT backend specific: in case of multi-reg call nodes, GTF_SPILL +// flag associated with the reg that is being spilled is cleared. The +// caller of this method is expected to clear GTF_SPILL flag on call +// node after all of its registers marked for spilling are spilled. +// +void RegSet::rsSpillTree(regNumber reg, GenTree* tree, unsigned regIdx /* =0 */) { - /* Must be currently marked as both used and locked */ - - assert((rsMaskUsed & regMask) == regMask); - rsMaskUsed -= regMask; - assert((rsMaskLock & regMask) == regMask); - rsMaskLock -= regMask; -} + assert(tree != nullptr); -/***************************************************************************** - * - * Mark the given set of registers as no longer locked. - */ + GenTreeCall* call = nullptr; + var_types treeType; +#if defined(_TARGET_ARM_) + GenTreePutArgSplit* splitArg = nullptr; + GenTreeMultiRegOp* multiReg = nullptr; +#endif -// inline -void RegSet::rsUnlockUsedReg(regMaskTP regMask) -{ - /* Must be currently marked as both used and locked */ + if (tree->IsMultiRegCall()) + { + call = tree->AsCall(); + ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); + treeType = retTypeDesc->GetReturnRegType(regIdx); + } +#ifdef _TARGET_ARM_ + else if (tree->OperIsPutArgSplit()) + { + splitArg = tree->AsPutArgSplit(); + treeType = splitArg->GetRegType(regIdx); + } + else if (tree->OperIsMultiRegOp()) + { + multiReg = tree->AsMultiRegOp(); + treeType = multiReg->GetRegType(regIdx); + } +#endif // _TARGET_ARM_ + else + { + treeType = tree->TypeGet(); + } - assert((rsMaskUsed & regMask) == regMask); - assert((rsMaskLock & regMask) == regMask); - rsMaskLock -= regMask; -} + var_types tempType = Compiler::tmpNormalizeType(treeType); + regMaskTP mask; + bool floatSpill = false; -/***************************************************************************** - * - * Mark the given set of registers as used and locked. It may already have - * been marked as used. - */ + if (isFloatRegType(treeType)) + { + floatSpill = true; + mask = genRegMaskFloat(reg, treeType); + } + else + { + mask = genRegMask(reg); + } -// inline -void RegSet::rsLockReg(regMaskTP regMask, regMaskTP* usedMask) -{ - /* Is it already marked as used? */ + rsNeededSpillReg = true; - regMaskTP used = (rsMaskUsed & regMask); - regMaskTP unused = (regMask & ~used); + // We should only be spilling nodes marked for spill, + // vars should be handled elsewhere, and to prevent + // spilling twice clear GTF_SPILL flag on tree node. + // + // In case of multi-reg call nodes only the spill flag + // associated with the reg is cleared. Spill flag on + // call node should be cleared by the caller of this method. + assert(tree->gtOper != GT_REG_VAR); + assert((tree->gtFlags & GTF_SPILL) != 0); - if (used) - rsLockUsedReg(used); + unsigned regFlags = 0; + if (call != nullptr) + { + regFlags = call->GetRegSpillFlagByIdx(regIdx); + assert((regFlags & GTF_SPILL) != 0); + regFlags &= ~GTF_SPILL; + } +#ifdef _TARGET_ARM_ + else if (splitArg != nullptr) + { + regFlags = splitArg->GetRegSpillFlagByIdx(regIdx); + assert((regFlags & GTF_SPILL) != 0); + regFlags &= ~GTF_SPILL; + } + else if (multiReg != nullptr) + { + regFlags = multiReg->GetRegSpillFlagByIdx(regIdx); + assert((regFlags & GTF_SPILL) != 0); + regFlags &= ~GTF_SPILL; + } +#endif // _TARGET_ARM_ + else + { + assert(!varTypeIsMultiReg(tree)); + tree->gtFlags &= ~GTF_SPILL; + } - if (unused) - rsLockReg(unused); +#if defined(_TARGET_ARM_) + assert(tree->gtRegNum == reg || (call != nullptr && call->GetRegNumByIdx(regIdx) == reg) || + (splitArg != nullptr && splitArg->GetRegNumByIdx(regIdx) == reg) || + (multiReg != nullptr && multiReg->GetRegNumByIdx(regIdx) == reg)); +#else + assert(tree->gtRegNum == reg || (call != nullptr && call->GetRegNumByIdx(regIdx) == reg)); +#endif // !_TARGET_ARM_ - *usedMask = used; -} + // Are any registers free for spillage? + SpillDsc* spill = SpillDsc::alloc(m_rsCompiler, this, tempType); -/***************************************************************************** - * - * Mark the given set of registers as no longer - */ + // Grab a temp to store the spilled value + TempDsc* temp = m_rsCompiler->tmpGetTemp(tempType); + spill->spillTemp = temp; + tempType = temp->tdTempType(); -// inline -void RegSet::rsUnlockReg(regMaskTP regMask, regMaskTP usedMask) -{ - regMaskTP unused = (regMask & ~usedMask); + // Remember what it is we have spilled + spill->spillTree = tree; - if (usedMask) - rsUnlockUsedReg(usedMask); +#ifdef DEBUG + if (m_rsCompiler->verbose) + { + printf("\t\t\t\t\t\t\tThe register %s spilled with ", m_rsCompiler->compRegVarName(reg)); + Compiler::printTreeID(spill->spillTree); + } +#endif - if (unused) - rsUnlockReg(unused); -} -#endif // LEGACY_BACKEND - -#ifdef LEGACY_BACKEND -/***************************************************************************** - * - * Assume all registers contain garbage (called at start of codegen and when - * we encounter a code label). - */ - -// inline -void RegTracker::rsTrackRegClr() -{ - assert(RV_TRASH == 0); - memset(rsRegValues, 0, sizeof(rsRegValues)); -} -#endif // LEGACY_BACKEND - -/***************************************************************************** - * - * Trash the rsRegValues associated with a register - */ - -// inline -void RegTracker::rsTrackRegTrash(regNumber reg) -{ - /* Keep track of which registers we ever touch */ - - regSet->rsSetRegsModified(genRegMask(reg)); - -#ifdef LEGACY_BACKEND - /* Record the new value for the register */ - - rsRegValues[reg].rvdKind = RV_TRASH; -#endif // LEGACY_BACKEND -} - -#ifdef LEGACY_BACKEND -/***************************************************************************** - * - * calls rsTrackRegTrash on the set of registers in regmask - */ - -// inline -void RegTracker::rsTrackRegMaskTrash(regMaskTP regMask) -{ - regMaskTP regBit = 1; - - for (regNumber regNum = REG_FIRST; regNum < REG_COUNT; regNum = REG_NEXT(regNum), regBit <<= 1) - { - if (regBit > regMask) - { - break; - } - - if (regBit & regMask) - { - rsTrackRegTrash(regNum); - } - } -} -#endif // LEGACY_BACKEND - -/*****************************************************************************/ - -// inline -void RegTracker::rsTrackRegIntCns(regNumber reg, ssize_t val) -{ - assert(genIsValidIntReg(reg)); - - /* Keep track of which registers we ever touch */ - - regSet->rsSetRegsModified(genRegMask(reg)); - -#ifdef LEGACY_BACKEND - /* Record the new value for the register */ - - rsRegValues[reg].rvdKind = RV_INT_CNS; - rsRegValues[reg].rvdIntCnsVal = val; -#endif -} - -#ifdef LEGACY_BACKEND -/*****************************************************************************/ - -// inline -void RegTracker::rsTrackRegLclVarLng(regNumber reg, unsigned var, bool low) -{ - assert(genIsValidIntReg(reg)); - - if (compiler->lvaTable[var].lvAddrExposed) - { - return; - } - - /* Keep track of which registers we ever touch */ - - regSet->rsSetRegsModified(genRegMask(reg)); - - /* Record the new value for the register */ - - rsRegValues[reg].rvdKind = (low ? RV_LCL_VAR_LNG_LO : RV_LCL_VAR_LNG_HI); - rsRegValues[reg].rvdLclVarNum = var; -} - -/*****************************************************************************/ - -// inline -bool RegTracker::rsTrackIsLclVarLng(regValKind rvKind) -{ - if (compiler->opts.MinOpts() || compiler->opts.compDbgCode) - { - return false; - } - - if (rvKind == RV_LCL_VAR_LNG_LO || rvKind == RV_LCL_VAR_LNG_HI) - { - return true; - } - else - { - return false; - } -} - -/*****************************************************************************/ - -// inline -void RegTracker::rsTrackRegClsVar(regNumber reg, GenTree* clsVar) -{ - rsTrackRegTrash(reg); -} - -/*****************************************************************************/ - -// inline -void RegTracker::rsTrackRegAssign(GenTree* op1, GenTree* op2) -{ - /* Constant/bitvalue has precedence over local */ - switch (rsRegValues[op2->gtRegNum].rvdKind) - { - case RV_INT_CNS: - break; - - default: - - /* Mark RHS register as containing the value */ - - switch (op1->gtOper) - { - case GT_LCL_VAR: - rsTrackRegLclVar(op2->gtRegNum, op1->gtLclVarCommon.gtLclNum); - break; - case GT_CLS_VAR: - rsTrackRegClsVar(op2->gtRegNum, op1); - break; - default: - break; - } - } -} - -/***************************************************************************** - * - * Given a regmask, find the best regPairNo that can be formed - * or return REG_PAIR_NONE if no register pair can be formed - */ - -regPairNo RegSet::rsFindRegPairNo(regMaskTP regAllowedMask) -{ - regPairNo regPair; - - // Remove any special purpose registers such as SP, EBP, etc... - regMaskTP specialUseMask = (rsMaskResvd | RBM_SPBASE); -#if ETW_EBP_FRAMED - specialUseMask |= RBM_FPBASE; -#else - if (m_rsCompiler->codeGen->isFramePointerUsed()) - specialUseMask |= RBM_FPBASE; -#endif - - regAllowedMask &= ~specialUseMask; - - /* Check if regAllowedMask has zero or one bits set */ - if ((regAllowedMask & (regAllowedMask - 1)) == 0) - { - /* If so we won't be able to find a reg pair */ - return REG_PAIR_NONE; - } - -#ifdef _TARGET_X86_ - if (regAllowedMask & RBM_EAX) - { - /* EAX is available, see if we can pair it with another reg */ - - if (regAllowedMask & RBM_EDX) - { - regPair = REG_PAIR_EAXEDX; - goto RET; - } - if (regAllowedMask & RBM_ECX) - { - regPair = REG_PAIR_EAXECX; - goto RET; - } - if (regAllowedMask & RBM_EBX) - { - regPair = REG_PAIR_EAXEBX; - goto RET; - } - if (regAllowedMask & RBM_ESI) - { - regPair = REG_PAIR_EAXESI; - goto RET; - } - if (regAllowedMask & RBM_EDI) - { - regPair = REG_PAIR_EAXEDI; - goto RET; - } - if (regAllowedMask & RBM_EBP) - { - regPair = REG_PAIR_EAXEBP; - goto RET; - } - } - - if (regAllowedMask & RBM_ECX) - { - /* ECX is available, see if we can pair it with another reg */ - - if (regAllowedMask & RBM_EDX) - { - regPair = REG_PAIR_ECXEDX; - goto RET; - } - if (regAllowedMask & RBM_EBX) - { - regPair = REG_PAIR_ECXEBX; - goto RET; - } - if (regAllowedMask & RBM_ESI) - { - regPair = REG_PAIR_ECXESI; - goto RET; - } - if (regAllowedMask & RBM_EDI) - { - regPair = REG_PAIR_ECXEDI; - goto RET; - } - if (regAllowedMask & RBM_EBP) - { - regPair = REG_PAIR_ECXEBP; - goto RET; - } - } - - if (regAllowedMask & RBM_EDX) - { - /* EDX is available, see if we can pair it with another reg */ - - if (regAllowedMask & RBM_EBX) - { - regPair = REG_PAIR_EDXEBX; - goto RET; - } - if (regAllowedMask & RBM_ESI) - { - regPair = REG_PAIR_EDXESI; - goto RET; - } - if (regAllowedMask & RBM_EDI) - { - regPair = REG_PAIR_EDXEDI; - goto RET; - } - if (regAllowedMask & RBM_EBP) - { - regPair = REG_PAIR_EDXEBP; - goto RET; - } - } - - if (regAllowedMask & RBM_EBX) - { - /* EBX is available, see if we can pair it with another reg */ - - if (regAllowedMask & RBM_ESI) - { - regPair = REG_PAIR_EBXESI; - goto RET; - } - if (regAllowedMask & RBM_EDI) - { - regPair = REG_PAIR_EBXEDI; - goto RET; - } - if (regAllowedMask & RBM_EBP) - { - regPair = REG_PAIR_EBXEBP; - goto RET; - } - } - - if (regAllowedMask & RBM_ESI) - { - /* ESI is available, see if we can pair it with another reg */ - - if (regAllowedMask & RBM_EDI) - { - regPair = REG_PAIR_ESIEDI; - goto RET; - } - if (regAllowedMask & RBM_EBP) - { - regPair = REG_PAIR_EBPESI; - goto RET; - } - } - - if (regAllowedMask & RBM_EDI) - { - /* EDI is available, see if we can pair it with another reg */ - - if (regAllowedMask & RBM_EBP) - { - regPair = REG_PAIR_EBPEDI; - goto RET; - } - } -#endif - -#ifdef _TARGET_ARM_ - // ARM is symmetric, so don't bother to prefer some pairs to others - // - // Iterate the registers in the order specified by rpRegTmpOrder/raRegTmpOrder - - for (unsigned index1 = 0; index1 < REG_TMP_ORDER_COUNT; index1++) - { - regNumber reg1; - if (m_rsCompiler->rpRegAllocDone) - reg1 = raRegTmpOrder[index1]; - else - reg1 = rpRegTmpOrder[index1]; - - regMaskTP reg1Mask = genRegMask(reg1); - - if ((regAllowedMask & reg1Mask) == 0) - continue; - - for (unsigned index2 = index1 + 1; index2 < REG_TMP_ORDER_COUNT; index2++) - { - regNumber reg2; - if (m_rsCompiler->rpRegAllocDone) - reg2 = raRegTmpOrder[index2]; - else - reg2 = rpRegTmpOrder[index2]; - - regMaskTP reg2Mask = genRegMask(reg2); - - if ((regAllowedMask & reg2Mask) == 0) - continue; - - regMaskTP pairMask = genRegMask(reg1) | genRegMask(reg2); - - // if reg1 is larger than reg2 then swap the registers - if (reg1 > reg2) - { - regNumber regT = reg1; - reg1 = reg2; - reg2 = regT; - } - - regPair = gen2regs2pair(reg1, reg2); - return regPair; - } - } -#endif - - assert(!"Unreachable code"); - regPair = REG_PAIR_NONE; - -#ifdef _TARGET_X86_ -RET: -#endif - - return regPair; -} - -#endif // LEGACY_BACKEND - -/*****************************************************************************/ - -RegSet::RegSet(Compiler* compiler, GCInfo& gcInfo) : m_rsCompiler(compiler), m_rsGCInfo(gcInfo) -{ - /* Initialize the spill logic */ - - rsSpillInit(); - - /* Initialize the argument register count */ - // TODO-Cleanup: Consider moving intRegState and floatRegState to RegSet. They used - // to be initialized here, but are now initialized in the CodeGen constructor. - // intRegState.rsCurRegArgNum = 0; - // loatRegState.rsCurRegArgNum = 0; - - rsMaskResvd = RBM_NONE; - -#ifdef LEGACY_BACKEND - rsMaskMult = RBM_NONE; - rsMaskUsed = RBM_NONE; - rsMaskLock = RBM_NONE; -#endif // LEGACY_BACKEND - -#ifdef _TARGET_ARMARCH_ - rsMaskCalleeSaved = RBM_NONE; -#endif // _TARGET_ARMARCH_ - -#ifdef _TARGET_ARM_ - rsMaskPreSpillRegArg = RBM_NONE; - rsMaskPreSpillAlign = RBM_NONE; -#endif - -#ifdef DEBUG - rsModifiedRegsMaskInitialized = false; -#endif // DEBUG -} - -#ifdef LEGACY_BACKEND -/***************************************************************************** - * - * Marks the register that holds the given operand value as 'used'. If 'addr' - * is non-zero, the register is part of a complex address mode that needs to - * be marked if the register is ever spilled. - */ - -void RegSet::rsMarkRegUsed(GenTree* tree, GenTree* addr) -{ - var_types type; - regNumber regNum; - regMaskTP regMask; - - /* The value must be sitting in a register */ - - assert(tree); - assert(tree->InReg()); - - type = tree->TypeGet(); - regNum = tree->gtRegNum; - - if (isFloatRegType(type)) - regMask = genRegMaskFloat(regNum, type); - else - regMask = genRegMask(regNum); - -#ifdef DEBUG - if (m_rsCompiler->verbose) - { - printf("\t\t\t\t\t\t\tThe register %s currently holds ", m_rsCompiler->compRegVarName(regNum)); - Compiler::printTreeID(tree); - if (addr != NULL) - { - printf("/"); - Compiler::printTreeID(addr); - } - else if (tree->gtOper == GT_CNS_INT) - { - if (tree->IsIconHandle()) - printf(" / Handle(0x%08p)", dspPtr(tree->gtIntCon.gtIconVal)); - else - printf(" / Constant(0x%X)", tree->gtIntCon.gtIconVal); - } - printf("\n"); - } -#endif // DEBUG - - /* Remember whether the register holds a pointer */ - - m_rsGCInfo.gcMarkRegPtrVal(regNum, type); - - /* No locked register may ever be marked as free */ - - assert((rsMaskLock & rsRegMaskFree()) == 0); - - /* Is the register used by two different values simultaneously? */ - - if (regMask & rsMaskUsed) - { - /* Save the preceding use information */ - - rsRecMultiReg(regNum, type); - } - - /* Set the register's bit in the 'used' bitset */ - - rsMaskUsed |= regMask; - - /* Remember what values are in what registers, in case we have to spill */ - assert(regNum != REG_SPBASE); - assert(rsUsedTree[regNum] == NULL); - rsUsedTree[regNum] = tree; - assert(rsUsedAddr[regNum] == NULL); - rsUsedAddr[regNum] = addr; -} - -void RegSet::rsMarkArgRegUsedByPromotedFieldArg(GenTree* promotedStructArg, regNumber regNum, bool isGCRef) -{ - regMaskTP regMask; - - /* The value must be sitting in a register */ - - assert(promotedStructArg); - assert(promotedStructArg->TypeGet() == TYP_STRUCT); - - assert(regNum < MAX_REG_ARG); - regMask = genRegMask(regNum); - assert((regMask & RBM_ARG_REGS) != RBM_NONE); - -#ifdef DEBUG - if (m_rsCompiler->verbose) - { - printf("\t\t\t\t\t\t\tThe register %s currently holds ", m_rsCompiler->compRegVarName(regNum)); - Compiler::printTreeID(promotedStructArg); - if (promotedStructArg->gtOper == GT_CNS_INT) - { - if (promotedStructArg->IsIconHandle()) - printf(" / Handle(0x%08p)", dspPtr(promotedStructArg->gtIntCon.gtIconVal)); - else - printf(" / Constant(0x%X)", promotedStructArg->gtIntCon.gtIconVal); - } - printf("\n"); - } -#endif - - /* Remember whether the register holds a pointer */ - - m_rsGCInfo.gcMarkRegPtrVal(regNum, (isGCRef ? TYP_REF : TYP_INT)); - - /* No locked register may ever be marked as free */ - - assert((rsMaskLock & rsRegMaskFree()) == 0); - - /* Is the register used by two different values simultaneously? */ - - if (regMask & rsMaskUsed) - { - /* Save the preceding use information */ - - assert(isValidIntArgReg(regNum)); // We are expecting only integer argument registers here - rsRecMultiReg(regNum, TYP_I_IMPL); - } - - /* Set the register's bit in the 'used' bitset */ - - rsMaskUsed |= regMask; - - /* Remember what values are in what registers, in case we have to spill */ - assert(regNum != REG_SPBASE); - assert(rsUsedTree[regNum] == 0); - rsUsedTree[regNum] = promotedStructArg; -} - -/***************************************************************************** - * - * Marks the register pair that holds the given operand value as 'used'. - */ - -void RegSet::rsMarkRegPairUsed(GenTree* tree) -{ - regNumber regLo; - regNumber regHi; - regPairNo regPair; - regMaskTP regMask; - - /* The value must be sitting in a register */ - - assert(tree); -#if CPU_HAS_FP_SUPPORT - assert(tree->gtType == TYP_LONG); -#else - assert(tree->gtType == TYP_LONG || tree->gtType == TYP_DOUBLE); -#endif - assert(tree->InReg()); - - regPair = tree->gtRegPair; - regMask = genRegPairMask(regPair); - - regLo = genRegPairLo(regPair); - regHi = genRegPairHi(regPair); - -#ifdef DEBUG - if (m_rsCompiler->verbose) - { - printf("\t\t\t\t\t\t\tThe register %s currently holds ", m_rsCompiler->compRegVarName(regLo)); - Compiler::printTreeID(tree); - printf("/lo32\n"); - printf("\t\t\t\t\t\t\tThe register %s currently holds ", m_rsCompiler->compRegVarName(regHi)); - Compiler::printTreeID(tree); - printf("/hi32\n"); - } -#endif - - /* Neither register obviously holds a pointer value */ - - m_rsGCInfo.gcMarkRegSetNpt(regMask); - - /* No locked register may ever be marked as free */ - - assert((rsMaskLock & rsRegMaskFree()) == 0); - - /* Are the registers used by two different values simultaneously? */ - - if (rsMaskUsed & genRegMask(regLo)) - { - /* Save the preceding use information */ - - rsRecMultiReg(regLo, TYP_INT); - } - - if (rsMaskUsed & genRegMask(regHi)) - { - /* Save the preceding use information */ - - rsRecMultiReg(regHi, TYP_INT); - } - - /* Can't mark a register pair more than once as used */ - - // assert((regMask & rsMaskUsed) == 0); - - /* Mark the registers as 'used' */ - - rsMaskUsed |= regMask; - - /* Remember what values are in what registers, in case we have to spill */ - - if (regLo != REG_STK) - { - assert(rsUsedTree[regLo] == 0); - assert(regLo != REG_SPBASE); - rsUsedTree[regLo] = tree; - } - - if (regHi != REG_STK) - { - assert(rsUsedTree[regHi] == 0); - assert(regHi != REG_SPBASE); - rsUsedTree[regHi] = tree; - } -} - -/***************************************************************************** - * - * Returns true if the given tree is currently held in reg. - * Note that reg may by used by multiple trees, in which case we have - * to search rsMultiDesc[reg]. - */ - -bool RegSet::rsIsTreeInReg(regNumber reg, GenTree* tree) -{ - /* First do the trivial check */ - - if (rsUsedTree[reg] == tree) - return true; - - /* If the register is used by multiple trees, we have to search the list - in rsMultiDesc[reg] */ - - if (genRegMask(reg) & rsMaskMult) - { - SpillDsc* multiDesc = rsMultiDesc[reg]; - assert(multiDesc); - - for (/**/; multiDesc; multiDesc = multiDesc->spillNext) - { - if (multiDesc->spillTree == tree) - return true; - - assert((!multiDesc->spillNext) == (!multiDesc->spillMoreMultis)); - } - } - - /* Not found. It must be spilled */ - - return false; -} -#endif // LEGACY_BACKEND - -/***************************************************************************** - * - * Finds the SpillDsc corresponding to 'tree' assuming it was spilled from 'reg'. - */ - -RegSet::SpillDsc* RegSet::rsGetSpillInfo(GenTree* tree, - regNumber reg, - SpillDsc** pPrevDsc -#ifdef LEGACY_BACKEND - , - SpillDsc** pMultiDsc -#endif // LEGACY_BACKEND - ) -{ - /* Normally, trees are unspilled in the order of being spilled due to - the post-order walking of trees during code-gen. However, this will - not be true for something like a GT_ARR_ELEM node */ - CLANG_FORMAT_COMMENT_ANCHOR; - -#ifdef LEGACY_BACKEND - SpillDsc* multi = rsSpillDesc[reg]; -#endif // LEGACY_BACKEND - - SpillDsc* prev; - SpillDsc* dsc; - for (prev = nullptr, dsc = rsSpillDesc[reg]; dsc != nullptr; prev = dsc, dsc = dsc->spillNext) - { -#ifdef LEGACY_BACKEND - if (prev && !prev->spillMoreMultis) - multi = dsc; -#endif // LEGACY_BACKEND - - if (dsc->spillTree == tree) - { - break; - } - } - - if (pPrevDsc) - { - *pPrevDsc = prev; - } -#ifdef LEGACY_BACKEND - if (pMultiDsc) - *pMultiDsc = multi; -#endif // LEGACY_BACKEND - - return dsc; -} - -#ifdef LEGACY_BACKEND -/***************************************************************************** - * - * Mark the register set given by the register mask as not used. - */ - -void RegSet::rsMarkRegFree(regMaskTP regMask) -{ - /* Are we freeing any multi-use registers? */ - - if (regMask & rsMaskMult) - { - rsMultRegFree(regMask); - return; - } - - m_rsGCInfo.gcMarkRegSetNpt(regMask); - - regMaskTP regBit = 1; - - for (regNumber regNum = REG_FIRST; regNum < REG_COUNT; regNum = REG_NEXT(regNum), regBit <<= 1) - { - if (regBit > regMask) - break; - - if (regBit & regMask) - { -#ifdef DEBUG - if (m_rsCompiler->verbose) - { - printf("\t\t\t\t\t\t\tThe register %s no longer holds ", m_rsCompiler->compRegVarName(regNum)); - Compiler::printTreeID(rsUsedTree[regNum]); - if (rsUsedAddr[regNum] != nullptr) - { - Compiler::printTreeID(rsUsedAddr[regNum]); - } - - printf("\n"); - } -#endif - GenTree* usedTree = rsUsedTree[regNum]; - assert(usedTree != NULL); - rsUsedTree[regNum] = NULL; - rsUsedAddr[regNum] = NULL; -#ifdef _TARGET_ARM_ - if (usedTree->TypeGet() == TYP_DOUBLE) - { - regNum = REG_NEXT(regNum); - regBit <<= 1; - - assert(regBit & regMask); - assert(rsUsedTree[regNum] == NULL); - assert(rsUsedAddr[regNum] == NULL); - } -#endif - } - } - - /* Remove the register set from the 'used' set */ - - assert((regMask & rsMaskUsed) == regMask); - rsMaskUsed -= regMask; - - /* No locked register may ever be marked as free */ - - assert((rsMaskLock & rsRegMaskFree()) == 0); -} - -/***************************************************************************** - * - * Free the register from the given tree. If the register holds other tree, - * it will still be marked as used, else it will be completely free. - */ - -void RegSet::rsMarkRegFree(regNumber reg, GenTree* tree) -{ - assert(rsIsTreeInReg(reg, tree)); - regMaskTP regMask = genRegMask(reg); - - /* If the register is not multi-used, it's easy. Just do the default work */ - - if (!(regMask & rsMaskMult)) - { - rsMarkRegFree(regMask); - return; - } - - /* The tree is multi-used. We just have to free it off the given tree but - leave other trees which use the register as they are. The register may - not be multi-used after freeing it from the given tree */ - - /* Is the tree in rsUsedTree[] or in rsMultiDesc[]? - If it is in rsUsedTree[], update rsUsedTree[] */ - - if (rsUsedTree[reg] == tree) - { - rsRmvMultiReg(reg); - return; - } - - /* The tree is in rsMultiDesc[] instead of in rsUsedTree[]. Find the desc - corresponding to the tree and just remove it from there */ - - for (SpillDsc *multiDesc = rsMultiDesc[reg], *prevDesc = NULL; multiDesc; - prevDesc = multiDesc, multiDesc = multiDesc->spillNext) - { - /* If we find the descriptor with the tree we are looking for, - discard it */ - - if (multiDesc->spillTree != tree) - continue; - - if (prevDesc == NULL) - { - /* The very first desc in rsMultiDesc[] matched. If there are - no further descs, then the register is no longer multi-used */ - - if (!multiDesc->spillMoreMultis) - rsMaskMult -= regMask; - - rsMultiDesc[reg] = multiDesc->spillNext; - } - else - { - /* There are a couple of other descs before the match. So the - register is still multi-used. However, we may have to - update spillMoreMultis for the previous desc. */ - - if (!multiDesc->spillMoreMultis) - prevDesc->spillMoreMultis = false; - - prevDesc->spillNext = multiDesc->spillNext; - } - - SpillDsc::freeDsc(this, multiDesc); - -#ifdef DEBUG - if (m_rsCompiler->verbose) - { - printf("\t\t\t\t\t\t\tRegister %s multi-use dec for ", m_rsCompiler->compRegVarName(reg)); - Compiler::printTreeID(tree); - printf(" - now "); - Compiler::printTreeID(rsUsedTree[reg]); - printf(" multMask=" REG_MASK_ALL_FMT "\n", rsMaskMult); - } -#endif - - return; - } - - assert(!"Didn't find the spilled tree in rsMultiDesc[]"); -} - -/***************************************************************************** - * - * Mark the register set given by the register mask as not used; there may - * be some 'multiple-use' registers in the set. - */ - -void RegSet::rsMultRegFree(regMaskTP regMask) -{ - /* Free any multiple-use registers first */ - regMaskTP nonMultMask = regMask & ~rsMaskMult; - regMaskTP myMultMask = regMask & rsMaskMult; - - if (myMultMask) - { - regNumber regNum; - regMaskTP regBit; - - for (regNum = REG_FIRST, regBit = 1; regNum < REG_COUNT; regNum = REG_NEXT(regNum), regBit <<= 1) - { - if (regBit > myMultMask) - break; - - if (regBit & myMultMask) - { - /* Free the multi-use register 'regNum' */ - var_types type = rsRmvMultiReg(regNum); -#ifdef _TARGET_ARM_ - if (genIsValidFloatReg(regNum) && (type == TYP_DOUBLE)) - { - // On ARM32, We skip the second register for a TYP_DOUBLE - regNum = REG_NEXT(regNum); - regBit <<= 1; - } -#endif // _TARGET_ARM_ - } - } - } - - /* If there are any single-use registers, free them */ - - if (nonMultMask) - rsMarkRegFree(nonMultMask); -} - -/***************************************************************************** - * - * Returns the number of registers that are currently free which appear in needReg. - */ - -unsigned RegSet::rsFreeNeededRegCount(regMaskTP needReg) -{ - regMaskTP regNeededFree = rsRegMaskFree() & needReg; - unsigned cntFree = 0; - - /* While some registers are free ... */ - - while (regNeededFree) - { - /* Remove the next register bit and bump the count */ - - regNeededFree -= genFindLowestBit(regNeededFree); - cntFree += 1; - } - - return cntFree; -} -#endif // LEGACY_BACKEND - -/***************************************************************************** - * - * Record the fact that the given register now contains the given local - * variable. Pointers are handled specially since reusing the register - * will extend the lifetime of a pointer register which is not a register - * variable. - */ - -void RegTracker::rsTrackRegLclVar(regNumber reg, unsigned var) -{ - LclVarDsc* varDsc = &compiler->lvaTable[var]; - assert(reg != REG_STK); -#if CPU_HAS_FP_SUPPORT - assert(varTypeIsFloating(varDsc->TypeGet()) == false); -#endif -#ifdef LEGACY_BACKEND - // Kill the register before doing anything in case we take a - // shortcut out of here - rsRegValues[reg].rvdKind = RV_TRASH; -#endif - - if (compiler->lvaTable[var].lvAddrExposed) - { - return; - } - - /* Keep track of which registers we ever touch */ - - regSet->rsSetRegsModified(genRegMask(reg)); - -#ifdef LEGACY_BACKEND - - /* Is the variable a pointer? */ - - if (varTypeIsGC(varDsc->TypeGet())) - { - /* Don't track pointer register vars */ - - if (varDsc->lvRegister) - { - return; - } - - /* Don't track when fully interruptible */ - - if (compiler->genInterruptible) - { - return; - } - } - else if (varDsc->lvNormalizeOnLoad()) - { - return; - } - -#ifdef DEBUG - if (compiler->verbose) - { - printf("\t\t\t\t\t\t\tThe register %s now holds V%02u\n", compiler->compRegVarName(reg), var); - } -#endif - - /* Record the new value for the register. ptr var needed for - * lifetime extension - */ - - rsRegValues[reg].rvdKind = RV_LCL_VAR; - - // If this is a cast of a 64 bit int, then we must have the low 32 bits. - if (genActualType(varDsc->TypeGet()) == TYP_LONG) - { - rsRegValues[reg].rvdKind = RV_LCL_VAR_LNG_LO; - } - - rsRegValues[reg].rvdLclVarNum = var; -#endif // LEGACY_BACKEND -} - -/*****************************************************************************/ - -#ifdef LEGACY_BACKEND -void RegTracker::rsTrackRegSwap(regNumber reg1, regNumber reg2) -{ - RegValDsc tmp; - - tmp = rsRegValues[reg1]; - rsRegValues[reg1] = rsRegValues[reg2]; - rsRegValues[reg2] = tmp; -} -#endif // LEGACY_BACKEND - -void RegTracker::rsTrackRegCopy(regNumber reg1, regNumber reg2) -{ - /* Keep track of which registers we ever touch */ - - assert(reg1 < REG_COUNT); - assert(reg2 < REG_COUNT); - - regSet->rsSetRegsModified(genRegMask(reg1)); - -#ifdef LEGACY_BACKEND - rsRegValues[reg1] = rsRegValues[reg2]; -#endif // LEGACY_BACKEND -} - -#ifdef LEGACY_BACKEND - -/***************************************************************************** - * One of the operands of this complex address mode has been spilled - */ - -void rsAddrSpillOper(GenTree* addr) -{ - if (addr) - { - assert(addr->gtOper == GT_IND || addr->gtOper == GT_ARR_ELEM || addr->gtOper == GT_LEA || - addr->gtOper == GT_CMPXCHG); - - // GTF_SPILLED_OP2 says "both operands have been spilled" - assert((addr->gtFlags & GTF_SPILLED_OP2) == 0); - - if ((addr->gtFlags & GTF_SPILLED_OPER) == 0) - addr->gtFlags |= GTF_SPILLED_OPER; - else - addr->gtFlags |= GTF_SPILLED_OP2; - } -} - -void rsAddrUnspillOper(GenTree* addr) -{ - if (addr) - { - assert(addr->gtOper == GT_IND || addr->gtOper == GT_ARR_ELEM || addr->gtOper == GT_LEA || - addr->gtOper == GT_CMPXCHG); - - assert((addr->gtFlags & GTF_SPILLED_OPER) != 0); - - // Both operands spilled? */ - if ((addr->gtFlags & GTF_SPILLED_OP2) != 0) - addr->gtFlags &= ~GTF_SPILLED_OP2; - else - addr->gtFlags &= ~GTF_SPILLED_OPER; - } -} - -void RegSet::rsSpillRegIfUsed(regNumber reg) -{ - if (rsMaskUsed & genRegMask(reg)) - { - rsSpillReg(reg); - } -} - -#endif // LEGACY_BACKEND - -//------------------------------------------------------------ -// rsSpillTree: Spill the tree held in 'reg'. -// -// Arguments: -// reg - Register of tree node that is to be spilled -// tree - GenTree node that is being spilled -// regIdx - Register index identifying the specific result -// register of a multi-reg call node. For single-reg -// producing tree nodes its value is zero. -// -// Return Value: -// None. -// -// Assumption: -// RyuJIT backend specific: in case of multi-reg call nodes, GTF_SPILL -// flag associated with the reg that is being spilled is cleared. The -// caller of this method is expected to clear GTF_SPILL flag on call -// node after all of its registers marked for spilling are spilled. -// -void RegSet::rsSpillTree(regNumber reg, GenTree* tree, unsigned regIdx /* =0 */) -{ - assert(tree != nullptr); - - GenTreeCall* call = nullptr; - var_types treeType; -#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_) - GenTreePutArgSplit* splitArg = nullptr; - GenTreeMultiRegOp* multiReg = nullptr; -#endif - -#ifndef LEGACY_BACKEND - if (tree->IsMultiRegCall()) - { - call = tree->AsCall(); - ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); - treeType = retTypeDesc->GetReturnRegType(regIdx); - } -#ifdef _TARGET_ARM_ - else if (tree->OperIsPutArgSplit()) - { - splitArg = tree->AsPutArgSplit(); - treeType = splitArg->GetRegType(regIdx); - } - else if (tree->OperIsMultiRegOp()) - { - multiReg = tree->AsMultiRegOp(); - treeType = multiReg->GetRegType(regIdx); - } -#endif // _TARGET_ARM_ - else -#endif // !LEGACY_BACKEND - { - treeType = tree->TypeGet(); - } - - var_types tempType = Compiler::tmpNormalizeType(treeType); - regMaskTP mask; - bool floatSpill = false; - - if (isFloatRegType(treeType)) - { - floatSpill = true; - mask = genRegMaskFloat(reg, treeType); - } - else - { - mask = genRegMask(reg); - } - - rsNeededSpillReg = true; - -#ifdef LEGACY_BACKEND - // The register we're spilling must be used but not locked - // or an enregistered variable. - - assert((mask & rsMaskUsed) == mask); - assert((mask & rsMaskLock) == 0); - assert((mask & rsMaskVars) == 0); -#endif // LEGACY_BACKEND - -#ifndef LEGACY_BACKEND - // We should only be spilling nodes marked for spill, - // vars should be handled elsewhere, and to prevent - // spilling twice clear GTF_SPILL flag on tree node. - // - // In case of multi-reg call nodes only the spill flag - // associated with the reg is cleared. Spill flag on - // call node should be cleared by the caller of this method. - assert(tree->gtOper != GT_REG_VAR); - assert((tree->gtFlags & GTF_SPILL) != 0); - - unsigned regFlags = 0; - if (call != nullptr) - { - regFlags = call->GetRegSpillFlagByIdx(regIdx); - assert((regFlags & GTF_SPILL) != 0); - regFlags &= ~GTF_SPILL; - } -#ifdef _TARGET_ARM_ - else if (splitArg != nullptr) - { - regFlags = splitArg->GetRegSpillFlagByIdx(regIdx); - assert((regFlags & GTF_SPILL) != 0); - regFlags &= ~GTF_SPILL; - } - else if (multiReg != nullptr) - { - regFlags = multiReg->GetRegSpillFlagByIdx(regIdx); - assert((regFlags & GTF_SPILL) != 0); - regFlags &= ~GTF_SPILL; - } -#endif // _TARGET_ARM_ - else - { - assert(!varTypeIsMultiReg(tree)); - tree->gtFlags &= ~GTF_SPILL; - } -#endif // !LEGACY_BACKEND - -#if CPU_LONG_USES_REGPAIR - // Are we spilling a part of a register pair? - if (treeType == TYP_LONG) - { - tempType = TYP_I_IMPL; - assert(genRegPairLo(tree->gtRegPair) == reg || genRegPairHi(tree->gtRegPair) == reg); - } - else - { - assert(tree->InReg()); - assert(tree->gtRegNum == reg); - } -#elif defined(_TARGET_ARM_) - assert(tree->gtRegNum == reg || (call != nullptr && call->GetRegNumByIdx(regIdx) == reg) || - (splitArg != nullptr && splitArg->GetRegNumByIdx(regIdx) == reg) || - (multiReg != nullptr && multiReg->GetRegNumByIdx(regIdx) == reg)); -#else - assert(tree->gtRegNum == reg || (call != nullptr && call->GetRegNumByIdx(regIdx) == reg)); -#endif // !CPU_LONG_USES_REGPAIR && !_TARGET_ARM_ - - // Are any registers free for spillage? - SpillDsc* spill = SpillDsc::alloc(m_rsCompiler, this, tempType); - - // Grab a temp to store the spilled value - TempDsc* temp = m_rsCompiler->tmpGetTemp(tempType); - spill->spillTemp = temp; - tempType = temp->tdTempType(); - - // Remember what it is we have spilled - spill->spillTree = tree; -#ifdef LEGACY_BACKEND - spill->spillAddr = rsUsedAddr[reg]; -#endif // LEGACY_BACKEND - -#ifdef DEBUG - if (m_rsCompiler->verbose) - { - printf("\t\t\t\t\t\t\tThe register %s spilled with ", m_rsCompiler->compRegVarName(reg)); - Compiler::printTreeID(spill->spillTree); -#ifdef LEGACY_BACKEND - if (spill->spillAddr != nullptr) - { - Compiler::printTreeID(spill->spillAddr); - } -#endif // LEGACY_BACKEND - } -#endif - -#ifdef LEGACY_BACKEND - // Is the register part of a complex address mode? - rsAddrSpillOper(rsUsedAddr[reg]); -#endif // LEGACY_BACKEND - - // 'lastDsc' is 'spill' for simple cases, and will point to the last - // multi-use descriptor if 'reg' is being multi-used - SpillDsc* lastDsc = spill; - -#ifdef LEGACY_BACKEND - if ((rsMaskMult & mask) == 0) - { - spill->spillMoreMultis = false; - } - else - { - // The register is being multi-used and will have entries in - // rsMultiDesc[reg]. Spill all of them (ie. move them to - // rsSpillDesc[reg]). - // When we unspill the reg, they will all be moved back to - // rsMultiDesc[]. - - spill->spillMoreMultis = true; - - SpillDsc* nextDsc = rsMultiDesc[reg]; - - do - { - assert(nextDsc != nullptr); - - // Is this multi-use part of a complex address mode? - rsAddrSpillOper(nextDsc->spillAddr); - - // Mark the tree node as having been spilled - rsMarkSpill(nextDsc->spillTree, reg); - - // lastDsc points to the last of the multi-spill descrs for 'reg' - nextDsc->spillTemp = temp; - -#ifdef DEBUG - if (m_rsCompiler->verbose) - { - printf(", "); - Compiler::printTreeID(nextDsc->spillTree); - printf("/"); - Compiler::printTreeID(nextDsc->spillAddr); - } -#endif - - lastDsc->spillNext = nextDsc; - lastDsc = nextDsc; - - nextDsc = nextDsc->spillNext; - } while (lastDsc->spillMoreMultis); - - rsMultiDesc[reg] = nextDsc; - - // 'reg' is no longer considered to be multi-used. We will set this - // mask again when this value gets unspilled - rsMaskMult &= ~mask; - } -#endif // LEGACY_BACKEND - - // Insert the spill descriptor(s) in the list - lastDsc->spillNext = rsSpillDesc[reg]; - rsSpillDesc[reg] = spill; - -#ifdef DEBUG - if (m_rsCompiler->verbose) - { - printf("\n"); - } -#endif - - // Generate the code to spill the register - var_types storeType = floatSpill ? treeType : tempType; - - m_rsCompiler->codeGen->spillReg(storeType, temp, reg); - - // Mark the tree node as having been spilled - rsMarkSpill(tree, reg); - -#ifdef LEGACY_BACKEND - // The register is now free - rsMarkRegFree(mask); -#else - // In case of multi-reg call node also mark the specific - // result reg as spilled. - if (call != nullptr) - { - regFlags |= GTF_SPILLED; - call->SetRegSpillFlagByIdx(regFlags, regIdx); - } -#ifdef _TARGET_ARM_ - else if (splitArg != nullptr) - { - regFlags |= GTF_SPILLED; - splitArg->SetRegSpillFlagByIdx(regFlags, regIdx); - } - else if (multiReg != nullptr) - { - regFlags |= GTF_SPILLED; - multiReg->SetRegSpillFlagByIdx(regFlags, regIdx); - } -#endif // _TARGET_ARM_ -#endif //! LEGACY_BACKEND -} - -#if defined(_TARGET_X86_) && !FEATURE_STACK_FP_X87 -/***************************************************************************** -* -* Spill the top of the FP x87 stack. -*/ -void RegSet::rsSpillFPStack(GenTreeCall* call) -{ - SpillDsc* spill; - TempDsc* temp; - var_types treeType = call->TypeGet(); - - spill = SpillDsc::alloc(m_rsCompiler, this, treeType); - - /* Grab a temp to store the spilled value */ - - spill->spillTemp = temp = m_rsCompiler->tmpGetTemp(treeType); - - /* Remember what it is we have spilled */ - - spill->spillTree = call; - SpillDsc* lastDsc = spill; - - regNumber reg = call->gtRegNum; - lastDsc->spillNext = rsSpillDesc[reg]; - rsSpillDesc[reg] = spill; - -#ifdef DEBUG - if (m_rsCompiler->verbose) - printf("\n"); -#endif - // m_rsCompiler->codeGen->inst_FS_ST(INS_fstp, emitActualTypeSize(treeType), temp, 0); - m_rsCompiler->codeGen->getEmitter()->emitIns_S(INS_fstp, emitActualTypeSize(treeType), temp->tdTempNum(), 0); - - /* Mark the tree node as having been spilled */ - - rsMarkSpill(call, reg); -} -#endif // defined(_TARGET_X86_) && !FEATURE_STACK_FP_X87 - -#ifdef LEGACY_BACKEND - -/***************************************************************************** - * - * Spill the given register (which we assume to be currently marked as used). - */ - -void RegSet::rsSpillReg(regNumber reg) -{ - /* We must know the value in the register that we are spilling */ - GenTree* tree = rsUsedTree[reg]; - -#ifdef _TARGET_ARM_ - if (tree == NULL && genIsValidFloatReg(reg) && !genIsValidDoubleReg(reg)) - { - reg = REG_PREV(reg); - assert(rsUsedTree[reg]); - assert(rsUsedTree[reg]->TypeGet() == TYP_DOUBLE); - tree = rsUsedTree[reg]; - } -#endif - - rsSpillTree(reg, tree); - - /* The register no longer holds its original value */ - - rsUsedTree[reg] = NULL; -} - -/***************************************************************************** - * - * Spill all registers in 'regMask' that are currently marked as used. - */ - -void RegSet::rsSpillRegs(regMaskTP regMask) -{ - /* The registers we're spilling must not be locked, - or enregistered variables */ - - assert((regMask & rsMaskLock) == 0); - assert((regMask & rsMaskVars) == 0); - - /* Only spill what's currently marked as used */ - - regMask &= rsMaskUsed; - assert(regMask); - - regNumber regNum; - regMaskTP regBit; - - for (regNum = REG_FIRST, regBit = 1; regNum < REG_COUNT; regNum = REG_NEXT(regNum), regBit <<= 1) - { - if (regMask & regBit) - { - rsSpillReg(regNum); - - regMask &= rsMaskUsed; - - if (!regMask) - break; - } - } -} - -/***************************************************************************** - * - * The following table determines the order in which registers are considered - * for internal tree temps to live in - */ - -#ifdef LEGACY_BACKEND -extern const regNumber raRegTmpOrder[] = {REG_TMP_ORDER}; -extern const regNumber rpRegTmpOrder[] = {REG_PREDICT_ORDER}; -#if FEATURE_FP_REGALLOC -extern const regNumber raRegFltTmpOrder[] = {REG_FLT_TMP_ORDER}; -#endif -#endif // LEGACY_BACKEND - -/***************************************************************************** - * - * Choose a register from the given set in the preferred order (see above); - * if no registers are in the set return REG_STK. - */ - -regNumber RegSet::rsPickRegInTmpOrder(regMaskTP regMask) -{ - if (regMask == RBM_NONE) - return REG_STK; - - bool firstPass = true; - regMaskTP avoidMask = - ~rsGetModifiedRegsMask() & RBM_CALLEE_SAVED; // We want to avoid using any new callee saved register - - while (true) - { - /* Iterate the registers in the order specified by raRegTmpOrder */ - - for (unsigned index = 0; index < REG_TMP_ORDER_COUNT; index++) - { - regNumber candidateReg = raRegTmpOrder[index]; - regMaskTP candidateMask = genRegMask(candidateReg); - - // For a FP base frame, don't use FP register. - if (m_rsCompiler->codeGen->isFramePointerUsed() && (candidateMask == RBM_FPBASE)) - continue; - - // For the first pass avoid selecting a never used register when there are other registers available - if (firstPass && ((candidateMask & avoidMask) != 0)) - continue; - - if (regMask & candidateMask) - return candidateReg; - } - - if (firstPass == true) - firstPass = false; // OK, now we are willing to select a never used register - else - break; - } - - return REG_STK; -} - -/***************************************************************************** - * Choose a register from the 'regMask' set and return it. If no registers in - * the set are currently free, one of them will be spilled (even if other - * registers - not in the set - are currently free). - * - * If you don't require a register from a particular set, you should use rsPickReg() instead. - * - * rsModifiedRegsMask is modified to include the returned register. - */ - -regNumber RegSet::rsGrabReg(regMaskTP regMask) -{ - regMaskTP OKmask; - regNumber regNum; - regMaskTP regBit; - - assert(regMask); - regMask &= ~rsMaskLock; - assert(regMask); - - /* See if one of the desired registers happens to be free */ - - OKmask = regMask & rsRegMaskFree(); - - regNum = rsPickRegInTmpOrder(OKmask); - if (REG_STK != regNum) - { - goto RET; - } - - /* We'll have to spill one of the registers in 'regMask' */ - - OKmask = regMask & rsRegMaskCanGrab(); - assert(OKmask); - - for (regNum = REG_FIRST, regBit = 1; (regBit & OKmask) == 0; regNum = REG_NEXT(regNum), regBit <<= 1) - { - if (regNum >= REG_COUNT) - { - assert(!"no register to grab!"); - NO_WAY("Could not grab a register, Predictor should have prevented this!"); - } - } - - /* This will be the victim -- spill it */ - rsSpillReg(regNum); - - /* Make sure we did find a register to spill */ - assert(genIsValidReg(regNum)); - -RET: - /* Keep track of which registers we ever touch */ - rsSetRegsModified(genRegMask(regNum)); - return regNum; -} - -/***************************************************************************** - * Find a register to use and return it, spilling if necessary. - * - * Look for a register in the following order: First, try and find a free register - * in 'regBest' (if 'regBest' is RBM_NONE, skip this step). Second, try to find a - * free register in 'regMask' (if 'regMask' is RBM_NONE, skip this step). Note that - * 'regBest' doesn't need to be a subset of 'regMask'. Third, find any free - * register. Fourth, spill a register. The register to spill will be in 'regMask', - * if 'regMask' is not RBM_NONE. - * - * Note that 'regMask' and 'regBest' are purely recommendations, and can be ignored; - * the caller can't expect that the returned register will be in those sets. In - * particular, under register stress, we specifically will pick registers not in - * these sets to ensure that callers don't require a register from those sets - * (and to ensure callers can handle the spilling that might ensue). - * - * Calling rsPickReg() with the default arguments (which sets 'regMask' and 'regBest' to RBM_NONE) - * is equivalent to calling rsGrabReg(rsRegMaskFree()). - * - * rsModifiedRegsMask is modified to include the returned register. - */ - -regNumber RegSet::rsPickReg(regMaskTP regMask, regMaskTP regBest) -{ - regNumber regNum; - regMaskTP spillMask; - regMaskTP canGrabMask; - -#ifdef DEBUG - if (rsStressRegs() >= 1) - { - /* 'regMask' is purely a recommendation, and callers should be - able to handle the case where it is not satisfied. - The logic here tries to return ~regMask to check that all callers - are prepared to handle such a case */ - - regMaskTP badRegs = rsMaskMult & rsRegMaskCanGrab(); - - badRegs = rsUseIfZero(badRegs, rsMaskUsed & rsRegMaskCanGrab()); - badRegs = rsUseIfZero(badRegs, rsRegMaskCanGrab()); - badRegs = rsExcludeHint(badRegs, regMask); - - assert(badRegs != RBM_NONE); - - return rsGrabReg(badRegs); - } - -#endif - - regMaskTP freeMask = rsRegMaskFree(); - -AGAIN: - - /* By default we'd prefer to accept all available registers */ - - regMaskTP OKmask = freeMask; - - // OKmask = rsNarrowHint(OKmask, rsUselessRegs()); - - /* Is there a 'best' register set? */ - - if (regBest) - { - OKmask &= regBest; - if (OKmask) - goto TRY_REG; - else - goto TRY_ALL; - } - - /* Was a register set recommended by the caller? */ - - if (regMask) - { - OKmask &= regMask; - if (!OKmask) - goto TRY_ALL; - } - -TRY_REG: - - /* Iterate the registers in the order specified by raRegTmpOrder */ - - regNum = rsPickRegInTmpOrder(OKmask); - if (REG_STK != regNum) - { - goto RET; - } - -TRY_ALL: - - /* Were we considering 'regBest' ? */ - - if (regBest) - { - /* 'regBest' is no good -- ignore it and try 'regMask' instead */ - - regBest = RBM_NONE; - goto AGAIN; - } - - /* Now let's consider all available registers */ - - /* Were we limited in our consideration? */ - - if (!regMask) - { - /* We need to spill one of the free registers */ - - spillMask = freeMask; - } - else - { - /* Did we not consider all free registers? */ - - if ((regMask & freeMask) != freeMask) - { - /* The recommended regset didn't work, so try all available regs */ - - regNum = rsPickRegInTmpOrder(freeMask); - if (REG_STK != regNum) - goto RET; - } - - /* If we're going to spill, might as well go for the right one */ - - spillMask = regMask; - } - - /* Make sure we can spill some register. */ - - canGrabMask = rsRegMaskCanGrab(); - if ((spillMask & canGrabMask) == 0) - spillMask = canGrabMask; - - assert(spillMask); - - /* We have no choice but to spill one of the regs */ - - return rsGrabReg(spillMask); - -RET: - - rsSetRegsModified(genRegMask(regNum)); - return regNum; -} - -#endif // LEGACY_BACKEND - -/***************************************************************************** - * - * Get the temp that was spilled from the given register (and free its - * spill descriptor while we're at it). Returns the temp (i.e. local var) - */ - -TempDsc* RegSet::rsGetSpillTempWord(regNumber reg, SpillDsc* dsc, SpillDsc* prevDsc) -{ - assert((prevDsc == nullptr) || (prevDsc->spillNext == dsc)); - -#ifdef LEGACY_BACKEND - /* Is dsc the last of a set of multi-used values */ - - if (prevDsc && prevDsc->spillMoreMultis && !dsc->spillMoreMultis) - prevDsc->spillMoreMultis = false; -#endif // LEGACY_BACKEND - - /* Remove this spill entry from the register's list */ - - (prevDsc ? prevDsc->spillNext : rsSpillDesc[reg]) = dsc->spillNext; - - /* Remember which temp the value is in */ - - TempDsc* temp = dsc->spillTemp; - - SpillDsc::freeDsc(this, dsc); - - /* return the temp variable */ - - return temp; -} - -#ifdef LEGACY_BACKEND -/***************************************************************************** - * - * Reload the value that was spilled from the given register (and free its - * spill descriptor while we're at it). Returns the new register (which will - * be a member of 'needReg' if that value is non-zero). - * - * 'willKeepNewReg' indicates if the caller intends to mark newReg as used. - * If not, then we can't unspill the other multi-used descriptor (if any). - * Instead, we will just hold on to the temp and unspill them - * again as needed. - */ - -regNumber RegSet::rsUnspillOneReg(GenTree* tree, regNumber oldReg, KeepReg willKeepNewReg, regMaskTP needReg) -{ - /* Was oldReg multi-used when it was spilled? */ - - SpillDsc *prevDsc, *multiDsc; - SpillDsc* spillDsc = rsGetSpillInfo(tree, oldReg, &prevDsc, &multiDsc); - noway_assert((spillDsc != NULL) && (multiDsc != NULL)); - - bool multiUsed = multiDsc->spillMoreMultis; - - /* We will use multiDsc to walk the rest of the spill list (if it's - multiUsed). As we're going to remove spillDsc from the multiDsc - list in the rsGetSpillTempWord() call we have to take care of the - case where multiDsc==spillDsc. We will set multiDsc as spillDsc->spillNext */ - if (multiUsed && multiDsc == spillDsc) - { - assert(spillDsc->spillNext); - multiDsc = spillDsc->spillNext; - } - - /* Get the temp and free the spill-descriptor */ - - TempDsc* temp = rsGetSpillTempWord(oldReg, spillDsc, prevDsc); - - // Pick a new home for the value: - // This must be a register matching the 'needReg' mask, if it is non-zero. - // Additionally, if 'oldReg' is in 'needMask' and it is free we will select oldReg. - // Also note that the rsGrabReg() call below may cause the chosen register to be spilled. - // - regMaskTP prefMask; - regMaskTP freeMask; - regNumber newReg; - var_types regType; - var_types loadType; - - bool floatUnspill = false; - -#if FEATURE_FP_REGALLOC - floatUnspill = genIsValidFloatReg(oldReg); -#endif - - if (floatUnspill) - { - if (temp->tdTempType() == TYP_DOUBLE) - regType = TYP_DOUBLE; - else - regType = TYP_FLOAT; - loadType = regType; - prefMask = genRegMaskFloat(oldReg, regType); - freeMask = RegFreeFloat(); - } - else - { - regType = TYP_I_IMPL; - loadType = temp->tdTempType(); - prefMask = genRegMask(oldReg); - freeMask = rsRegMaskFree(); - } - - if ((((prefMask & needReg) != 0) || (needReg == 0)) && ((prefMask & freeMask) != 0)) - { - needReg = prefMask; - } - - if (floatUnspill) - { - RegisterPreference pref(RBM_ALLFLOAT, needReg); - newReg = PickRegFloat(regType, &pref, true); - } - else - { - newReg = rsGrabReg(rsUseIfZero(needReg, RBM_ALLINT)); - } - - m_rsCompiler->codeGen->trashReg(newReg); - - /* Reload the value from the saved location into the new register */ - - m_rsCompiler->codeGen->reloadReg(loadType, temp, newReg); - - if (multiUsed && (willKeepNewReg == KEEP_REG)) - { - /* We will unspill all the other multi-use trees if the register - is going to be marked as used. If it is not going to be marked - as used, we will have a problem if the new register gets spilled - again. - */ - - /* We don't do the extra unspilling for complex address modes, - since someone up the call chain may have a different idea about - what registers are used to form the complex address mode (the - addrReg return value from genMakeAddressable). - - Also, it is not safe to unspill all the multi-uses with a TYP_LONG. - - Finally, it is not safe to unspill into a different register, because - the caller of genMakeAddressable caches the addrReg return value - (register mask), but when unspilling into a different register it's - not possible to inform the caller that addrReg is now different. - See bug #89946 for an example of this. There is an assert for this - in rsMarkRegFree via genDoneAddressable. - */ - - for (SpillDsc* dsc = multiDsc; /**/; dsc = dsc->spillNext) - { - if ((oldReg != newReg) || (dsc->spillAddr != NULL) || (dsc->spillTree->gtType == TYP_LONG)) - { - return newReg; - } - - if (!dsc->spillMoreMultis) - { - /* All the remaining multi-uses are fine. We will now - unspill them all */ - break; - } - } - - bool bFound = false; - SpillDsc* pDsc; - SpillDsc** ppPrev; - - for (pDsc = rsSpillDesc[oldReg], ppPrev = &rsSpillDesc[oldReg];; pDsc = pDsc->spillNext) - { - if (pDsc == multiDsc) - { - // We've found the sequence we were searching for - bFound = true; - } - - if (bFound) - { - rsAddrUnspillOper(pDsc->spillAddr); - - // Mark the tree node as having been unspilled into newReg - rsMarkUnspill(pDsc->spillTree, newReg); - } - - if (!pDsc->spillMoreMultis) - { - if (bFound) - { - // End of sequence - - // We link remaining sides of list - *ppPrev = pDsc->spillNext; - - // Exit walk - break; - } - else - { - ppPrev = &(pDsc->spillNext); - } - } - } - - /* pDsc points to the last multi-used descriptor from the spill-list - for the current value (pDsc->spillMoreMultis == false) */ - - pDsc->spillNext = rsMultiDesc[newReg]; - rsMultiDesc[newReg] = multiDsc; - - if (floatUnspill) - rsMaskMult |= genRegMaskFloat(newReg, regType); - else - rsMaskMult |= genRegMask(newReg); - } - - if (!multiUsed || (willKeepNewReg == KEEP_REG)) - { - // Free the temp, it's no longer used. - // For multi-used regs that aren't (willKeepNewReg == KEEP_REG), we didn't unspill everything, so - // we need to leave the temp for future unspilling. - m_rsCompiler->tmpRlsTemp(temp); - } - - return newReg; -} -#endif // LEGACY_BACKEND - -//--------------------------------------------------------------------- -// rsUnspillInPlace: The given tree operand has been spilled; just mark -// it as unspilled so that we can use it as "normal" local. -// -// Arguments: -// tree - GenTree that needs to be marked as unspilled. -// oldReg - reg of tree that was spilled. -// -// Return Value: -// None. -// -// Assumptions: -// 1. It is the responsibility of the caller to free the spill temp. -// 2. RyuJIT backend specific: In case of multi-reg call node -// GTF_SPILLED flag associated with reg is cleared. It is the -// responsibility of caller to clear GTF_SPILLED flag on call node -// itself after ensuring there are no outstanding regs in GTF_SPILLED -// state. -// -TempDsc* RegSet::rsUnspillInPlace(GenTree* tree, regNumber oldReg, unsigned regIdx /* =0 */) -{ - assert(!isRegPairType(tree->gtType)); - - // Get the tree's SpillDsc - SpillDsc* prevDsc; - SpillDsc* spillDsc = rsGetSpillInfo(tree, oldReg, &prevDsc); - PREFIX_ASSUME(spillDsc != nullptr); - - // Get the temp - TempDsc* temp = rsGetSpillTempWord(oldReg, spillDsc, prevDsc); - - // The value is now unspilled - if (tree->IsMultiRegCall()) - { - GenTreeCall* call = tree->AsCall(); - unsigned flags = call->GetRegSpillFlagByIdx(regIdx); - flags &= ~GTF_SPILLED; - call->SetRegSpillFlagByIdx(flags, regIdx); - } -#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_) - else if (tree->OperIsPutArgSplit()) - { - GenTreePutArgSplit* splitArg = tree->AsPutArgSplit(); - unsigned flags = splitArg->GetRegSpillFlagByIdx(regIdx); - flags &= ~GTF_SPILLED; - splitArg->SetRegSpillFlagByIdx(flags, regIdx); - } - else if (tree->OperIsMultiRegOp()) - { - GenTreeMultiRegOp* multiReg = tree->AsMultiRegOp(); - unsigned flags = multiReg->GetRegSpillFlagByIdx(regIdx); - flags &= ~GTF_SPILLED; - multiReg->SetRegSpillFlagByIdx(flags, regIdx); - } -#endif // !LEGACY_BACKEND && _TARGET_ARM_ - else - { - tree->gtFlags &= ~GTF_SPILLED; - } - -#ifdef DEBUG - if (m_rsCompiler->verbose) - { - printf("\t\t\t\t\t\t\tTree-Node marked unspilled from "); - Compiler::printTreeID(tree); - printf("\n"); - } -#endif - - return temp; -} - -#ifdef LEGACY_BACKEND - -/***************************************************************************** - * - * The given tree operand has been spilled; reload it into a register that - * is in 'needReg' (if 'needReg' is RBM_NONE, any register will do). If 'keepReg' - * is set to KEEP_REG, we'll mark the new register as used. - */ - -void RegSet::rsUnspillReg(GenTree* tree, regMaskTP needReg, KeepReg keepReg) -{ - assert(!isRegPairType(tree->gtType)); // use rsUnspillRegPair() - regNumber oldReg = tree->gtRegNum; - - /* Get the SpillDsc for the tree */ - - SpillDsc* spillDsc = rsGetSpillInfo(tree, oldReg); - PREFIX_ASSUME(spillDsc != NULL); - - /* Before spillDsc is stomped on by rsUnspillOneReg(), note whether - * the reg was part of an address mode - */ - - GenTree* unspillAddr = spillDsc->spillAddr; - - /* Pick a new home for the value */ - - regNumber newReg = rsUnspillOneReg(tree, oldReg, keepReg, needReg); - - /* Mark the tree node as having been unspilled into newReg */ - - rsMarkUnspill(tree, newReg); - - // If this reg was part of a complex address mode, need to clear this flag which - // tells address mode building that a component has been spilled + // 'lastDsc' is 'spill' for simple cases, and will point to the last + // multi-use descriptor if 'reg' is being multi-used + SpillDsc* lastDsc = spill; - rsAddrUnspillOper(unspillAddr); + // Insert the spill descriptor(s) in the list + lastDsc->spillNext = rsSpillDesc[reg]; + rsSpillDesc[reg] = spill; #ifdef DEBUG if (m_rsCompiler->verbose) { - printf("\t\t\t\t\t\t\tThe register %s unspilled from ", m_rsCompiler->compRegVarName(newReg)); - Compiler::printTreeID(tree); printf("\n"); } #endif - /* Mark the new value as used, if the caller desires so */ - - if (keepReg == KEEP_REG) - rsMarkRegUsed(tree, unspillAddr); -} -#endif // LEGACY_BACKEND - -void RegSet::rsMarkSpill(GenTree* tree, regNumber reg) -{ -#ifdef LEGACY_BACKEND - tree->SetInReg(false); -#endif - tree->gtFlags |= GTF_SPILLED; -} - -#ifdef LEGACY_BACKEND - -void RegSet::rsMarkUnspill(GenTree* tree, regNumber reg) -{ -#ifndef _TARGET_AMD64_ - assert(tree->gtType != TYP_LONG); -#endif // _TARGET_AMD64_ - - tree->gtFlags &= ~GTF_SPILLED; - tree->gtRegNum = reg; - tree->SetInReg(); -} - -/***************************************************************************** - * - * Choose a register pair from the given set (note: only registers in the - * given set will be considered). - */ - -regPairNo RegSet::rsGrabRegPair(regMaskTP regMask) -{ - regPairNo regPair; - regMaskTP OKmask; - regNumber reg1; - regNumber reg2; - - assert(regMask); - regMask &= ~rsMaskLock; - assert(regMask); - - /* We'd prefer to choose a free register pair if possible */ - - OKmask = regMask & rsRegMaskFree(); - - /* Any takers in the recommended/free set? */ - - regPair = rsFindRegPairNo(OKmask); - - if (regPair != REG_PAIR_NONE) - { - // The normal early exit - - /* Keep track of which registers we ever touch */ - rsSetRegsModified(genRegPairMask(regPair)); - - return regPair; - } - - /* We have no choice but to spill one or two used regs */ - - if (OKmask) - { - /* One (and only one) register is free and acceptable - grab it */ - - assert(genMaxOneBit(OKmask)); - - for (reg1 = REG_INT_FIRST; reg1 <= REG_INT_LAST; reg1 = REG_NEXT(reg1)) - { - if (OKmask & genRegMask(reg1)) - break; - } - assert(OKmask & genRegMask(reg1)); - } - else - { - /* No register is free and acceptable - we'll have to spill two */ - - reg1 = rsGrabReg(regMask); - } - - /* Temporarily lock the first register so it doesn't go away */ - - rsLockReg(genRegMask(reg1)); - - /* Now grab another register */ - - reg2 = rsGrabReg(regMask); - - /* We can unlock the first register now */ - - rsUnlockReg(genRegMask(reg1)); - - /* Convert the two register numbers into a pair */ - - if (reg1 < reg2) - regPair = gen2regs2pair(reg1, reg2); - else - regPair = gen2regs2pair(reg2, reg1); - - return regPair; -} - -/***************************************************************************** - * - * Choose a register pair from the given set (if non-zero) or from the set of - * currently available registers (if 'regMask' is zero). - */ - -regPairNo RegSet::rsPickRegPair(regMaskTP regMask) -{ - regMaskTP OKmask; - regPairNo regPair; - - int repeat = 0; - - /* By default we'd prefer to accept all available registers */ - - OKmask = rsRegMaskFree(); - - if (regMask) - { - /* A register set was recommended by the caller */ - - OKmask &= regMask; - } - -AGAIN: - - regPair = rsFindRegPairNo(OKmask); - - if (regPair != REG_PAIR_NONE) - { - return regPair; // Normal early exit - } - - regMaskTP freeMask; - regMaskTP spillMask; - - /* Now let's consider all available registers */ - - freeMask = rsRegMaskFree(); - - /* Were we limited in our consideration? */ - - if (!regMask) - { - /* We need to spill two of the free registers */ - - spillMask = freeMask; - } - else - { - /* Did we not consider all free registers? */ - - if ((regMask & freeMask) != freeMask && repeat == 0) - { - /* The recommended regset didn't work, so try all available regs */ - - OKmask = freeMask; - repeat++; - goto AGAIN; - } - - /* If we're going to spill, might as well go for the right one */ - - spillMask = regMask; - } - - /* Make sure that we have at least two bits set */ - - if (genMaxOneBit(spillMask & rsRegMaskCanGrab())) - spillMask = rsRegMaskCanGrab(); - - assert(!genMaxOneBit(spillMask)); - - /* We have no choice but to spill 1/2 of the regs */ - - return rsGrabRegPair(spillMask); -} - -/***************************************************************************** - * - * The given tree operand has been spilled; reload it into a register pair - * that is in 'needReg' (if 'needReg' is RBM_NONE, any register pair will do). If - * 'keepReg' is KEEP_REG, we'll mark the new register pair as used. It is - * assumed that the current register pair has been marked as used (modulo - * any spillage, of course). - */ - -void RegSet::rsUnspillRegPair(GenTree* tree, regMaskTP needReg, KeepReg keepReg) -{ - assert(isRegPairType(tree->gtType)); - - regPairNo regPair = tree->gtRegPair; - regNumber regLo = genRegPairLo(regPair); - regNumber regHi = genRegPairHi(regPair); - - /* Has the register holding the lower half been spilled? */ - - if (!rsIsTreeInReg(regLo, tree)) - { - /* Is the upper half already in the right place? */ - - if (rsIsTreeInReg(regHi, tree)) - { - // Temporarily lock the high part if necessary. If this register is a multi-use register that is shared - // with another tree, the register may already be locked. - const regMaskTP regHiMask = genRegMask(regHi); - const bool lockReg = (rsMaskLock & regHiMask) == 0; - if (lockReg) - { - rsLockUsedReg(regHiMask); - } - - /* Pick a new home for the lower half */ - - regLo = rsUnspillOneReg(tree, regLo, keepReg, needReg); - - /* We can unlock the high part now */ - if (lockReg) - { - rsUnlockUsedReg(regHiMask); - } - } - else - { - /* Pick a new home for the lower half */ - - regLo = rsUnspillOneReg(tree, regLo, keepReg, needReg); - } - } - else - { - /* Free the register holding the lower half */ - - rsMarkRegFree(genRegMask(regLo)); - } - - if (regHi != REG_STK) - { - /* Has the register holding the upper half been spilled? */ - - if (!rsIsTreeInReg(regHi, tree)) - { - regMaskTP regLoUsed = RBM_NONE; - - // Temporarily lock the low part if necessary. If this register is a multi-use register that is shared - // with another tree, the register may already be locked. - const regMaskTP regLoMask = genRegMask(regLo); - const bool lockReg = (rsMaskLock & regLoMask) == 0; - if (lockReg) - { - rsLockReg(regLoMask, ®LoUsed); - } - - /* Pick a new home for the upper half */ - - regHi = rsUnspillOneReg(tree, regHi, keepReg, needReg); - - /* We can unlock the low register now */ - if (lockReg) - { - rsUnlockReg(regLoMask, regLoUsed); - } - } - else - { - /* Free the register holding the upper half */ - - rsMarkRegFree(genRegMask(regHi)); - } - } - - /* The value is now residing in the new register */ - - tree->SetInReg(); - tree->gtFlags &= ~GTF_SPILLED; - tree->gtRegPair = gen2regs2pair(regLo, regHi); - - /* Mark the new value as used, if the caller desires so */ - - if (keepReg == KEEP_REG) - rsMarkRegPairUsed(tree); -} - -/***************************************************************************** - * - * The given register is being used by multiple trees (all of which represent - * the same logical value). Happens mainly because of REDUNDANT_LOAD; - * We don't want to really spill the register as it actually holds the - * value we want. But the multiple trees may be part of different - * addressing modes. - * Save the previous 'use' info so that when we return the register will - * appear unused. - */ - -void RegSet::rsRecMultiReg(regNumber reg, var_types type) -{ - SpillDsc* spill; - regMaskTP regMask; - - if (genIsValidFloatReg(reg) && isFloatRegType(type)) - regMask = genRegMaskFloat(reg, type); - else - regMask = genRegMask(reg); - -#ifdef DEBUG - if (m_rsCompiler->verbose) - { - printf("\t\t\t\t\t\t\tRegister %s multi-use inc for ", m_rsCompiler->compRegVarName(reg)); - Compiler::printTreeID(rsUsedTree[reg]); - printf(" multMask=" REG_MASK_ALL_FMT "\n", rsMaskMult | regMask); - } -#endif - - /* The register is supposed to be already used */ - - assert(regMask & rsMaskUsed); - - assert(rsUsedTree[reg]); - - /* Allocate/reuse a spill descriptor */ - - spill = SpillDsc::alloc(m_rsCompiler, this, rsUsedTree[reg]->TypeGet()); - - /* Record the current 'use' info in the spill descriptor */ - - spill->spillTree = rsUsedTree[reg]; - rsUsedTree[reg] = 0; - spill->spillAddr = rsUsedAddr[reg]; - rsUsedAddr[reg] = 0; - - /* Remember whether the register is already 'multi-use' */ - - spill->spillMoreMultis = ((rsMaskMult & regMask) != 0); - - /* Insert the new multi-use record in the list for the register */ - - spill->spillNext = rsMultiDesc[reg]; - rsMultiDesc[reg] = spill; - - /* This register is now 'multi-use' */ - - rsMaskMult |= regMask; -} - -/***************************************************************************** - * - * Free the given register, which is known to have multiple uses. - */ - -var_types RegSet::rsRmvMultiReg(regNumber reg) -{ - SpillDsc* dsc; - - assert(rsMaskMult & genRegMask(reg)); - -#ifdef DEBUG - if (m_rsCompiler->verbose) - { - printf("\t\t\t\t\t\t\tRegister %s multi-use dec for ", m_rsCompiler->compRegVarName(reg)); - Compiler::printTreeID(rsUsedTree[reg]); - printf(" multMask=" REG_MASK_ALL_FMT "\n", rsMaskMult); - } -#endif - - /* Get hold of the spill descriptor for the register */ - - dsc = rsMultiDesc[reg]; - assert(dsc); - rsMultiDesc[reg] = dsc->spillNext; - - /* Copy the previous 'use' info from the descriptor */ - - assert(reg != REG_SPBASE); - rsUsedTree[reg] = dsc->spillTree; - rsUsedAddr[reg] = dsc->spillAddr; - - if (!(dsc->spillTree->gtFlags & GTF_SPILLED)) - m_rsGCInfo.gcMarkRegPtrVal(reg, dsc->spillTree->TypeGet()); - - var_types type = dsc->spillTree->TypeGet(); - regMaskTP regMask; - - if (genIsValidFloatReg(reg) && isFloatRegType(type)) - regMask = genRegMaskFloat(reg, type); - else - regMask = genRegMask(reg); + // Generate the code to spill the register + var_types storeType = floatSpill ? treeType : tempType; - /* Is only one use of the register left? */ + m_rsCompiler->codeGen->spillReg(storeType, temp, reg); - if (!dsc->spillMoreMultis) - { - rsMaskMult -= regMask; - } + // Mark the tree node as having been spilled + rsMarkSpill(tree, reg); -#ifdef DEBUG - if (m_rsCompiler->verbose) + // In case of multi-reg call node also mark the specific + // result reg as spilled. + if (call != nullptr) { - printf("\t\t\t\t\t\t\tRegister %s multi-use dec - now ", m_rsCompiler->compRegVarName(reg)); - Compiler::printTreeID(rsUsedTree[reg]); - printf(" multMask=" REG_MASK_ALL_FMT "\n", rsMaskMult); + regFlags |= GTF_SPILLED; + call->SetRegSpillFlagByIdx(regFlags, regIdx); } -#endif - - SpillDsc::freeDsc(this, dsc); - return type; -} -/*****************************************************************************/ -/***************************************************************************** - * - * Search for a register which contains the given constant value. - * Return success/failure and set the register if success. - * If the closeDelta argument is non-NULL then look for a - * register that has a close constant value. For ARM, find - * the closest register value, independent of constant delta. - * For non-ARM, only consider values that are within -128..+127. - * If one is found, *closeDelta is set to the difference that needs - * to be added to the register returned. On x86/amd64, an lea instruction - * is used to set the target register using the register that - * contains the close integer constant. - */ - -regNumber RegTracker::rsIconIsInReg(ssize_t val, ssize_t* closeDelta /* = NULL */) -{ - regNumber closeReg = REG_NA; - - if (compiler->opts.MinOpts() || compiler->opts.compDbgCode) +#ifdef _TARGET_ARM_ + else if (splitArg != nullptr) { - return REG_NA; + regFlags |= GTF_SPILLED; + splitArg->SetRegSpillFlagByIdx(regFlags, regIdx); } - - for (regNumber reg = REG_INT_FIRST; reg <= REG_INT_LAST; reg = REG_NEXT(reg)) + else if (multiReg != nullptr) { - if (rsRegValues[reg].rvdKind == RV_INT_CNS) - { - ssize_t regCnsVal = rsRegValues[reg].rvdIntCnsVal; - if (regCnsVal == val) - { - if (closeDelta) - { - *closeDelta = 0; - } - return reg; - } - if (closeDelta) - { -#ifdef _TARGET_ARM_ - // Find the smallest delta; the caller checks the size - // TODO-CQ: find the smallest delta from a low register? - // That is, is it better to return a high register with a - // small constant delta, or a low register with - // a larger offset? It's better to have a low register with an offset within the low register - // range, or a high register otherwise... - - ssize_t regCnsDelta = val - regCnsVal; - if ((closeReg == REG_NA) || (unsigned_abs(regCnsDelta) < unsigned_abs(*closeDelta))) - { - closeReg = reg; - *closeDelta = regCnsDelta; - } -#else - if (closeReg == REG_NA) - { - ssize_t regCnsDelta = val - regCnsVal; - /* Does delta fit inside a byte [-128..127] */ - if (regCnsDelta == (signed char)regCnsDelta) - { - closeReg = reg; - *closeDelta = (int)regCnsDelta; - } - } -#endif - } - } + regFlags |= GTF_SPILLED; + multiReg->SetRegSpillFlagByIdx(regFlags, regIdx); } - - /* There was not an exact match */ - - return closeReg; /* will always be REG_NA when closeDelta is NULL */ +#endif // _TARGET_ARM_ } +#if defined(_TARGET_X86_) /***************************************************************************** - * - * Assume all non-integer registers contain garbage (this is called when - * we encounter a code label that isn't jumped by any block; we need to - * clear pointer values out of the table lest the GC pointer tables get - * out of date). - */ - -void RegTracker::rsTrackRegClrPtr() +* +* Spill the top of the FP x87 stack. +*/ +void RegSet::rsSpillFPStack(GenTreeCall* call) { - for (regNumber reg = REG_FIRST; reg < REG_COUNT; reg = REG_NEXT(reg)) - { - /* Preserve constant values */ - - if (rsRegValues[reg].rvdKind == RV_INT_CNS) - { - /* Make sure we don't preserve NULL (it's a pointer) */ - - if (rsRegValues[reg].rvdIntCnsVal != NULL) - { - continue; - } - } + SpillDsc* spill; + TempDsc* temp; + var_types treeType = call->TypeGet(); - /* Preserve variables known to not be pointers */ + spill = SpillDsc::alloc(m_rsCompiler, this, treeType); - if (rsRegValues[reg].rvdKind == RV_LCL_VAR) - { - if (!varTypeIsGC(compiler->lvaTable[rsRegValues[reg].rvdLclVarNum].TypeGet())) - { - continue; - } - } + /* Grab a temp to store the spilled value */ - rsRegValues[reg].rvdKind = RV_TRASH; - } -} + spill->spillTemp = temp = m_rsCompiler->tmpGetTemp(treeType); -/***************************************************************************** - * - * This routine trashes the registers that hold stack GCRef/ByRef variables. (VSW: 561129) - * It should be called at each gc-safe point. - * - * It returns a mask of the registers that used to contain tracked stack variables that - * were trashed. - * - */ + /* Remember what it is we have spilled */ -regMaskTP RegTracker::rsTrashRegsForGCInterruptability() -{ - regMaskTP result = RBM_NONE; - for (regNumber reg = REG_FIRST; reg < REG_COUNT; reg = REG_NEXT(reg)) - { - if (rsRegValues[reg].rvdKind == RV_LCL_VAR) - { - LclVarDsc* varDsc = &compiler->lvaTable[rsRegValues[reg].rvdLclVarNum]; + spill->spillTree = call; + SpillDsc* lastDsc = spill; - if (!varTypeIsGC(varDsc->TypeGet())) - { - continue; - } + regNumber reg = call->gtRegNum; + lastDsc->spillNext = rsSpillDesc[reg]; + rsSpillDesc[reg] = spill; - // Only stack locals got tracked. - assert(!varDsc->lvRegister); +#ifdef DEBUG + if (m_rsCompiler->verbose) + printf("\n"); +#endif - rsRegValues[reg].rvdKind = RV_TRASH; + m_rsCompiler->codeGen->getEmitter()->emitIns_S(INS_fstp, emitActualTypeSize(treeType), temp->tdTempNum(), 0); - result |= genRegMask(reg); - } - } + /* Mark the tree node as having been spilled */ - return result; + rsMarkSpill(call, reg); } +#endif // defined(_TARGET_X86_) /***************************************************************************** * - * Search for a register which contains the given local var. - * Return success/failure and set the register if success. - * Return FALSE on register variables, because otherwise their lifetimes - * can get bungled with respect to pointer tracking. + * Get the temp that was spilled from the given register (and free its + * spill descriptor while we're at it). Returns the temp (i.e. local var) */ -regNumber RegTracker::rsLclIsInReg(unsigned var) +TempDsc* RegSet::rsGetSpillTempWord(regNumber reg, SpillDsc* dsc, SpillDsc* prevDsc) { - assert(var < compiler->lvaCount); + assert((prevDsc == nullptr) || (prevDsc->spillNext == dsc)); - if (compiler->opts.MinOpts() || compiler->opts.compDbgCode) - { - return REG_NA; - } + /* Remove this spill entry from the register's list */ - /* return false if register var so genMarkLclVar can do its job */ + (prevDsc ? prevDsc->spillNext : rsSpillDesc[reg]) = dsc->spillNext; - if (compiler->lvaTable[var].lvRegister) - { - return REG_NA; - } + /* Remember which temp the value is in */ - for (regNumber reg = REG_FIRST; reg < REG_COUNT; reg = REG_NEXT(reg)) - { - if (rsRegValues[reg].rvdLclVarNum == var && rsRegValues[reg].rvdKind == RV_LCL_VAR) - { - return reg; - } - } + TempDsc* temp = dsc->spillTemp; - return REG_NA; -} + SpillDsc::freeDsc(this, dsc); -/*****************************************************************************/ + /* return the temp variable */ -regPairNo RegTracker::rsLclIsInRegPair(unsigned var) -{ - assert(var < compiler->lvaCount); + return temp; +} - if (compiler->opts.MinOpts() || compiler->opts.compDbgCode) - { - return REG_PAIR_NONE; - } +//--------------------------------------------------------------------- +// rsUnspillInPlace: The given tree operand has been spilled; just mark +// it as unspilled so that we can use it as "normal" local. +// +// Arguments: +// tree - GenTree that needs to be marked as unspilled. +// oldReg - reg of tree that was spilled. +// +// Return Value: +// None. +// +// Assumptions: +// 1. It is the responsibility of the caller to free the spill temp. +// 2. RyuJIT backend specific: In case of multi-reg call node +// GTF_SPILLED flag associated with reg is cleared. It is the +// responsibility of caller to clear GTF_SPILLED flag on call node +// itself after ensuring there are no outstanding regs in GTF_SPILLED +// state. +// +TempDsc* RegSet::rsUnspillInPlace(GenTree* tree, regNumber oldReg, unsigned regIdx /* =0 */) +{ + // Get the tree's SpillDsc + SpillDsc* prevDsc; + SpillDsc* spillDsc = rsGetSpillInfo(tree, oldReg, &prevDsc); + PREFIX_ASSUME(spillDsc != nullptr); - regValKind rvKind = RV_TRASH; - regNumber regNo = DUMMY_INIT(REG_NA); + // Get the temp + TempDsc* temp = rsGetSpillTempWord(oldReg, spillDsc, prevDsc); - for (regNumber reg = REG_FIRST; reg < REG_COUNT; reg = REG_NEXT(reg)) + // The value is now unspilled + if (tree->IsMultiRegCall()) { - if (rvKind != rsRegValues[reg].rvdKind && rsTrackIsLclVarLng(rsRegValues[reg].rvdKind) && - rsRegValues[reg].rvdLclVarNum == var) - { - /* first occurrence of this variable ? */ - - if (rvKind == RV_TRASH) - { - regNo = reg; - rvKind = rsRegValues[reg].rvdKind; - } - else if (rvKind == RV_LCL_VAR_LNG_HI) - { - /* We found the lower half of the long */ - - return gen2regs2pair(reg, regNo); - } - else - { - /* We found the upper half of the long */ - - assert(rvKind == RV_LCL_VAR_LNG_LO); - return gen2regs2pair(regNo, reg); - } - } + GenTreeCall* call = tree->AsCall(); + unsigned flags = call->GetRegSpillFlagByIdx(regIdx); + flags &= ~GTF_SPILLED; + call->SetRegSpillFlagByIdx(flags, regIdx); } - - return REG_PAIR_NONE; -} - -/*****************************************************************************/ - -void RegTracker::rsTrashLclLong(unsigned var) -{ - if (compiler->opts.MinOpts() || compiler->opts.compDbgCode) +#if defined(_TARGET_ARM_) + else if (tree->OperIsPutArgSplit()) { - return; + GenTreePutArgSplit* splitArg = tree->AsPutArgSplit(); + unsigned flags = splitArg->GetRegSpillFlagByIdx(regIdx); + flags &= ~GTF_SPILLED; + splitArg->SetRegSpillFlagByIdx(flags, regIdx); } - - for (regNumber reg = REG_FIRST; reg < REG_COUNT; reg = REG_NEXT(reg)) + else if (tree->OperIsMultiRegOp()) { - if (rsTrackIsLclVarLng(rsRegValues[reg].rvdKind) && rsRegValues[reg].rvdLclVarNum == var) - { - rsRegValues[reg].rvdKind = RV_TRASH; - } + GenTreeMultiRegOp* multiReg = tree->AsMultiRegOp(); + unsigned flags = multiReg->GetRegSpillFlagByIdx(regIdx); + flags &= ~GTF_SPILLED; + multiReg->SetRegSpillFlagByIdx(flags, regIdx); } -} - -/***************************************************************************** - * - * Local's value has changed, mark all regs which contained it as trash. - */ - -void RegTracker::rsTrashLcl(unsigned var) -{ - if (compiler->opts.MinOpts() || compiler->opts.compDbgCode) +#endif // _TARGET_ARM_ + else { - return; + tree->gtFlags &= ~GTF_SPILLED; } - for (regNumber reg = REG_FIRST; reg < REG_COUNT; reg = REG_NEXT(reg)) +#ifdef DEBUG + if (m_rsCompiler->verbose) { - if (rsRegValues[reg].rvdKind == RV_LCL_VAR && rsRegValues[reg].rvdLclVarNum == var) - { - rsRegValues[reg].rvdKind = RV_TRASH; - } + printf("\t\t\t\t\t\t\tTree-Node marked unspilled from "); + Compiler::printTreeID(tree); + printf("\n"); } +#endif + + return temp; +} + +void RegSet::rsMarkSpill(GenTree* tree, regNumber reg) +{ + tree->gtFlags |= GTF_SPILLED; } -#endif // LEGACY_BACKEND /***************************************************************************** * @@ -3209,33 +614,6 @@ void RegTracker::rsTrashRegSet(regMaskTP regMask) } } -#ifdef LEGACY_BACKEND -/***************************************************************************** - * - * Return a mask of registers that hold no useful value. - */ - -regMaskTP RegTracker::rsUselessRegs() -{ - if (compiler->opts.MinOpts() || compiler->opts.compDbgCode) - { - return RBM_ALLINT; - } - - regMaskTP mask = RBM_NONE; - for (regNumber reg = REG_FIRST; reg < REG_COUNT; reg = REG_NEXT(reg)) - { - if (rsRegValues[reg].rvdKind == RV_TRASH) - { - mask |= genRegMask(reg); - } - } - - return mask; -} - -/*****************************************************************************/ -#endif // LEGACY_BACKEND /*****************************************************************************/ /* @@ -3252,11 +630,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX void Compiler::tmpInit() { -#ifdef LEGACY_BACKEND - tmpDoubleSpillMax = 0; - tmpIntSpillMax = 0; -#endif // LEGACY_BACKEND - tmpCount = 0; tmpSize = 0; #ifdef DEBUG @@ -3270,8 +643,6 @@ void Compiler::tmpInit() /* static */ var_types Compiler::tmpNormalizeType(var_types type) { -#ifndef LEGACY_BACKEND - type = genActualType(type); #if defined(FEATURE_SIMD) && !defined(_TARGET_64BIT_) @@ -3284,23 +655,6 @@ var_types Compiler::tmpNormalizeType(var_types type) } #endif // defined(FEATURE_SIMD) && !defined(_TARGET_64BIT_) -#else // LEGACY_BACKEND - if (!varTypeIsGC(type)) - { - switch (genTypeStSz(type)) - { - case 1: - type = TYP_INT; // Maps all 4-byte non-GC types to TYP_INT temps - break; - case 2: - type = TYP_DOUBLE; // Maps all 8-byte types to TYP_DOUBLE temps - break; - default: - assert(!"unexpected type"); - } - } -#endif // LEGACY_BACKEND - return type; } @@ -3345,35 +699,8 @@ TempDsc* Compiler::tmpGetTemp(var_types type) bool isNewTemp = false; #endif // DEBUG -#ifndef LEGACY_BACKEND - noway_assert(temp != nullptr); -#else // LEGACY_BACKEND - - if (temp == nullptr) - { -#ifdef DEBUG - isNewTemp = true; -#endif // DEBUG - tmpCount++; - tmpSize += (unsigned)size; - -#ifdef _TARGET_ARM_ - if (type == TYP_DOUBLE) - { - // Adjust tmpSize in case it needs alignment - tmpSize += TARGET_POINTER_SIZE; - } -#endif // _TARGET_ARM_ - - genEmitter->emitTmpSizeChanged(tmpSize); - - temp = new (this, CMK_Unknown) TempDsc(-((int)tmpCount), size, type); - } - -#endif // LEGACY_BACKEND - #ifdef DEBUG if (verbose) { @@ -3389,8 +716,6 @@ TempDsc* Compiler::tmpGetTemp(var_types type) return temp; } -#ifndef LEGACY_BACKEND - /***************************************************************************** * Preallocate 'count' temps of type 'type'. This type must be a normalized * type (by the definition of tmpNormalizeType()). @@ -3444,8 +769,6 @@ void Compiler::tmpPreAllocateTemps(var_types type, unsigned count) } } -#endif // !LEGACY_BACKEND - /***************************************************************************** * * Release the given temp. @@ -3626,38 +949,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ -/***************************************************************************** - * - * Returns whether regPair is a combination of two x86 registers or - * contains a pseudo register. - * In debug it also asserts that reg1 and reg2 are not the same. - */ - -bool genIsProperRegPair(regPairNo regPair) -{ - regNumber rlo = genRegPairLo(regPair); - regNumber rhi = genRegPairHi(regPair); - - assert(regPair >= REG_PAIR_FIRST && regPair <= REG_PAIR_LAST); - - if (rlo == rhi) - { - return false; - } - - if (rlo == REG_L_STK || rhi == REG_L_STK) - { - return false; - } - - if (rlo >= REG_COUNT || rhi >= REG_COUNT) - { - return false; - } - - return (rlo != REG_STK && rhi != REG_STK); -} - /***************************************************************************** * * Given a register that is an argument register @@ -3738,13 +1029,6 @@ void RegSet::rsSpillInit() memset(rsSpillDesc, 0, sizeof(rsSpillDesc)); -#ifdef LEGACY_BACKEND - memset(rsUsedTree, 0, sizeof(rsUsedTree)); - memset(rsUsedAddr, 0, sizeof(rsUsedAddr)); - memset(rsMultiDesc, 0, sizeof(rsMultiDesc)); - rsSpillFloat = nullptr; -#endif // LEGACY_BACKEND - rsNeededSpillReg = false; /* We don't have any descriptors allocated */ @@ -3840,11 +1124,6 @@ void RegSet::rsSpillChk() for (regNumber reg = REG_FIRST; reg < REG_COUNT; reg = REG_NEXT(reg)) { assert(rsSpillDesc[reg] == nullptr); - -#ifdef LEGACY_BACKEND - assert(rsUsedTree[reg] == NULL); - assert(rsMultiDesc[reg] == NULL); -#endif // LEGACY_BACKEND } } @@ -3856,24 +1135,3 @@ void RegSet::rsSpillChk() } #endif - -/*****************************************************************************/ -#ifdef LEGACY_BACKEND - -// inline -bool RegTracker::rsIconIsInReg(ssize_t val, regNumber reg) -{ - if (compiler->opts.MinOpts() || compiler->opts.compDbgCode) - { - return false; - } - - if (rsRegValues[reg].rvdKind == RV_INT_CNS && rsRegValues[reg].rvdIntCnsVal == val) - { - return true; - } - return false; -} - -#endif // LEGACY_BACKEND -/*****************************************************************************/ diff --git a/src/jit/regset.h b/src/jit/regset.h index 5542bc5f88..c50c23aec8 100644 --- a/src/jit/regset.h +++ b/src/jit/regset.h @@ -29,24 +29,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ -#ifdef LEGACY_BACKEND -/***************************************************************************** -* -* Keep track of the current state of each register. This is intended to be -* used for things like register reload suppression, but for now the only -* thing it does is note which registers we use in each method. -*/ - -enum regValKind -{ - RV_TRASH, // random unclassified garbage - RV_INT_CNS, // integer constant - RV_LCL_VAR, // local variable value - RV_LCL_VAR_LNG_LO, // lower half of long local variable - RV_LCL_VAR_LNG_HI, -}; -#endif // LEGACY_BACKEND - /*****************************************************************************/ class RegSet @@ -73,46 +55,17 @@ private: struct SpillDsc { SpillDsc* spillNext; // next spilled value of same reg - - union { - GenTree* spillTree; // the value that was spilled -#ifdef LEGACY_BACKEND - LclVarDsc* spillVarDsc; // variable if it's an enregistered variable -#endif // LEGACY_BACKEND - }; - - TempDsc* spillTemp; // the temp holding the spilled value - -#ifdef LEGACY_BACKEND - GenTree* spillAddr; // owning complex address mode or nullptr - - union { - bool spillMoreMultis; - bool bEnregisteredVariable; // For FP. Indicates that what was spilled was - // an enregistered variable - }; -#endif // LEGACY_BACKEND + GenTree* spillTree; // the value that was spilled + TempDsc* spillTemp; // the temp holding the spilled value static SpillDsc* alloc(Compiler* pComp, RegSet* regSet, var_types type); static void freeDsc(RegSet* regSet, SpillDsc* spillDsc); }; -#ifdef LEGACY_BACKEND -public: - regMaskTP rsUseIfZero(regMaskTP regs, regMaskTP includeHint); -#endif // LEGACY_BACKEND - -//------------------------------------------------------------------------- -// -// Track the status of the registers -// -#ifdef LEGACY_BACKEND -public: // TODO-Cleanup: Should be private, but Compiler uses it - GenTree* rsUsedTree[REG_COUNT]; // trees currently sitting in the registers -private: - GenTree* rsUsedAddr[REG_COUNT]; // addr for which rsUsedTree[reg] is a part of the addressing mode - SpillDsc* rsMultiDesc[REG_COUNT]; // keeps track of 'multiple-use' registers. -#endif // LEGACY_BACKEND + //------------------------------------------------------------------------- + // + // Track the status of the registers + // private: bool rsNeededSpillReg; // true if this method needed to spill any registers @@ -142,10 +95,6 @@ public: } public: // TODO-Cleanup: Should be private, but GCInfo uses them -#ifdef LEGACY_BACKEND - regMaskTP rsMaskUsed; // currently 'used' registers mask -#endif // LEGACY_BACKEND - __declspec(property(get = GetMaskVars, put = SetMaskVars)) regMaskTP rsMaskVars; // mask of registers currently // allocated to variables @@ -174,11 +123,6 @@ public: // TODO-Cleanup: Should be private, but GCInfo uses them private: regMaskTP _rsMaskVars; // backing store for rsMaskVars property -#ifdef LEGACY_BACKEND - regMaskTP rsMaskLock; // currently 'locked' registers mask - regMaskTP rsMaskMult; // currently 'multiply used' registers mask -#endif // LEGACY_BACKEND - #ifdef _TARGET_ARMARCH_ regMaskTP rsMaskCalleeSaved; // mask of the registers pushed/popped in the prolog/epilog #endif // _TARGET_ARM_ @@ -195,126 +139,6 @@ public: // The PreSpill masks are used in LclVars.cpp // and all enregistered user arguments in a varargs call #endif // _TARGET_ARM_ -#ifdef LEGACY_BACKEND - -private: - // These getters/setters are ifdef here so that the accesses to these values in sharedfloat.cpp are redirected - // to the appropriate value. - // With FEATURE_STACK_FP_X87 (x86 FP codegen) we have separate register mask that just handle FP registers. - // For all other platforms (and eventually on x86) we use unified register masks that handle both kinds. - // - regMaskTP rsGetMaskUsed(); // Getter for rsMaskUsed or rsMaskUsedFloat - regMaskTP rsGetMaskVars(); // Getter for rsMaskVars or rsMaskRegVarFloat - regMaskTP rsGetMaskLock(); // Getter for rsMaskLock or rsMaskLockedFloat - regMaskTP rsGetMaskMult(); // Getter for rsMaskMult or 0 - - void rsSetMaskUsed(regMaskTP maskUsed); // Setter for rsMaskUsed or rsMaskUsedFloat - void rsSetMaskVars(regMaskTP maskVars); // Setter for rsMaskVars or rsMaskRegVarFloat - void rsSetMaskLock(regMaskTP maskLock); // Setter for rsMaskLock or rsMaskLockedFloat - - void rsSetUsedTree(regNumber regNum, GenTree* tree); // Setter for rsUsedTree[]/genUsedRegsFloat[] - void rsFreeUsedTree(regNumber regNum, GenTree* tree); // Free for rsUsedTree[]/genUsedRegsFloat[] - -public: - regPairNo rsFindRegPairNo(regMaskTP regMask); - -private: - bool rsIsTreeInReg(regNumber reg, GenTree* tree); - - regMaskTP rsExcludeHint(regMaskTP regs, regMaskTP excludeHint); - regMaskTP rsNarrowHint(regMaskTP regs, regMaskTP narrowHint); - regMaskTP rsMustExclude(regMaskTP regs, regMaskTP exclude); - regMaskTP rsRegMaskFree(); - regMaskTP rsRegMaskCanGrab(); - - void rsMarkRegUsed(GenTree* tree, GenTree* addr = 0); - // A special case of "rsMarkRegUsed": the register used is an argument register, used to hold part of - // the given argument node "promotedStructArg". (The name suggests that we're likely to use use this - // for register holding a promoted struct argument, but the implementation doesn't depend on that.) The - // "isGCRef" argument indicates whether the register contains a GC reference. - void rsMarkArgRegUsedByPromotedFieldArg(GenTree* promotedStructArg, regNumber regNum, bool isGCRef); - - void rsMarkRegPairUsed(GenTree* tree); - - void rsMarkRegFree(regMaskTP regMask); - void rsMarkRegFree(regNumber reg, GenTree* tree); - void rsMultRegFree(regMaskTP regMask); - unsigned rsFreeNeededRegCount(regMaskTP needReg); - - void rsLockReg(regMaskTP regMask); - void rsUnlockReg(regMaskTP regMask); - void rsLockUsedReg(regMaskTP regMask); - void rsUnlockUsedReg(regMaskTP regMask); - void rsLockReg(regMaskTP regMask, regMaskTP* usedMask); - void rsUnlockReg(regMaskTP regMask, regMaskTP usedMask); - - regMaskTP rsRegExclMask(regMaskTP regMask, regMaskTP rmvMask); - - regNumber rsPickRegInTmpOrder(regMaskTP regMask); - -public: // used by emitter (!) - regNumber rsGrabReg(regMaskTP regMask); - -private: - regNumber rsPickReg(regMaskTP regMask = RBM_NONE, regMaskTP regBest = RBM_NONE); - -public: // used by emitter (!) - regNumber rsPickFreeReg(regMaskTP regMaskHint = RBM_ALLINT); - -private: - regPairNo rsGrabRegPair(regMaskTP regMask); - regPairNo rsPickRegPair(regMaskTP regMask); - - class RegisterPreference - { - public: - regMaskTP ok; - regMaskTP best; - RegisterPreference(regMaskTP _ok, regMaskTP _best) - { - ok = _ok; - best = _best; - } - }; - regNumber PickRegFloat(GenTree* tree, - var_types type = TYP_DOUBLE, - RegisterPreference* pref = NULL, - bool bUsed = true); - regNumber PickRegFloat(var_types type = TYP_DOUBLE, RegisterPreference* pref = NULL, bool bUsed = true); - regNumber PickRegFloatOtherThan(GenTree* tree, var_types type, regNumber reg); - regNumber PickRegFloatOtherThan(var_types type, regNumber reg); - - regMaskTP RegFreeFloat(); - - void SetUsedRegFloat(GenTree* tree, bool bValue); - void SetLockedRegFloat(GenTree* tree, bool bValue); - bool IsLockedRegFloat(GenTree* tree); - - var_types rsRmvMultiReg(regNumber reg); - void rsRecMultiReg(regNumber reg, var_types type); -#endif // LEGACY_BACKEND - -public: -#ifdef DEBUG - /***************************************************************************** - * Should we stress register tracking logic ? - * This is set via COMPlus_JitStressRegs. - * The following values are ordered, such that any value greater than RS_xx - * implies RS_xx. - * LSRA defines a different set of values, but uses the same COMPlus_JitStressRegs - * value, with the same notion of relative ordering. - * 1 = rsPickReg() picks 'bad' registers. - * 2 = codegen spills at safe points. This is still flaky - */ - enum rsStressRegsType - { - RS_STRESS_NONE = 0, - RS_PICK_BAD_REG = 01, - RS_SPILL_SAFE = 02, - }; - rsStressRegsType rsStressRegs(); -#endif // DEBUG - private: //------------------------------------------------------------------------- // @@ -325,10 +149,6 @@ private: SpillDsc* rsSpillDesc[REG_COUNT]; SpillDsc* rsSpillFree; // list of unused spill descriptors -#ifdef LEGACY_BACKEND - SpillDsc* rsSpillFloat; -#endif // LEGACY_BACKEND - void rsSpillChk(); void rsSpillInit(); void rsSpillDone(); @@ -337,63 +157,17 @@ private: void rsSpillTree(regNumber reg, GenTree* tree, unsigned regIdx = 0); -#if defined(_TARGET_X86_) && !FEATURE_STACK_FP_X87 +#if defined(_TARGET_X86_) void rsSpillFPStack(GenTreeCall* call); -#endif // defined(_TARGET_X86_) && !FEATURE_STACK_FP_X87 - -#ifdef LEGACY_BACKEND - void rsSpillReg(regNumber reg); - void rsSpillRegIfUsed(regNumber reg); - void rsSpillRegs(regMaskTP regMask); -#endif // LEGACY_BACKEND - - SpillDsc* rsGetSpillInfo(GenTree* tree, - regNumber reg, - SpillDsc** pPrevDsc = nullptr -#ifdef LEGACY_BACKEND - , - SpillDsc** pMultiDsc = NULL -#endif // LEGACY_BACKEND - ); +#endif // defined(_TARGET_X86_) - TempDsc* rsGetSpillTempWord(regNumber oldReg, SpillDsc* dsc, SpillDsc* prevDsc); + SpillDsc* rsGetSpillInfo(GenTree* tree, regNumber reg, SpillDsc** pPrevDsc = nullptr); -#ifdef LEGACY_BACKEND - enum ExactReg - { - ANY_REG, - EXACT_REG - }; - enum KeepReg - { - FREE_REG, - KEEP_REG - }; - - regNumber rsUnspillOneReg(GenTree* tree, regNumber oldReg, KeepReg willKeepNewReg, regMaskTP needReg); -#endif // LEGACY_BACKEND + TempDsc* rsGetSpillTempWord(regNumber oldReg, SpillDsc* dsc, SpillDsc* prevDsc); TempDsc* rsUnspillInPlace(GenTree* tree, regNumber oldReg, unsigned regIdx = 0); -#ifdef LEGACY_BACKEND - void rsUnspillReg(GenTree* tree, regMaskTP needReg, KeepReg keepReg); - - void rsUnspillRegPair(GenTree* tree, regMaskTP needReg, KeepReg keepReg); -#endif // LEGACY_BACKEND - void rsMarkSpill(GenTree* tree, regNumber reg); - -#ifdef LEGACY_BACKEND - void rsMarkUnspill(GenTree* tree, regNumber reg); -#endif // LEGACY_BACKEND - -#if FEATURE_STACK_FP_X87 - regMaskTP rsMaskUsedFloat; - regMaskTP rsMaskRegVarFloat; - regMaskTP rsMaskLockedFloat; - GenTree* genUsedRegsFloat[REG_FPCOUNT]; - LclVarDsc* genRegVarsFloat[REG_FPCOUNT]; -#endif // FEATURE_STACK_FP_X87 }; //------------------------------------------------------------------------- @@ -404,69 +178,22 @@ private: // Only integer registers are tracked. // -#ifdef LEGACY_BACKEND -struct RegValDsc -{ - regValKind rvdKind; - union { - ssize_t rvdIntCnsVal; // for rvdKind == RV_INT_CNS - unsigned rvdLclVarNum; // for rvdKind == RV_LCL_VAR, RV_LCL_VAR_LNG_LO, RV_LCL_VAR_LNG_HI - }; -}; -#endif // LEGACY_BACKEND - class RegTracker { Compiler* compiler; RegSet* regSet; -#ifdef LEGACY_BACKEND - RegValDsc rsRegValues[REG_COUNT]; -#endif public: void rsTrackInit(Compiler* comp, RegSet* rs) { compiler = comp; regSet = rs; -#ifdef LEGACY_BACKEND - rsTrackRegClr(); -#endif } -#ifdef LEGACY_BACKEND - void rsTrackRegClr(); - void rsTrackRegClrPtr(); -#endif // LEGACY_BACKEND void rsTrackRegTrash(regNumber reg); -#ifdef LEGACY_BACKEND - void rsTrackRegMaskTrash(regMaskTP regMask); - regMaskTP rsTrashRegsForGCInterruptability(); -#endif // LEGACY_BACKEND void rsTrackRegIntCns(regNumber reg, ssize_t val); void rsTrackRegLclVar(regNumber reg, unsigned var); -#ifdef LEGACY_BACKEND - void rsTrackRegLclVarLng(regNumber reg, unsigned var, bool low); - bool rsTrackIsLclVarLng(regValKind rvKind); - void rsTrackRegClsVar(regNumber reg, GenTree* clsVar); -#endif // LEGACY_BACKEND void rsTrackRegCopy(regNumber reg1, regNumber reg2); -#ifdef LEGACY_BACKEND - void rsTrackRegSwap(regNumber reg1, regNumber reg2); - void rsTrackRegAssign(GenTree* op1, GenTree* op2); - - regNumber rsIconIsInReg(ssize_t val, ssize_t* closeDelta = nullptr); - bool rsIconIsInReg(ssize_t val, regNumber reg); - regNumber rsLclIsInReg(unsigned var); - regPairNo rsLclIsInRegPair(unsigned var); - - //---------------------- Load suppression --------------------------------- - - void rsTrashLclLong(unsigned var); - void rsTrashLcl(unsigned var); -#endif // LEGACY_BACKEND void rsTrashRegSet(regMaskTP regMask); -#ifdef LEGACY_BACKEND - regMaskTP rsUselessRegs(); -#endif // LEGACY_BACKEND }; #endif // _REGSET_H diff --git a/src/jit/sharedfloat.cpp b/src/jit/sharedfloat.cpp deleted file mode 100644 index 35d5519216..0000000000 --- a/src/jit/sharedfloat.cpp +++ /dev/null @@ -1,500 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// -// NOTE: The code in this file is only used for LEGACY_BACKEND compiles. - -#include "jitpch.h" -#ifdef _MSC_VER -#pragma hdrstop -#endif - -#include "compiler.h" -#include "emit.h" -#include "codegen.h" - -#ifdef LEGACY_BACKEND - -#if FEATURE_STACK_FP_X87 -regMaskTP RegSet::rsGetMaskUsed() -{ - return rsMaskUsedFloat; -} -regMaskTP RegSet::rsGetMaskVars() -{ - return rsMaskRegVarFloat; -} -regMaskTP RegSet::rsGetMaskLock() -{ - return rsMaskLockedFloat; -} -regMaskTP RegSet::rsGetMaskMult() -{ - return 0; -} - -void RegSet::rsSetMaskUsed(regMaskTP maskUsed) -{ - rsMaskUsedFloat = maskUsed; -} -void RegSet::rsSetMaskVars(regMaskTP maskVars) -{ - rsMaskRegVarFloat = maskVars; -} -void RegSet::rsSetMaskLock(regMaskTP maskLock) -{ - rsMaskLockedFloat = maskLock; -} - -void RegSet::rsSetUsedTree(regNumber regNum, GenTree* tree) -{ - assert(genUsedRegsFloat[regNum] == 0); - genUsedRegsFloat[regNum] = tree; -} -void RegSet::rsFreeUsedTree(regNumber regNum, GenTree* tree) -{ - assert(genUsedRegsFloat[regNum] == tree); - genUsedRegsFloat[regNum] = 0; -} - -#else // !FEATURE_STACK_FP_X87 -regMaskTP RegSet::rsGetMaskUsed() -{ - return rsMaskUsed; -} -regMaskTP RegSet::rsGetMaskVars() -{ - return rsMaskVars; -} -regMaskTP RegSet::rsGetMaskLock() -{ - return rsMaskLock; -} -regMaskTP RegSet::rsGetMaskMult() -{ - return rsMaskMult; -} - -void RegSet::rsSetMaskUsed(regMaskTP maskUsed) -{ - rsMaskUsed = maskUsed; -} -void RegSet::rsSetMaskVars(regMaskTP maskVars) -{ - rsMaskVars = maskVars; -} -void RegSet::rsSetMaskLock(regMaskTP maskLock) -{ - rsMaskLock = maskLock; -} - -void RegSet::rsSetUsedTree(regNumber regNum, GenTree* tree) -{ - assert(rsUsedTree[regNum] == 0); - rsUsedTree[regNum] = tree; -} -void RegSet::rsFreeUsedTree(regNumber regNum, GenTree* tree) -{ - assert(rsUsedTree[regNum] == tree); - rsUsedTree[regNum] = 0; -} -#endif // !FEATURE_STACK_FP_X87 - -// float stress mode. Will lock out registers to stress high register pressure. -// This implies setting interferences in register allocator and pushing regs in -// the prolog and popping them before a ret. -#ifdef DEBUG -int CodeGenInterface::genStressFloat() -{ - return compiler->compStressCompile(Compiler::STRESS_FLATFP, 40) ? 1 : JitConfig.JitStressFP(); -} -#endif - -regMaskTP RegSet::RegFreeFloat() -{ - regMaskTP mask = RBM_ALLFLOAT; -#if FEATURE_FP_REGALLOC - mask &= m_rsCompiler->raConfigRestrictMaskFP(); -#endif - - mask &= ~rsGetMaskUsed(); - mask &= ~rsGetMaskLock(); - mask &= ~rsGetMaskVars(); - -#ifdef DEBUG - if (m_rsCompiler->codeGen->genStressFloat()) - { - mask &= ~(m_rsCompiler->codeGen->genStressLockedMaskFloat()); - } -#endif - return mask; -} - -#ifdef _TARGET_ARM_ -// order registers are picked -// go in reverse order to minimize chance of spilling with calls -static const regNumber pickOrder[] = {REG_F15, REG_F14, REG_F13, REG_F12, REG_F11, REG_F10, REG_F9, REG_F8, - REG_F7, REG_F6, REG_F5, REG_F4, REG_F3, REG_F2, REG_F1, REG_F0, - - REG_F16, REG_F17, REG_F18, REG_F19, REG_F20, REG_F21, REG_F22, REG_F23, - REG_F24, REG_F25, REG_F26, REG_F27, REG_F28, REG_F29, REG_F30, REG_F31}; - -#elif _TARGET_AMD64_ -// order registers are picked -static const regNumber pickOrder[] = {REG_XMM0, REG_XMM1, REG_XMM2, REG_XMM3, REG_XMM4, REG_XMM5, - REG_XMM6, REG_XMM7, REG_XMM8, REG_XMM9, REG_XMM10, REG_XMM11, - REG_XMM12, REG_XMM13, REG_XMM14, REG_XMM15}; - -#elif _TARGET_X86_ -// order registers are picked -static const regNumber pickOrder[] = {REG_FPV0, REG_FPV1, REG_FPV2, REG_FPV3, REG_FPV4, REG_FPV5, REG_FPV6, REG_FPV7}; -#endif - -// picks a reg other than the one specified -regNumber RegSet::PickRegFloatOtherThan(GenTree* tree, var_types type, regNumber reg) -{ - return PickRegFloatOtherThan(type, reg); -} - -regNumber RegSet::PickRegFloatOtherThan(var_types type, regNumber reg) -{ - RegisterPreference pref(RBM_ALLFLOAT ^ genRegMask(reg), 0); - return PickRegFloat(type, &pref); -} - -regNumber RegSet::PickRegFloat(GenTree* tree, var_types type, RegisterPreference* pref, bool bUsed) -{ - return PickRegFloat(type, pref, bUsed); -} - -regNumber RegSet::PickRegFloat(var_types type, RegisterPreference* pref, bool bUsed) -{ - regMaskTP wantedMask; - bool tryBest = true; - bool tryOk = true; - bool bSpill = false; - regNumber reg = REG_NA; - - while (tryOk) - { - if (pref) - { - if (tryBest) - { - wantedMask = pref->best; - tryBest = false; - } - else - { - assert(tryOk); - wantedMask = pref->ok; - tryOk = false; - } - } - else // pref is NULL - { - wantedMask = RBM_ALLFLOAT; - tryBest = false; - tryOk = false; - } - - // better not have asked for a non-fp register - assert((wantedMask & ~RBM_ALLFLOAT) == 0); - - regMaskTP availMask = RegFreeFloat(); - regMaskTP OKmask = availMask & wantedMask; - - if (OKmask == 0) - { - if (tryOk) - { - // the pref->best mask doesn't work so try the pref->ok mask next - continue; - } - - if (bUsed) - { - // Allow used registers to be picked - OKmask |= rsGetMaskUsed() & ~rsGetMaskLock(); - bSpill = true; - } - } -#if FEATURE_FP_REGALLOC - regMaskTP restrictMask = (m_rsCompiler->raConfigRestrictMaskFP() | RBM_FLT_CALLEE_TRASH); -#endif - - for (unsigned i = 0; i < ArrLen(pickOrder); i++) - { - regNumber r = pickOrder[i]; - if (!floatRegCanHoldType(r, type)) - continue; - - regMaskTP mask = genRegMaskFloat(r, type); - -#if FEATURE_FP_REGALLOC - if ((mask & restrictMask) != mask) - continue; -#endif - if ((OKmask & mask) == mask) - { - reg = r; - goto RET; - } - } - - if (tryOk) - { - // We couldn't find a register using tryBest - continue; - } - - assert(!"Unable to find a free FP virtual register"); - NO_WAY("FP register allocator was too optimistic!"); - } -RET: - if (bSpill) - { - m_rsCompiler->codeGen->SpillFloat(reg); - } - -#if FEATURE_FP_REGALLOC - rsSetRegsModified(genRegMaskFloat(reg, type)); -#endif - - return reg; -} - -#ifdef LEGACY_BACKEND -void RegSet::SetUsedRegFloat(GenTree* tree, bool bValue) -{ - /* The value must be sitting in a register */ - assert(tree); - assert(tree->InReg()); - - var_types type = tree->TypeGet(); -#ifdef _TARGET_ARM_ - if (type == TYP_STRUCT) - { - assert(m_rsCompiler->IsHfa(tree)); - type = TYP_FLOAT; - } -#endif - regNumber regNum = tree->gtRegNum; - regMaskTP regMask = genRegMaskFloat(regNum, type); - - if (bValue) - { -#ifdef DEBUG - if (m_rsCompiler->verbose) - { - printf("\t\t\t\t\t\t\tThe register %s currently holds ", getRegNameFloat(regNum, type)); - Compiler::printTreeID(tree); - printf("\n"); - } -#endif - - // Mark as used - assert((rsGetMaskLock() & regMask) == 0); - -#if FEATURE_STACK_FP_X87 - assert((rsGetMaskUsed() & regMask) == 0); -#else - /* Is the register used by two different values simultaneously? */ - - if (regMask & rsGetMaskUsed()) - { - /* Save the preceding use information */ - - rsRecMultiReg(regNum, type); - } -#endif - /* Set the register's bit in the 'used' bitset */ - - rsSetMaskUsed((rsGetMaskUsed() | regMask)); - - // Assign slot - rsSetUsedTree(regNum, tree); - } - else - { -#ifdef DEBUG - if (m_rsCompiler->verbose) - { - printf("\t\t\t\t\t\t\tThe register %s no longer holds ", getRegNameFloat(regNum, type)); - Compiler::printTreeID(tree); - printf("\n"); - } -#endif - - // Mark as free - assert((rsGetMaskUsed() & regMask) == regMask); - - // Are we freeing a multi-use registers? - - if (regMask & rsGetMaskMult()) - { - // Free any multi-use registers - rsMultRegFree(regMask); - return; - } - - rsSetMaskUsed((rsGetMaskUsed() & ~regMask)); - - // Free slot - rsFreeUsedTree(regNum, tree); - } -} -#endif // LEGACY_BACKEND - -void RegSet::SetLockedRegFloat(GenTree* tree, bool bValue) -{ - regNumber reg = tree->gtRegNum; - var_types type = tree->TypeGet(); - assert(varTypeIsFloating(type)); - regMaskTP regMask = genRegMaskFloat(reg, tree->TypeGet()); - - if (bValue) - { - JITDUMP("locking register %s\n", getRegNameFloat(reg, type)); - - assert((rsGetMaskUsed() & regMask) == regMask); - assert((rsGetMaskLock() & regMask) == 0); - - rsSetMaskLock((rsGetMaskLock() | regMask)); - } - else - { - JITDUMP("unlocking register %s\n", getRegNameFloat(reg, type)); - - assert((rsGetMaskUsed() & regMask) == regMask); - assert((rsGetMaskLock() & regMask) == regMask); - - rsSetMaskLock((rsGetMaskLock() & ~regMask)); - } -} - -bool RegSet::IsLockedRegFloat(GenTree* tree) -{ - /* The value must be sitting in a register */ - assert(tree); - assert(tree->InReg()); - assert(varTypeIsFloating(tree->TypeGet())); - - regMaskTP regMask = genRegMaskFloat(tree->gtRegNum, tree->TypeGet()); - return (rsGetMaskLock() & regMask) == regMask; -} - -void CodeGen::UnspillFloat(GenTree* tree) -{ -#ifdef DEBUG - if (verbose) - { - printf("UnspillFloat() for tree "); - Compiler::printTreeID(tree); - printf("\n"); - } -#endif // DEBUG - - RegSet::SpillDsc* cur = regSet.rsSpillFloat; - assert(cur); - - while (cur->spillTree != tree) - cur = cur->spillNext; - - UnspillFloat(cur); -} - -void CodeGen::UnspillFloat(LclVarDsc* varDsc) -{ - JITDUMP("UnspillFloat() for var [%08p]\n", dspPtr(varDsc)); - - RegSet::SpillDsc* cur = regSet.rsSpillFloat; - assert(cur); - - while (cur->spillVarDsc != varDsc) - cur = cur->spillNext; - - UnspillFloat(cur); -} - -void CodeGen::RemoveSpillDsc(RegSet::SpillDsc* spillDsc) -{ - RegSet::SpillDsc* cur; - RegSet::SpillDsc** prev; - - for (cur = regSet.rsSpillFloat, prev = ®Set.rsSpillFloat; cur != spillDsc; - prev = &cur->spillNext, cur = cur->spillNext) - ; // EMPTY LOOP - - assert(cur); - - // Remove node from list - *prev = cur->spillNext; -} - -void CodeGen::UnspillFloat(RegSet::SpillDsc* spillDsc) -{ - JITDUMP("UnspillFloat() for SpillDsc [%08p]\n", dspPtr(spillDsc)); - - RemoveSpillDsc(spillDsc); - UnspillFloatMachineDep(spillDsc); - - RegSet::SpillDsc::freeDsc(®Set, spillDsc); - compiler->tmpRlsTemp(spillDsc->spillTemp); -} - -#if FEATURE_STACK_FP_X87 - -Compiler::fgWalkResult CodeGen::genRegVarDiesInSubTreeWorker(GenTree** pTree, Compiler::fgWalkData* data) -{ - GenTree* tree = *pTree; - genRegVarDiesInSubTreeData* pData = (genRegVarDiesInSubTreeData*)data->pCallbackData; - - // if it's dying, just rename the register, else load it normally - if (tree->IsRegVar() && tree->IsRegVarDeath() && tree->gtRegVar.gtRegNum == pData->reg) - { - pData->result = true; - return Compiler::WALK_ABORT; - } - - return Compiler::WALK_CONTINUE; -} - -bool CodeGen::genRegVarDiesInSubTree(GenTree* tree, regNumber reg) -{ - genRegVarDiesInSubTreeData Data; - Data.reg = reg; - Data.result = false; - - compiler->fgWalkTreePre(&tree, genRegVarDiesInSubTreeWorker, (void*)&Data); - - return Data.result; -} - -#endif // FEATURE_STACK_FP_X87 - -/***************************************************************************** - * - * Force floating point expression results to memory, to get rid of the extra - * 80 byte "temp-real" precision. - * Assumes the tree operand has been computed to the top of the stack. - * If type!=TYP_UNDEF, that is the desired presicion, else it is op->gtType - */ - -void CodeGen::genRoundFpExpression(GenTree* op, var_types type) -{ -#if FEATURE_STACK_FP_X87 - return genRoundFpExpressionStackFP(op, type); -#else - return genRoundFloatExpression(op, type); -#endif -} - -void CodeGen::genCodeForTreeFloat(GenTree* tree, regMaskTP needReg, regMaskTP bestReg) -{ - RegSet::RegisterPreference pref(needReg, bestReg); - genCodeForTreeFloat(tree, &pref); -} - -#endif // LEGACY_BACKEND diff --git a/src/jit/simd.cpp b/src/jit/simd.cpp index b49c5f1ca4..c45aa8982e 100644 --- a/src/jit/simd.cpp +++ b/src/jit/simd.cpp @@ -30,8 +30,6 @@ #pragma hdrstop #endif -#ifndef LEGACY_BACKEND // This file is ONLY used for the RyuJIT backend that uses the linear scan register allocator. - #ifdef FEATURE_SIMD // Intrinsic Id to intrinsic info map @@ -3116,5 +3114,3 @@ GenTree* Compiler::impSIMDIntrinsic(OPCODE opcode, } #endif // FEATURE_SIMD - -#endif // !LEGACY_BACKEND diff --git a/src/jit/simdcodegenxarch.cpp b/src/jit/simdcodegenxarch.cpp index 64a9bd5cf5..a61fbb7e94 100644 --- a/src/jit/simdcodegenxarch.cpp +++ b/src/jit/simdcodegenxarch.cpp @@ -15,9 +15,9 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #pragma hdrstop #endif -#ifndef LEGACY_BACKEND // This file is ONLY used for the RyuJIT backend that uses the linear scan register allocator. - #ifdef _TARGET_XARCH_ +#ifdef FEATURE_SIMD + #include "emit.h" #include "codegen.h" #include "sideeffects.h" @@ -25,8 +25,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #include "gcinfo.h" #include "gcinfoencoder.h" -#ifdef FEATURE_SIMD - // Instruction immediates // Insertps: @@ -3209,4 +3207,3 @@ void CodeGen::genSIMDIntrinsic(GenTreeSIMD* simdNode) #endif // FEATURE_SIMD #endif //_TARGET_XARCH_ -#endif // !LEGACY_BACKEND diff --git a/src/jit/stackfp.cpp b/src/jit/stackfp.cpp deleted file mode 100644 index c15ae629d1..0000000000 --- a/src/jit/stackfp.cpp +++ /dev/null @@ -1,4506 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#include "jitpch.h" -#ifdef _MSC_VER -#pragma hdrstop -#endif - -#ifdef LEGACY_BACKEND // This file is NOT used for the RyuJIT backend that uses the linear scan register allocator. - -#ifdef _TARGET_AMD64_ -#error AMD64 must be !LEGACY_BACKEND -#endif - -#include "compiler.h" -#include "emit.h" -#include "codegen.h" - -// Instruction list -// N=normal, R=reverse, P=pop -#if FEATURE_STACK_FP_X87 -const static instruction FPmathNN[] = {INS_fadd, INS_fsub, INS_fmul, INS_fdiv}; -const static instruction FPmathNP[] = {INS_faddp, INS_fsubp, INS_fmulp, INS_fdivp}; -const static instruction FPmathRN[] = {INS_fadd, INS_fsubr, INS_fmul, INS_fdivr}; -const static instruction FPmathRP[] = {INS_faddp, INS_fsubrp, INS_fmulp, INS_fdivrp}; - -FlatFPStateX87* CodeGenInterface::FlatFPAllocFPState(FlatFPStateX87* pInitFrom) -{ - FlatFPStateX87* pNewState; - - pNewState = new (compiler, CMK_FlatFPStateX87) FlatFPStateX87; - pNewState->Init(pInitFrom); - - return pNewState; -} - -bool CodeGen::FlatFPSameRegisters(FlatFPStateX87* pState, regMaskTP mask) -{ - int i; - for (i = REG_FPV0; i < REG_FPCOUNT; i++) - { - if (pState->Mapped(i)) - { - regMaskTP regmask = genRegMaskFloat((regNumber)i); - if ((mask & regmask) == 0) - { - return false; - } - - mask &= ~regmask; - } - } - - return mask ? false : true; -} - -bool FlatFPStateX87::Mapped(unsigned uEntry) -{ - return m_uVirtualMap[uEntry] != (unsigned)FP_VRNOTMAPPED; -} - -void FlatFPStateX87::Unmap(unsigned uEntry) -{ - assert(Mapped(uEntry)); - m_uVirtualMap[uEntry] = (unsigned)FP_VRNOTMAPPED; -} - -bool FlatFPStateX87::AreEqual(FlatFPStateX87* pA, FlatFPStateX87* pB) -{ - unsigned i; - - assert(pA->IsConsistent()); - assert(pB->IsConsistent()); - - if (pA->m_uStackSize != pB->m_uStackSize) - { - return false; - } - - for (i = 0; i < pA->m_uStackSize; i++) - { - if (pA->m_uStack[i] != pB->m_uStack[i]) - { - return false; - } - } - - return true; -} - -#ifdef DEBUG -bool FlatFPStateX87::IsValidEntry(unsigned uEntry) -{ - return (Mapped(uEntry) && (m_uVirtualMap[uEntry] >= 0 && m_uVirtualMap[uEntry] < m_uStackSize)) || !Mapped(uEntry); -} - -bool FlatFPStateX87::IsConsistent() -{ - unsigned i; - - for (i = 0; i < FP_VIRTUALREGISTERS; i++) - { - if (!IsValidEntry(i)) - { - if (m_bIgnoreConsistencyChecks) - { - return true; - } - else - { - assert(!"Virtual register is marked as mapped but out of the stack range"); - return false; - } - } - } - - for (i = 0; i < m_uStackSize; i++) - { - if (m_uVirtualMap[m_uStack[i]] != i) - { - if (m_bIgnoreConsistencyChecks) - { - return true; - } - else - { - assert(!"Register File and stack layout don't match!"); - return false; - } - } - } - - return true; -} - -void FlatFPStateX87::Dump() -{ - unsigned i; - - assert(IsConsistent()); - - if (m_uStackSize > 0) - { - printf("Virtual stack state: "); - for (i = 0; i < m_uStackSize; i++) - { - printf("ST(%i): FPV%i | ", StackToST(i), m_uStack[i]); - } - printf("\n"); - } -} - -void FlatFPStateX87::UpdateMappingFromStack() -{ - memset(m_uVirtualMap, -1, sizeof(m_uVirtualMap)); - - unsigned i; - - for (i = 0; i < m_uStackSize; i++) - { - m_uVirtualMap[m_uStack[i]] = i; - } -} - -#endif - -unsigned FlatFPStateX87::StackToST(unsigned uEntry) -{ - assert(IsValidEntry(uEntry)); - return m_uStackSize - 1 - uEntry; -} - -unsigned FlatFPStateX87::VirtualToST(unsigned uEntry) -{ - assert(Mapped(uEntry)); - - return StackToST(m_uVirtualMap[uEntry]); -} - -unsigned FlatFPStateX87::STToVirtual(unsigned uST) -{ - assert(uST < m_uStackSize); - - return m_uStack[m_uStackSize - 1 - uST]; -} - -void FlatFPStateX87::Init(FlatFPStateX87* pFrom) -{ - if (pFrom) - { - memcpy(this, pFrom, sizeof(*this)); - } - else - { - memset(m_uVirtualMap, -1, sizeof(m_uVirtualMap)); - -#ifdef DEBUG - memset(m_uStack, -1, sizeof(m_uStack)); -#endif - m_uStackSize = 0; - } - -#ifdef DEBUG - m_bIgnoreConsistencyChecks = false; -#endif -} - -void FlatFPStateX87::Associate(unsigned uEntry, unsigned uStack) -{ - assert(uStack < m_uStackSize); - - m_uStack[uStack] = uEntry; - m_uVirtualMap[uEntry] = uStack; -} - -unsigned FlatFPStateX87::TopIndex() -{ - return m_uStackSize - 1; -} - -unsigned FlatFPStateX87::TopVirtual() -{ - assert(m_uStackSize > 0); - return m_uStack[m_uStackSize - 1]; -} - -void FlatFPStateX87::Rename(unsigned uVirtualTo, unsigned uVirtualFrom) -{ - assert(!Mapped(uVirtualTo)); - - unsigned uSlot = m_uVirtualMap[uVirtualFrom]; - - Unmap(uVirtualFrom); - Associate(uVirtualTo, uSlot); -} - -void FlatFPStateX87::Push(unsigned uEntry) -{ - assert(m_uStackSize <= FP_PHYSICREGISTERS); - assert(!Mapped(uEntry)); - - m_uStackSize++; - Associate(uEntry, TopIndex()); - - assert(IsConsistent()); -} - -unsigned FlatFPStateX87::Pop() -{ - assert(m_uStackSize != 0); - - unsigned uVirtual = m_uStack[--m_uStackSize]; - -#ifdef DEBUG - m_uStack[m_uStackSize] = (unsigned)-1; -#endif - - Unmap(uVirtual); - - return uVirtual; -} - -bool FlatFPStateX87::IsEmpty() -{ - return m_uStackSize == 0; -} - -void CodeGen::genCodeForTransitionStackFP(FlatFPStateX87* pSrc, FlatFPStateX87* pDst) -{ - FlatFPStateX87 fpState; - FlatFPStateX87* pTmp; - int i; - - // Make a temp copy - memcpy(&fpState, pSrc, sizeof(FlatFPStateX87)); - pTmp = &fpState; - - // Make sure everything seems consistent. - assert(pSrc->m_uStackSize >= pDst->m_uStackSize); -#ifdef DEBUG - for (i = 0; i < FP_VIRTUALREGISTERS; i++) - { - if (!pTmp->Mapped(i) && pDst->Mapped(i)) - { - assert(!"Dst stack state can't have a virtual register live if Src target has it dead"); - } - } -#endif - - // First we need to get rid of the stuff that's dead in pDst - for (i = 0; i < FP_VIRTUALREGISTERS; i++) - { - if (pTmp->Mapped(i) && !pDst->Mapped(i)) - { - // We have to get rid of this one - JITDUMP("Removing virtual register V%i from stack\n", i); - - // Don't need this virtual register any more - FlatFPX87_Unload(pTmp, i); - } - } - - assert(pTmp->m_uStackSize == pDst->m_uStackSize); - - // Extract cycles - int iProcessed = 0; - - // We start with the top of the stack so that we can - // easily recognize the cycle that contains it - for (i = pTmp->m_uStackSize - 1; i >= 0; i--) - { - // Have we processed this stack element yet? - if (((1 << i) & iProcessed) == 0) - { - // Extract cycle - int iCycle[FP_VIRTUALREGISTERS]; - int iCycleLength = 0; - int iCurrent = i; - int iTOS = pTmp->m_uStackSize - 1; - - do - { - // Mark current stack element as processed - iProcessed |= (1 << iCurrent); - - // Update cycle - iCycle[iCycleLength++] = iCurrent; - - // Next element in cycle - iCurrent = pDst->m_uVirtualMap[pTmp->m_uStack[iCurrent]]; - - } while ((iProcessed & (1 << iCurrent)) == 0); - -#ifdef DEBUG - if (verbose) - { - printf("Cycle: ("); - for (int l = 0; l < iCycleLength; l++) - { - printf("%i", pTmp->StackToST(iCycle[l])); - if (l + 1 < iCycleLength) - printf(", "); - } - printf(")\n"); - } -#endif - - // Extract cycle - if (iCycleLength == 1) - { - // Stack element in the same place. Nothing to do - } - else - { - if (iCycle[0] == iTOS) - { - // Cycle includes stack element 0 - int j; - - for (j = 1; j < iCycleLength; j++) - { - FlatFPX87_SwapStack(pTmp, iCycle[j], iTOS); - } - } - else - { - // Cycle doesn't include stack element 0 - int j; - - for (j = 0; j < iCycleLength; j++) - { - FlatFPX87_SwapStack(pTmp, iCycle[j], iTOS); - } - - FlatFPX87_SwapStack(pTmp, iCycle[0], iTOS); - } - } - } - } - - assert(FlatFPStateX87::AreEqual(pTmp, pDst)); -} - -void CodeGen::genCodeForTransitionFromMask(FlatFPStateX87* pSrc, regMaskTP mask, bool bEmitCode) -{ - unsigned i; - for (i = REG_FPV0; i < REG_FPCOUNT; i++) - { - if (pSrc->Mapped(i)) - { - if ((mask & genRegMaskFloat((regNumber)i)) == 0) - { - FlatFPX87_Unload(pSrc, i, bEmitCode); - } - } - else - { - assert((mask & genRegMaskFloat((regNumber)i)) == 0 && - "A register marked as incoming live in the target block isnt live in the current block"); - } - } -} - -void CodeGen::genCodeForPrologStackFP() -{ - assert(compiler->compGeneratingProlog); - assert(compiler->fgFirstBB); - - FlatFPStateX87* pState = compiler->fgFirstBB->bbFPStateX87; - - if (pState && pState->m_uStackSize) - { - VARSET_TP liveEnregIn( - VarSetOps::Intersection(compiler, compiler->fgFirstBB->bbLiveIn, compiler->optAllFPregVars)); - unsigned i; - -#ifdef DEBUG - unsigned uLoads = 0; -#endif - - assert(pState->m_uStackSize <= FP_VIRTUALREGISTERS); - for (i = 0; i < pState->m_uStackSize; i++) - { - // Get the virtual register that matches - unsigned iVirtual = pState->STToVirtual(pState->m_uStackSize - i - 1); - - unsigned varNum; - LclVarDsc* varDsc; - - for (varNum = 0, varDsc = compiler->lvaTable; varNum < compiler->lvaCount; varNum++, varDsc++) - { - if (varDsc->IsFloatRegType() && varDsc->lvRegister && varDsc->lvRegNum == iVirtual) - { - unsigned varIndex = varDsc->lvVarIndex; - - // Is this variable live on entry? - if (VarSetOps::IsMember(compiler, liveEnregIn, varIndex)) - { - if (varDsc->lvIsParam) - { - getEmitter()->emitIns_S(INS_fld, EmitSize(varDsc->TypeGet()), varNum, 0); - } - else - { - // unitialized regvar - getEmitter()->emitIns(INS_fldz); - } - -#ifdef DEBUG - uLoads++; -#endif - break; - } - } - } - - assert(varNum != compiler->lvaCount); // We have to find the matching var!!!! - } - - assert(uLoads == VarSetOps::Count(compiler, liveEnregIn)); - } -} - -void CodeGen::genCodeForEndBlockTransitionStackFP(BasicBlock* block) -{ - switch (block->bbJumpKind) - { - case BBJ_EHFINALLYRET: - case BBJ_EHFILTERRET: - case BBJ_EHCATCHRET: - // Nothing to do - assert(compCurFPState.m_uStackSize == 0); - break; - case BBJ_THROW: - break; - case BBJ_RETURN: - // Nothing to do - assert((varTypeIsFloating(compiler->info.compRetType) && compCurFPState.m_uStackSize == 1) || - compCurFPState.m_uStackSize == 0); - break; - case BBJ_COND: - case BBJ_NONE: - genCodeForBBTransitionStackFP(block->bbNext); - break; - case BBJ_ALWAYS: - genCodeForBBTransitionStackFP(block->bbJumpDest); - break; - case BBJ_LEAVE: - assert(!"BBJ_LEAVE blocks shouldn't get here"); - break; - case BBJ_CALLFINALLY: - assert(compCurFPState.IsEmpty() && "we don't enregister variables live on entry to finallys"); - genCodeForBBTransitionStackFP(block->bbJumpDest); - break; - case BBJ_SWITCH: - // Nothing to do here - break; - default: - noway_assert(!"Unexpected bbJumpKind"); - break; - } -} - -regMaskTP CodeGen::genRegMaskFromLivenessStackFP(VARSET_VALARG_TP varset) -{ - unsigned varNum; - LclVarDsc* varDsc; - regMaskTP result = 0; - - for (varNum = 0, varDsc = compiler->lvaTable; varNum < compiler->lvaCount; varNum++, varDsc++) - { - if (varDsc->IsFloatRegType() && varDsc->lvRegister) - { - - unsigned varIndex = varDsc->lvVarIndex; - - /* Is this variable live on entry? */ - - if (VarSetOps::IsMember(compiler, varset, varIndex)) - { - // We should only call this function doing a transition - // To a block which hasn't state yet. All incoming live enregistered variables - // should have been already initialized. - assert(varDsc->lvRegNum != REG_FPNONE); - - result |= genRegMaskFloat(varDsc->lvRegNum); - } - } - } - - return result; -} - -void CodeGen::genCodeForBBTransitionStackFP(BasicBlock* pDst) -{ - assert(compCurFPState.IsConsistent()); - if (pDst->bbFPStateX87) - { - // Target block has an associated state. generate transition - genCodeForTransitionStackFP(&compCurFPState, pDst->bbFPStateX87); - } - else - { - // Target block hasn't got an associated state. As it can only possibly - // have a subset of the current state, we'll take advantage of this and - // generate the optimal transition - - // Copy current state - pDst->bbFPStateX87 = FlatFPAllocFPState(&compCurFPState); - - regMaskTP liveRegIn = - genRegMaskFromLivenessStackFP(VarSetOps::Intersection(compiler, pDst->bbLiveIn, compiler->optAllFPregVars)); - - // Match to live vars - genCodeForTransitionFromMask(pDst->bbFPStateX87, liveRegIn); - } -} - -void CodeGen::SpillTempsStackFP(regMaskTP canSpillMask) -{ - - unsigned i; - regMaskTP spillMask = 0; - regNumber reg; - - // First pass we determine which registers we spill - for (i = 0; i < compCurFPState.m_uStackSize; i++) - { - reg = (regNumber)compCurFPState.m_uStack[i]; - regMaskTP regMask = genRegMaskFloat(reg); - if ((regMask & canSpillMask) && (regMask & regSet.rsMaskRegVarFloat) == 0) - { - spillMask |= regMask; - } - } - - // Second pass we do the actual spills - for (i = REG_FPV0; i < REG_FPCOUNT; i++) - { - if ((genRegMaskFloat((regNumber)i) & spillMask)) - { - JITDUMP("spilling temp in register %s\n", regVarNameStackFP((regNumber)i)); - SpillFloat((regNumber)i, true); - } - } -} - -// Spills all the fp stack. We need this to spill -// across calls -void CodeGen::SpillForCallStackFP() -{ - unsigned i; - unsigned uSize = compCurFPState.m_uStackSize; - - for (i = 0; i < uSize; i++) - { - SpillFloat((regNumber)compCurFPState.m_uStack[compCurFPState.TopIndex()], true); - } -} - -void CodeGenInterface::SpillFloat(regNumber reg, bool bIsCall) -{ -#ifdef DEBUG - regMaskTP mask = genRegMaskFloat(reg); - - // We can allow spilling regvars, but we don't need it at the moment, and we're - // missing code in setupopforflatfp, so assert. - assert(bIsCall || (mask & (regSet.rsMaskLockedFloat | regSet.rsMaskRegVarFloat)) == 0); -#endif - - JITDUMP("SpillFloat spilling register %s\n", regVarNameStackFP(reg)); - - // We take the virtual register to the top of the stack - FlatFPX87_MoveToTOS(&compCurFPState, reg); - - // Allocate spill structure - RegSet::SpillDsc* spill = RegSet::SpillDsc::alloc(compiler, ®Set, TYP_FLOAT); - - // Fill out spill structure - var_types type; - if (regSet.genUsedRegsFloat[reg]) - { - JITDUMP("will spill tree [%08p]\n", dspPtr(regSet.genUsedRegsFloat[reg])); - // register used for temp stack - spill->spillTree = regSet.genUsedRegsFloat[reg]; - spill->bEnregisteredVariable = false; - - regSet.genUsedRegsFloat[reg]->gtFlags |= GTF_SPILLED; - - type = genActualType(regSet.genUsedRegsFloat[reg]->TypeGet()); - - // Clear used flag - regSet.SetUsedRegFloat(regSet.genUsedRegsFloat[reg], false); - } - else - { - JITDUMP("will spill varDsc [%08p]\n", dspPtr(regSet.genRegVarsFloat[reg])); - - // enregistered variable - spill->spillVarDsc = regSet.genRegVarsFloat[reg]; - assert(spill->spillVarDsc); - - spill->bEnregisteredVariable = true; - - // Mark as spilled - spill->spillVarDsc->lvSpilled = true; - type = genActualType(regSet.genRegVarsFloat[reg]->TypeGet()); - - // Clear register flag - SetRegVarFloat(reg, type, 0); - } - - // Add to spill list - spill->spillNext = regSet.rsSpillFloat; - regSet.rsSpillFloat = spill; - - // Obtain space - TempDsc* temp = spill->spillTemp = compiler->tmpGetTemp(type); - emitAttr size = EmitSize(type); - - getEmitter()->emitIns_S(INS_fstp, size, temp->tdTempNum(), 0); - compCurFPState.Pop(); -} - -void CodeGen::UnspillFloatMachineDep(RegSet::SpillDsc* spillDsc, bool useSameReg) -{ - NYI(!"Need not be implemented for x86."); -} - -void CodeGen::UnspillFloatMachineDep(RegSet::SpillDsc* spillDsc) -{ - // Do actual unspill - if (spillDsc->bEnregisteredVariable) - { - assert(spillDsc->spillVarDsc->lvSpilled); - - // Do the logic as it was a regvar birth - genRegVarBirthStackFP(spillDsc->spillVarDsc); - - // Mark as not spilled any more - spillDsc->spillVarDsc->lvSpilled = false; - - // Update stack layout. - compCurFPState.Push(spillDsc->spillVarDsc->lvRegNum); - } - else - { - assert(spillDsc->spillTree->gtFlags & GTF_SPILLED); - - spillDsc->spillTree->gtFlags &= ~GTF_SPILLED; - - regNumber reg = regSet.PickRegFloat(); - genMarkTreeInReg(spillDsc->spillTree, reg); - regSet.SetUsedRegFloat(spillDsc->spillTree, true); - - compCurFPState.Push(reg); - } - - // load from spilled spot - emitAttr size = EmitSize(spillDsc->spillTemp->tdTempType()); - getEmitter()->emitIns_S(INS_fld, size, spillDsc->spillTemp->tdTempNum(), 0); -} - -// unspills any reg var that we have in the spill list. We need this -// because we can't have any spilled vars across basic blocks -void CodeGen::UnspillRegVarsStackFp() -{ - RegSet::SpillDsc* cur; - RegSet::SpillDsc* next; - - for (cur = regSet.rsSpillFloat; cur; cur = next) - { - next = cur->spillNext; - - if (cur->bEnregisteredVariable) - { - UnspillFloat(cur); - } - } -} - -#ifdef DEBUG -const char* regNamesFP[] = { -#define REGDEF(name, rnum, mask, sname) sname, -#include "registerfp.h" -}; - -// static -const char* CodeGenInterface::regVarNameStackFP(regNumber reg) -{ - return regNamesFP[reg]; -} - -bool CodeGen::ConsistentAfterStatementStackFP() -{ - if (!compCurFPState.IsConsistent()) - { - return false; - } - - if (regSet.rsMaskUsedFloat != 0) - { - assert(!"FP register marked as used after statement"); - return false; - } - if (regSet.rsMaskLockedFloat != 0) - { - assert(!"FP register marked as locked after statement"); - return false; - } - if (genCountBits(regSet.rsMaskRegVarFloat) > compCurFPState.m_uStackSize) - { - assert(!"number of FP regvars in regSet.rsMaskRegVarFloat doesnt match current FP state"); - return false; - } - - return true; -} - -#endif - -int CodeGen::genNumberTemps() -{ - return compCurFPState.m_uStackSize - genCountBits(regSet.rsMaskRegVarFloat); -} - -void CodeGen::genDiscardStackFP(GenTree* tree) -{ - assert(tree->InReg()); - assert(varTypeIsFloating(tree)); - - FlatFPX87_Unload(&compCurFPState, tree->gtRegNum, true); -} - -void CodeGen::genRegRenameWithMasks(regNumber dstReg, regNumber srcReg) -{ - regMaskTP dstregmask = genRegMaskFloat(dstReg); - regMaskTP srcregmask = genRegMaskFloat(srcReg); - - // rename use register - compCurFPState.Rename(dstReg, srcReg); - - regSet.rsMaskUsedFloat &= ~srcregmask; - regSet.rsMaskUsedFloat |= dstregmask; - - if (srcregmask & regSet.rsMaskLockedFloat) - { - assert((dstregmask & regSet.rsMaskLockedFloat) == 0); - // We will set the new one as locked - regSet.rsMaskLockedFloat &= ~srcregmask; - regSet.rsMaskLockedFloat |= dstregmask; - } - - // Updated used tree - assert(!regSet.genUsedRegsFloat[dstReg]); - regSet.genUsedRegsFloat[dstReg] = regSet.genUsedRegsFloat[srcReg]; - regSet.genUsedRegsFloat[dstReg]->gtRegNum = dstReg; - regSet.genUsedRegsFloat[srcReg] = NULL; -} - -void CodeGen::genRegVarBirthStackFP(LclVarDsc* varDsc) -{ - // Mark the virtual register we're assigning to this local; - regNumber reg = varDsc->lvRegNum; - -#ifdef DEBUG - regMaskTP regmask = genRegMaskFloat(reg); -#endif - - assert(varDsc->lvTracked && varDsc->lvRegister && reg != REG_FPNONE); - if (regSet.genUsedRegsFloat[reg]) - { - - // Register was marked as used... will have to rename it so we can put the - // regvar where it belongs. - JITDUMP("Renaming used register %s\n", regVarNameStackFP(reg)); - - regNumber newreg; - - newreg = regSet.PickRegFloat(); - -#ifdef DEBUG - regMaskTP newregmask = genRegMaskFloat(newreg); -#endif - - // Update used mask - assert((regSet.rsMaskUsedFloat & regmask) && (regSet.rsMaskUsedFloat & newregmask) == 0); - - genRegRenameWithMasks(newreg, reg); - } - - // Mark the reg as holding a regvar - varDsc->lvSpilled = false; - SetRegVarFloat(reg, varDsc->TypeGet(), varDsc); -} - -void CodeGen::genRegVarBirthStackFP(GenTree* tree) -{ -#ifdef DEBUG - if (compiler->verbose) - { - printf("variable V%i is going live in ", tree->gtLclVarCommon.gtLclNum); - Compiler::printTreeID(tree); - printf("\n"); - } -#endif // DEBUG - - // Update register in local var - LclVarDsc* varDsc = compiler->lvaTable + tree->gtLclVarCommon.gtLclNum; - - genRegVarBirthStackFP(varDsc); - assert(tree->gtRegNum == tree->gtRegVar.gtRegNum && tree->gtRegNum == varDsc->lvRegNum); -} - -void CodeGen::genRegVarDeathStackFP(LclVarDsc* varDsc) -{ - regNumber reg = varDsc->lvRegNum; - - assert(varDsc->lvTracked && varDsc->lvRegister && reg != REG_FPNONE); - SetRegVarFloat(reg, varDsc->TypeGet(), 0); -} - -void CodeGen::genRegVarDeathStackFP(GenTree* tree) -{ -#ifdef DEBUG - if (compiler->verbose) - { - printf("register %s is going dead in ", regVarNameStackFP(tree->gtRegVar.gtRegNum)); - Compiler::printTreeID(tree); - printf("\n"); - } -#endif // DEBUG - - LclVarDsc* varDsc = compiler->lvaTable + tree->gtLclVarCommon.gtLclNum; - genRegVarDeathStackFP(varDsc); -} - -void CodeGen::genLoadStackFP(GenTree* tree, regNumber reg) -{ -#ifdef DEBUG - if (compiler->verbose) - { - printf("genLoadStackFP"); - Compiler::printTreeID(tree); - printf(" %s\n", regVarNameStackFP(reg)); - } -#endif // DEBUG - - if (tree->IsRegVar()) - { - // if it has been spilled, unspill it.% - LclVarDsc* varDsc = &compiler->lvaTable[tree->gtLclVarCommon.gtLclNum]; - if (varDsc->lvSpilled) - { - UnspillFloat(varDsc); - } - - // if it's dying, just rename the register, else load it normally - if (tree->IsRegVarDeath()) - { - genRegVarDeathStackFP(tree); - compCurFPState.Rename(reg, tree->gtRegVar.gtRegNum); - } - else - { - assert(tree->gtRegNum == tree->gtRegVar.gtRegNum); - inst_FN(INS_fld, compCurFPState.VirtualToST(tree->gtRegVar.gtRegNum)); - FlatFPX87_PushVirtual(&compCurFPState, reg); - } - } - else - { - FlatFPX87_PushVirtual(&compCurFPState, reg); - inst_FS_TT(INS_fld, tree); - } -} - -void CodeGen::genMovStackFP(GenTree* dst, regNumber dstreg, GenTree* src, regNumber srcreg) -{ - if (dstreg == REG_FPNONE && !dst->IsRegVar()) - { - regNumber reg; - - // reg to mem path - if (srcreg == REG_FPNONE) - { - assert(src->IsRegVar()); - reg = src->gtRegNum; - } - else - { - reg = srcreg; - } - - // Mov src to top of the stack - FlatFPX87_MoveToTOS(&compCurFPState, reg); - - if (srcreg != REG_FPNONE || (src->IsRegVar() && src->IsRegVarDeath())) - { - // Emit instruction - inst_FS_TT(INS_fstp, dst); - - // Update stack - compCurFPState.Pop(); - } - else - { - inst_FS_TT(INS_fst, dst); - } - } - else - { - if (dstreg == REG_FPNONE) - { - assert(dst->IsRegVar()); - dstreg = dst->gtRegNum; - } - - if (srcreg == REG_FPNONE && !src->IsRegVar()) - { - // mem to reg - assert(dst->IsRegVar() && dst->IsRegVarBirth()); - - FlatFPX87_PushVirtual(&compCurFPState, dstreg); - FlatFPX87_MoveToTOS(&compCurFPState, dstreg); - - if (src->gtOper == GT_CNS_DBL) - { - genConstantLoadStackFP(src); - } - else - { - inst_FS_TT(INS_fld, src); - } - } - else - { - // disposable reg to reg, use renaming - assert(dst->IsRegVar() && dst->IsRegVarBirth()); - assert(src->IsRegVar() || (src->InReg())); - assert(src->gtRegNum != REG_FPNONE); - - if ((src->InReg()) || (src->IsRegVar() && src->IsRegVarDeath())) - { - // src is disposable and dst is a regvar, so we'll rename src to dst - - // SetupOp should have masked out the regvar - assert(!src->IsRegVar() || !src->IsRegVarDeath() || - !(genRegMaskFloat(src->gtRegVar.gtRegNum) & regSet.rsMaskRegVarFloat)); - - // get slot that holds the value - unsigned uStack = compCurFPState.m_uVirtualMap[src->gtRegNum]; - - // unlink the slot that holds the value - compCurFPState.Unmap(src->gtRegNum); - - regNumber tgtreg = dst->gtRegVar.gtRegNum; - - compCurFPState.IgnoreConsistencyChecks(true); - - if (regSet.genUsedRegsFloat[tgtreg]) - { - // tgtreg is used, we move it to src reg. We do this here as src reg won't be - // marked as used, if tgtreg is used it srcreg will be a candidate for moving - // which is something we don't want, so we do the renaming here. - genRegRenameWithMasks(src->gtRegNum, tgtreg); - } - - compCurFPState.IgnoreConsistencyChecks(false); - - // Birth of FP var - genRegVarBirthStackFP(dst); - - // Associate target reg with source physical register - compCurFPState.Associate(tgtreg, uStack); - } - else - { - if (src->IsRegVar()) - { - // regvar that isnt dying to regvar - assert(!src->IsRegVarDeath()); - - // Birth of FP var - genRegVarBirthStackFP(dst); - - // Load register - inst_FN(INS_fld, compCurFPState.VirtualToST(src->gtRegVar.gtRegNum)); - - // update our logic stack - FlatFPX87_PushVirtual(&compCurFPState, dst->gtRegVar.gtRegNum); - } - else - { - // memory to regvar - - // Birth of FP var - genRegVarBirthStackFP(dst); - - // load into stack - inst_FS_TT(INS_fld, src); - - // update our logic stack - FlatFPX87_PushVirtual(&compCurFPState, dst->gtRegVar.gtRegNum); - } - } - } - } -} - -void CodeGen::genCodeForTreeStackFP_DONE(GenTree* tree, regNumber reg) -{ - return genCodeForTree_DONE(tree, reg); -} - -// Does the setup of the FP stack on entry to block -void CodeGen::genSetupStateStackFP(BasicBlock* block) -{ - bool bGenerate = !block->bbFPStateX87; - if (bGenerate) - { - // Allocate FP state - block->bbFPStateX87 = FlatFPAllocFPState(); - block->bbFPStateX87->Init(); - } - - // Update liveset and lock enregistered live vars on entry - VARSET_TP liveSet(VarSetOps::Intersection(compiler, block->bbLiveIn, compiler->optAllFPregVars)); - - if (!VarSetOps::IsEmpty(compiler, liveSet)) - { - unsigned varNum; - LclVarDsc* varDsc; - - for (varNum = 0, varDsc = compiler->lvaTable; varNum < compiler->lvaCount; varNum++, varDsc++) - { - if (varDsc->IsFloatRegType() && varDsc->lvRegister) - { - - unsigned varIndex = varDsc->lvVarIndex; - - // Is this variable live on entry? - if (VarSetOps::IsMember(compiler, liveSet, varIndex)) - { - JITDUMP("genSetupStateStackFP(): enregistered variable V%i is live on entry to block\n", varNum); - - assert(varDsc->lvTracked); - assert(varDsc->lvRegNum != REG_FPNONE); - - genRegVarBirthStackFP(varDsc); - - if (bGenerate) - { - // If we're generating layout, update it. - block->bbFPStateX87->Push(varDsc->lvRegNum); - } - } - } - } - } - - compCurFPState.Init(block->bbFPStateX87); - - assert(block->bbFPStateX87->IsConsistent()); -} - -regMaskTP CodeGen::genPushArgumentStackFP(GenTree* args) -{ - regMaskTP addrReg = 0; - unsigned opsz = genTypeSize(genActualType(args->TypeGet())); - - switch (args->gtOper) - { - GenTree* temp; - GenTree* fval; - size_t flopsz; - - case GT_CNS_DBL: - { - float f = 0.0; - int* addr = NULL; - if (args->TypeGet() == TYP_FLOAT) - { - f = (float)args->gtDblCon.gtDconVal; - // *(long*) (&f) used instead of *addr because of of strict - // pointer aliasing optimization. According to the ISO C/C++ - // standard, an optimizer can assume two pointers of - // non-compatible types do not point to the same memory. - inst_IV(INS_push, *((int*)(&f))); - genSinglePush(); - addrReg = 0; - } - else - { - addr = (int*)&args->gtDblCon.gtDconVal; - - // store forwarding fix for pentium 4 and Centrino - // (even for down level CPUs as we don't care about their perf any more) - fval = genMakeConst(&args->gtDblCon.gtDconVal, args->gtType, args, true); - inst_FS_TT(INS_fld, fval); - flopsz = (size_t)8; - inst_RV_IV(INS_sub, REG_ESP, flopsz, EA_PTRSIZE); - getEmitter()->emitIns_AR_R(INS_fstp, EA_ATTR(flopsz), REG_NA, REG_ESP, 0); - genSinglePush(); - genSinglePush(); - - addrReg = 0; - } - - break; - } - - case GT_CAST: - { - // Is the value a cast from double ? - if ((args->gtOper == GT_CAST) && (args->CastFromType() == TYP_DOUBLE)) - { - /* Load the value onto the FP stack */ - - genCodeForTreeFlt(args->gtCast.CastOp(), false); - - /* Go push the value as a float/double */ - args = args->gtCast.CastOp(); - - addrReg = 0; - goto PUSH_FLT; - } - // Fall through to default case.... - } - default: - { - temp = genMakeAddrOrFPstk(args, &addrReg, false); - if (temp) - { - unsigned offs; - - // We have the address of the float operand, push its bytes - offs = opsz; - assert(offs % sizeof(int) == 0); - - if (offs == 4) - { - assert(args->gtType == temp->gtType); - do - { - offs -= sizeof(int); - inst_TT(INS_push, temp, offs); - genSinglePush(); - } while (offs); - } - else - { - // store forwarding fix for pentium 4 and Centrino - inst_FS_TT(INS_fld, temp); - flopsz = (size_t)offs; - inst_RV_IV(INS_sub, REG_ESP, (size_t)flopsz, EA_PTRSIZE); - getEmitter()->emitIns_AR_R(INS_fstp, EA_ATTR(flopsz), REG_NA, REG_ESP, 0); - genSinglePush(); - genSinglePush(); - } - } - else - { - // The argument is on the FP stack -- pop it into [ESP-4/8] - - PUSH_FLT: - - inst_RV_IV(INS_sub, REG_ESP, opsz, EA_PTRSIZE); - - genSinglePush(); - if (opsz == 2 * sizeof(unsigned)) - genSinglePush(); - - // Take reg to top of stack - FlatFPX87_MoveToTOS(&compCurFPState, args->gtRegNum); - - // Pop it off to stack - compCurFPState.Pop(); - getEmitter()->emitIns_AR_R(INS_fstp, EA_ATTR(opsz), REG_NA, REG_ESP, 0); - } - - gcInfo.gcMarkRegSetNpt(addrReg); - break; - } - } - - return addrReg; -} - -void CodeGen::genRoundFpExpressionStackFP(GenTree* op, var_types type) -{ - // Do nothing with memory resident opcodes - these are the right precision - // (even if genMakeAddrOrFPstk loads them to the FP stack) - if (type == TYP_UNDEF) - type = op->TypeGet(); - - switch (op->gtOper) - { - case GT_LCL_VAR: - case GT_LCL_FLD: - case GT_CLS_VAR: - case GT_CNS_DBL: - case GT_IND: - case GT_LEA: - if (type == op->TypeGet()) - return; - default: - break; - } - - assert(op->gtRegNum != REG_FPNONE); - - // Take register to top of stack - FlatFPX87_MoveToTOS(&compCurFPState, op->gtRegNum); - - // Allocate a temp for the expression - TempDsc* temp = compiler->tmpGetTemp(type); - - // Store the FP value into the temp - inst_FS_ST(INS_fstp, EmitSize(type), temp, 0); - - // Load the value back onto the FP stack - inst_FS_ST(INS_fld, EmitSize(type), temp, 0); - - // We no longer need the temp - compiler->tmpRlsTemp(temp); -} - -void CodeGen::genCodeForTreeStackFP_Const(GenTree* tree) -{ -#ifdef DEBUG - if (compiler->verbose) - { - printf("genCodeForTreeStackFP_Const() "); - Compiler::printTreeID(tree); - printf("\n"); - } -#endif // DEBUG - -#ifdef DEBUG - if (tree->OperGet() != GT_CNS_DBL) - { - compiler->gtDispTree(tree); - assert(!"bogus float const"); - } -#endif - // Pick register - regNumber reg = regSet.PickRegFloat(); - - // Load constant - genConstantLoadStackFP(tree); - - // Push register to virtual stack - FlatFPX87_PushVirtual(&compCurFPState, reg); - - // Update tree - genCodeForTreeStackFP_DONE(tree, reg); -} - -void CodeGen::genCodeForTreeStackFP_Leaf(GenTree* tree) -{ -#ifdef DEBUG - if (compiler->verbose) - { - printf("genCodeForTreeStackFP_Leaf() "); - Compiler::printTreeID(tree); - printf("\n"); - } -#endif // DEBUG - - switch (tree->OperGet()) - { - case GT_LCL_VAR: - case GT_LCL_FLD: - { - assert(!compiler->lvaTable[tree->gtLclVarCommon.gtLclNum].lvRegister); - - // Pick register - regNumber reg = regSet.PickRegFloat(); - - // Load it - genLoadStackFP(tree, reg); - - genCodeForTreeStackFP_DONE(tree, reg); - - break; - } - - case GT_REG_VAR: - { - regNumber reg = regSet.PickRegFloat(); - - genLoadStackFP(tree, reg); - - genCodeForTreeStackFP_DONE(tree, reg); - - break; - } - - case GT_CLS_VAR: - { - // Pick register - regNumber reg = regSet.PickRegFloat(); - - // Load it - genLoadStackFP(tree, reg); - - genCodeForTreeStackFP_DONE(tree, reg); - - break; - } - - default: -#ifdef DEBUG - compiler->gtDispTree(tree); -#endif - assert(!"unexpected leaf"); - } - - genUpdateLife(tree); -} - -void CodeGen::genCodeForTreeStackFP_Asg(GenTree* tree) -{ -#ifdef DEBUG - if (compiler->verbose) - { - printf("genCodeForTreeStackFP_Asg() "); - Compiler::printTreeID(tree); - printf("\n"); - } -#endif // DEBUG - - emitAttr size; - unsigned offs; - GenTree* op1 = tree->gtOp.gtOp1; - GenTree* op2 = tree->gtGetOp2IfPresent(); - - assert(tree->OperGet() == GT_ASG); - - if (!op1->IsRegVar() && (op2->gtOper == GT_CAST) && (op1->gtType == op2->gtType) && - varTypeIsFloating(op2->gtCast.CastOp())) - { - /* We can discard the cast */ - op2 = op2->gtCast.CastOp(); - } - - size = EmitSize(op1); - offs = 0; - - // If lhs is a comma expression, evaluate the non-last parts, make op1 be the remainder. - // (But can't do this if the assignment is reversed...) - if ((tree->gtFlags & GTF_REVERSE_OPS) == 0) - { - op1 = genCodeForCommaTree(op1); - } - - GenTree* op1NonCom = op1->gtEffectiveVal(); - if (op1NonCom->gtOper == GT_LCL_VAR) - { -#ifdef DEBUG - LclVarDsc* varDsc = &compiler->lvaTable[op1NonCom->gtLclVarCommon.gtLclNum]; - // No dead stores - assert(!varDsc->lvTracked || compiler->opts.MinOpts() || !(op1NonCom->gtFlags & GTF_VAR_DEATH)); -#endif - - /* For non-debuggable code, every definition of a lcl-var has - * to be checked to see if we need to open a new scope for it. - */ - - if (compiler->opts.compScopeInfo && !compiler->opts.compDbgCode && (compiler->info.compVarScopesCount > 0)) - { - siCheckVarScope(op1NonCom->gtLclVarCommon.gtLclNum, op1NonCom->gtLclVar.gtLclILoffs); - } - } - - assert(op2); - switch (op2->gtOper) - { - case GT_CNS_DBL: - - assert(compCurFPState.m_uStackSize <= FP_PHYSICREGISTERS); - - regMaskTP addrRegInt; - addrRegInt = 0; - regMaskTP addrRegFlt; - addrRegFlt = 0; - - // op2 is already "evaluated," so doesn't matter if they're reversed or not... - op1 = genCodeForCommaTree(op1); - op1 = genMakeAddressableStackFP(op1, &addrRegInt, &addrRegFlt); - - // We want to 'cast' the constant to the op1'a type - double constantValue; - constantValue = op2->gtDblCon.gtDconVal; - if (op1->gtType == TYP_FLOAT) - { - float temp = forceCastToFloat(constantValue); - constantValue = (double)temp; - } - - GenTree* constantTree; - constantTree = compiler->gtNewDconNode(constantValue); - if (genConstantLoadStackFP(constantTree, true)) - { - if (op1->IsRegVar()) - { - // regvar birth - genRegVarBirthStackFP(op1); - - // Update - compCurFPState.Push(op1->gtRegNum); - } - else - { - // store in target - inst_FS_TT(INS_fstp, op1); - } - } - else - { - // Standard constant - if (op1->IsRegVar()) - { - // Load constant to fp stack. - - GenTree* cnsaddr; - - // Create slot for constant - if (op1->gtType == TYP_FLOAT || StackFPIsSameAsFloat(op2->gtDblCon.gtDconVal)) - { - // We're going to use that double as a float, so recompute addr - float f = forceCastToFloat(op2->gtDblCon.gtDconVal); - cnsaddr = genMakeConst(&f, TYP_FLOAT, tree, true); - } - else - { - cnsaddr = genMakeConst(&op2->gtDblCon.gtDconVal, TYP_DOUBLE, tree, true); - } - - // Load into stack - inst_FS_TT(INS_fld, cnsaddr); - - // regvar birth - genRegVarBirthStackFP(op1); - - // Update - compCurFPState.Push(op1->gtRegNum); - } - else - { - if (size == 4) - { - - float f = forceCastToFloat(op2->gtDblCon.gtDconVal); - int* addr = (int*)&f; - - do - { - inst_TT_IV(INS_mov, op1, *addr++, offs); - offs += sizeof(int); - } while (offs < size); - } - else - { - // store forwarding fix for pentium 4 and centrino and also - // fld for doubles that can be represented as floats, saving - // 4 bytes of load - GenTree* cnsaddr; - - // Create slot for constant - if (op1->gtType == TYP_FLOAT || StackFPIsSameAsFloat(op2->gtDblCon.gtDconVal)) - { - // We're going to use that double as a float, so recompute addr - float f = forceCastToFloat(op2->gtDblCon.gtDconVal); - cnsaddr = genMakeConst(&f, TYP_FLOAT, tree, true); - } - else - { - assert(tree->gtType == TYP_DOUBLE); - cnsaddr = genMakeConst(&op2->gtDblCon.gtDconVal, TYP_DOUBLE, tree, true); - } - - inst_FS_TT(INS_fld, cnsaddr); - inst_FS_TT(INS_fstp, op1); - } - } - } - - genDoneAddressableStackFP(op1, addrRegInt, addrRegFlt, RegSet::KEEP_REG); - genUpdateLife(op1); - return; - - default: - break; - } - - // Not one of the easy optimizations. Proceed normally - if (tree->gtFlags & GTF_REVERSE_OPS) - { - /* Evaluate the RHS onto the FP stack. - We don't need to round it as we will be doing a spill for - the assignment anyway (unless op1 is a GT_REG_VAR). */ - - genSetupForOpStackFP(op1, op2, true, true, false, true); - - // Do the move - genMovStackFP(op1, REG_FPNONE, op2, (op2->InReg()) ? op2->gtRegNum : REG_FPNONE); - } - else - { - // Have to evaluate left side before - - // This should never happen - assert(!op1->IsRegVar()); - - genSetupForOpStackFP(op1, op2, false, true, false, true); - - // Do the actual move - genMovStackFP(op1, REG_FPNONE, op2, (op2->InReg()) ? op2->gtRegNum : REG_FPNONE); - } -} - -void CodeGen::genSetupForOpStackFP( - GenTree*& op1, GenTree*& op2, bool bReverse, bool bMakeOp1Addressable, bool bOp1ReadOnly, bool bOp2ReadOnly) -{ - if (bMakeOp1Addressable) - { - if (bReverse) - { - genSetupForOpStackFP(op2, op1, false, false, bOp2ReadOnly, bOp1ReadOnly); - } - else - { - regMaskTP addrRegInt = 0; - regMaskTP addrRegFlt = 0; - - op1 = genCodeForCommaTree(op1); - - // Evaluate RHS on FP stack - if (bOp2ReadOnly && op2->IsRegVar() && !op2->IsRegVarDeath()) - { - // read only and not dying, so just make addressable - op1 = genMakeAddressableStackFP(op1, &addrRegInt, &addrRegFlt); - genKeepAddressableStackFP(op1, &addrRegInt, &addrRegFlt); - genUpdateLife(op2); - } - else - { - // Make target addressable - op1 = genMakeAddressableStackFP(op1, &addrRegInt, &addrRegFlt); - - op2 = genCodeForCommaTree(op2); - - genCodeForTreeFloat(op2); - - regSet.SetUsedRegFloat(op2, true); - regSet.SetLockedRegFloat(op2, true); - - // Make sure target is still adressable - genKeepAddressableStackFP(op1, &addrRegInt, &addrRegFlt); - - regSet.SetLockedRegFloat(op2, false); - regSet.SetUsedRegFloat(op2, false); - } - - /* Free up anything that was tied up by the target address */ - genDoneAddressableStackFP(op1, addrRegInt, addrRegFlt, RegSet::KEEP_REG); - } - } - else - { - assert(!bReverse || - !"Can't do this. if op2 is a reg var and dies in op1, we have a serious problem. For the " - "moment, handle this in the caller"); - - regMaskTP addrRegInt = 0; - regMaskTP addrRegFlt = 0; - - op1 = genCodeForCommaTree(op1); - - if (bOp1ReadOnly && op1->IsRegVar() && !op1->IsRegVarDeath() && - !genRegVarDiesInSubTree(op2, op1->gtRegVar.gtRegNum)) // regvar can't die in op2 either - { - // First update liveness for op1, since we're "evaluating" it here - genUpdateLife(op1); - - op2 = genCodeForCommaTree(op2); - - // read only and not dying, we dont have to do anything. - op2 = genMakeAddressableStackFP(op2, &addrRegInt, &addrRegFlt); - genKeepAddressableStackFP(op2, &addrRegInt, &addrRegFlt); - } - else - { - genCodeForTreeFloat(op1); - - regSet.SetUsedRegFloat(op1, true); - - op2 = genCodeForCommaTree(op2); - - op2 = genMakeAddressableStackFP(op2, &addrRegInt, &addrRegFlt); - - // Restore op1 if necessary - if (op1->gtFlags & GTF_SPILLED) - { - UnspillFloat(op1); - } - - // Lock op1 - regSet.SetLockedRegFloat(op1, true); - - genKeepAddressableStackFP(op2, &addrRegInt, &addrRegFlt); - - // unlock op1 - regSet.SetLockedRegFloat(op1, false); - - // mark as free - regSet.SetUsedRegFloat(op1, false); - } - - genDoneAddressableStackFP(op2, addrRegInt, addrRegFlt, RegSet::KEEP_REG); - } -} - -void CodeGen::genCodeForTreeStackFP_Arithm(GenTree* tree) -{ -#ifdef DEBUG - if (compiler->verbose) - { - printf("genCodeForTreeStackFP_Arithm() "); - Compiler::printTreeID(tree); - printf("\n"); - } -#endif // DEBUG - - assert(tree->OperGet() == GT_ADD || tree->OperGet() == GT_SUB || tree->OperGet() == GT_MUL || - tree->OperGet() == GT_DIV); - - // We handle the reverse here instead of leaving setupop to do it. As for this case - // - // + with reverse - // op1 regvar - // - // and in regvar dies in op1, we would need a load of regvar, instead of a noop. So we handle this - // here and tell genArithmStackFP to do the reverse operation - bool bReverse; - - GenTree* op1; - GenTree* op2; - - if (tree->gtFlags & GTF_REVERSE_OPS) - { - bReverse = true; - op1 = tree->gtGetOp2IfPresent(); - op2 = tree->gtOp.gtOp1; - } - else - { - bReverse = false; - op1 = tree->gtOp.gtOp1; - op2 = tree->gtGetOp2IfPresent(); - } - - regNumber result; - - // Fast paths - genTreeOps oper = tree->OperGet(); - if (op1->IsRegVar() && op2->IsRegVar() && !op1->IsRegVarDeath() && op2->IsRegVarDeath()) - { - // In this fastpath, we will save a load by doing the operation directly on the op2 - // register, as it's dying. - - // Mark op2 as dead - genRegVarDeathStackFP(op2); - - // Do operation - result = genArithmStackFP(oper, op2, op2->gtRegVar.gtRegNum, op1, REG_FPNONE, !bReverse); - - genUpdateLife(op1); - genUpdateLife(op2); - } - else if (!op1->IsRegVar() && // We don't do this for regvars, as we'll need a scratch reg - ((tree->gtFlags & GTF_SIDE_EFFECT) == 0) && // No side effects - GenTree::Compare(op1, op2)) // op1 and op2 are the same - { - // op1 is same thing as op2. Ideal for CSEs that werent optimized - // due to their low cost. - - // First we need to update lifetimes from op1 - VarSetOps::AssignNoCopy(compiler, compiler->compCurLife, genUpdateLiveSetForward(op1)); - compiler->compCurLifeTree = op1; - - genCodeForTreeFloat(op2); - - result = genArithmStackFP(oper, op2, op2->gtRegNum, op2, op2->gtRegNum, bReverse); - } - else - { - genSetupForOpStackFP(op1, op2, false, false, false, true); - - result = genArithmStackFP(oper, op1, (op1->InReg()) ? op1->gtRegNum : REG_FPNONE, op2, - (op2->InReg()) ? op2->gtRegNum : REG_FPNONE, bReverse); - } - - genCodeForTreeStackFP_DONE(tree, result); -} - -regNumber CodeGen::genArithmStackFP( - genTreeOps oper, GenTree* dst, regNumber dstreg, GenTree* src, regNumber srcreg, bool bReverse) -{ -#ifdef DEBUG - if (compiler->verbose) - { - printf("genArithmStackFP() dst: "); - Compiler::printTreeID(dst); - printf(" src: "); - Compiler::printTreeID(src); - printf(" dstreg: %s srcreg: %s\n", dstreg == REG_FPNONE ? "NONE" : regVarNameStackFP(dstreg), - srcreg == REG_FPNONE ? "NONE" : regVarNameStackFP(srcreg)); - } -#endif // DEBUG - - // Select instruction depending on oper and bReverseOp - - instruction ins_NN; - instruction ins_RN; - instruction ins_RP; - instruction ins_NP; - - switch (oper) - { - default: - assert(!"Unexpected oper"); - case GT_ADD: - case GT_SUB: - case GT_MUL: - case GT_DIV: - - /* Make sure the instruction tables look correctly ordered */ - assert(FPmathNN[GT_ADD - GT_ADD] == INS_fadd); - assert(FPmathNN[GT_SUB - GT_ADD] == INS_fsub); - assert(FPmathNN[GT_MUL - GT_ADD] == INS_fmul); - assert(FPmathNN[GT_DIV - GT_ADD] == INS_fdiv); - - assert(FPmathNP[GT_ADD - GT_ADD] == INS_faddp); - assert(FPmathNP[GT_SUB - GT_ADD] == INS_fsubp); - assert(FPmathNP[GT_MUL - GT_ADD] == INS_fmulp); - assert(FPmathNP[GT_DIV - GT_ADD] == INS_fdivp); - - assert(FPmathRN[GT_ADD - GT_ADD] == INS_fadd); - assert(FPmathRN[GT_SUB - GT_ADD] == INS_fsubr); - assert(FPmathRN[GT_MUL - GT_ADD] == INS_fmul); - assert(FPmathRN[GT_DIV - GT_ADD] == INS_fdivr); - - assert(FPmathRP[GT_ADD - GT_ADD] == INS_faddp); - assert(FPmathRP[GT_SUB - GT_ADD] == INS_fsubrp); - assert(FPmathRP[GT_MUL - GT_ADD] == INS_fmulp); - assert(FPmathRP[GT_DIV - GT_ADD] == INS_fdivrp); - - if (bReverse) - { - ins_NN = FPmathRN[oper - GT_ADD]; - ins_NP = FPmathRP[oper - GT_ADD]; - ins_RN = FPmathNN[oper - GT_ADD]; - ins_RP = FPmathNP[oper - GT_ADD]; - } - else - { - ins_NN = FPmathNN[oper - GT_ADD]; - ins_NP = FPmathNP[oper - GT_ADD]; - ins_RN = FPmathRN[oper - GT_ADD]; - ins_RP = FPmathRP[oper - GT_ADD]; - } - } - - regNumber result = REG_FPNONE; - - if (dstreg != REG_FPNONE) - { - if (srcreg == REG_FPNONE) - { - if (src->IsRegVar()) - { - if (src->IsRegVarDeath()) - { - if (compCurFPState.TopVirtual() == (unsigned)dst->gtRegNum) - { - // Do operation and store in srcreg - inst_FS(ins_RP, compCurFPState.VirtualToST(src->gtRegNum)); - - // kill current dst and rename src as dst. - FlatFPX87_Kill(&compCurFPState, dstreg); - compCurFPState.Rename(dstreg, src->gtRegNum); - } - else - { - // Take src to top of stack - FlatFPX87_MoveToTOS(&compCurFPState, src->gtRegNum); - - // do reverse and pop operation - inst_FS(ins_NP, compCurFPState.VirtualToST(dstreg)); - - // Kill the register - FlatFPX87_Kill(&compCurFPState, src->gtRegNum); - } - - assert(!src->IsRegVar() || !src->IsRegVarDeath() || - !(genRegMaskFloat(src->gtRegVar.gtRegNum) & regSet.rsMaskRegVarFloat)); - } - else - { - if (compCurFPState.TopVirtual() == (unsigned)src->gtRegNum) - { - inst_FS(ins_RN, compCurFPState.VirtualToST(dst->gtRegNum)); - } - else - { - FlatFPX87_MoveToTOS(&compCurFPState, dst->gtRegNum); - inst_FN(ins_NN, compCurFPState.VirtualToST(src->gtRegNum)); - } - } - } - else - { - // do operation with memory and store in dest - FlatFPX87_MoveToTOS(&compCurFPState, dst->gtRegNum); - inst_FS_TT(ins_NN, src); - } - } - else - { - if (dstreg == srcreg) - { - FlatFPX87_MoveToTOS(&compCurFPState, dstreg); - inst_FN(ins_NN, compCurFPState.VirtualToST(dstreg)); - } - else - { - if (compCurFPState.TopVirtual() == (unsigned)dst->gtRegNum) - { - // Do operation and store in srcreg - inst_FS(ins_RP, compCurFPState.VirtualToST(srcreg)); - - // kill current dst and rename src as dst. - FlatFPX87_Kill(&compCurFPState, dstreg); - compCurFPState.Rename(dstreg, srcreg); - } - else - { - FlatFPX87_MoveToTOS(&compCurFPState, srcreg); - - // do reverse and pop operation - inst_FS(ins_NP, compCurFPState.VirtualToST(dstreg)); - - // Kill the register - FlatFPX87_Kill(&compCurFPState, srcreg); - } - } - } - - result = dstreg; - } - else - { - assert(!"if we get here it means we didnt load op1 into a temp. Investigate why"); - } - - assert(result != REG_FPNONE); - return result; -} - -void CodeGen::genCodeForTreeStackFP_AsgArithm(GenTree* tree) -{ -#ifdef DEBUG - if (compiler->verbose) - { - printf("genCodeForTreeStackFP_AsgArithm() "); - Compiler::printTreeID(tree); - printf("\n"); - } -#endif // DEBUG - - assert(tree->OperGet() == GT_ASG_ADD || tree->OperGet() == GT_ASG_SUB || tree->OperGet() == GT_ASG_MUL || - tree->OperGet() == GT_ASG_DIV); - - GenTree* op1 = tree->gtOp.gtOp1; - GenTree* op2 = tree->gtGetOp2IfPresent(); - - genSetupForOpStackFP(op1, op2, (tree->gtFlags & GTF_REVERSE_OPS) ? true : false, true, false, true); - - regNumber result = genAsgArithmStackFP(tree->OperGet(), op1, (op1->InReg()) ? op1->gtRegNum : REG_FPNONE, op2, - (op2->InReg()) ? op2->gtRegNum : REG_FPNONE); - - genCodeForTreeStackFP_DONE(tree, result); -} - -regNumber CodeGen::genAsgArithmStackFP(genTreeOps oper, GenTree* dst, regNumber dstreg, GenTree* src, regNumber srcreg) -{ - regNumber result = REG_FPNONE; - -#ifdef DEBUG - if (compiler->verbose) - { - printf("genAsgArithmStackFP() dst: "); - Compiler::printTreeID(dst); - printf(" src: "); - Compiler::printTreeID(src); - printf(" dstreg: %s srcreg: %s\n", dstreg == REG_FPNONE ? "NONE" : regVarNameStackFP(dstreg), - srcreg == REG_FPNONE ? "NONE" : regVarNameStackFP(srcreg)); - } -#endif // DEBUG - - instruction ins_NN; - instruction ins_RN; - instruction ins_RP; - instruction ins_NP; - - switch (oper) - { - default: - assert(!"Unexpected oper"); - break; - case GT_ASG_ADD: - case GT_ASG_SUB: - case GT_ASG_MUL: - case GT_ASG_DIV: - - assert(FPmathRN[GT_ASG_ADD - GT_ASG_ADD] == INS_fadd); - assert(FPmathRN[GT_ASG_SUB - GT_ASG_ADD] == INS_fsubr); - assert(FPmathRN[GT_ASG_MUL - GT_ASG_ADD] == INS_fmul); - assert(FPmathRN[GT_ASG_DIV - GT_ASG_ADD] == INS_fdivr); - - assert(FPmathRP[GT_ASG_ADD - GT_ASG_ADD] == INS_faddp); - assert(FPmathRP[GT_ASG_SUB - GT_ASG_ADD] == INS_fsubrp); - assert(FPmathRP[GT_ASG_MUL - GT_ASG_ADD] == INS_fmulp); - assert(FPmathRP[GT_ASG_DIV - GT_ASG_ADD] == INS_fdivrp); - - ins_NN = FPmathNN[oper - GT_ASG_ADD]; - ins_NP = FPmathNP[oper - GT_ASG_ADD]; - - ins_RN = FPmathRN[oper - GT_ASG_ADD]; - ins_RP = FPmathRP[oper - GT_ASG_ADD]; - - if (dstreg != REG_FPNONE) - { - assert(!"dst should be a regvar or memory"); - } - else - { - if (dst->IsRegVar()) - { - if (src->IsRegVar()) - { - if (src->IsRegVarDeath()) - { - // Take src to top of stack - FlatFPX87_MoveToTOS(&compCurFPState, src->gtRegNum); - - // Do op - inst_FS(ins_NP, compCurFPState.VirtualToST(dst->gtRegNum)); - - // Kill the register - FlatFPX87_Kill(&compCurFPState, src->gtRegNum); - - // SetupOp should mark the regvar as dead - assert((genRegMaskFloat(src->gtRegVar.gtRegNum) & regSet.rsMaskRegVarFloat) == 0); - } - else - { - assert(src->gtRegNum == src->gtRegVar.gtRegNum && - "We shoudnt be loading regvar src on the stack as src is readonly"); - - // Take src to top of stack - FlatFPX87_MoveToTOS(&compCurFPState, src->gtRegNum); - - // Do op - inst_FS(ins_RN, compCurFPState.VirtualToST(dst->gtRegNum)); - } - } - else - { - if (srcreg == REG_FPNONE) - { - // take enregistered variable to top of stack - FlatFPX87_MoveToTOS(&compCurFPState, dst->gtRegNum); - - // Do operation with mem - inst_FS_TT(ins_NN, src); - } - else - { - // take enregistered variable to top of stack - FlatFPX87_MoveToTOS(&compCurFPState, src->gtRegNum); - - // do op - inst_FS(ins_NP, compCurFPState.VirtualToST(dst->gtRegNum)); - - // Kill the register - FlatFPX87_Kill(&compCurFPState, src->gtRegNum); - } - } - } - else - { - // To memory - if ((src->IsRegVar()) && !src->IsRegVarDeath()) - { - // We set src as read only, but as dst is in memory, we will need - // an extra physical register (which we should have, as we have a - // spare one for transitions). - // - // There used to be an assertion: assert(src->gtRegNum == src->gtRegVar.gtRegNum, ...) - // here, but there's actually no reason to assume that. AFAICT, for FP vars under stack FP, - // src->gtRegVar.gtRegNum is the allocated stack pseudo-register, but src->gtRegNum is the - // FP stack position into which that is loaded to represent a particular use of the variable. - inst_FN(INS_fld, compCurFPState.VirtualToST(src->gtRegNum)); - - // Do operation with mem - inst_FS_TT(ins_RN, dst); - - // store back - inst_FS_TT(INS_fstp, dst); - } - else - { - // put src in top of stack - FlatFPX87_MoveToTOS(&compCurFPState, srcreg); - - // Do operation with mem - inst_FS_TT(ins_RN, dst); - - // store back - inst_FS_TT(INS_fstp, dst); - - // SetupOp should have marked the regvar as dead in tat case - assert(!src->IsRegVar() || !src->IsRegVarDeath() || - (genRegMaskFloat(src->gtRegVar.gtRegNum) & regSet.rsMaskRegVarFloat) == 0); - - FlatFPX87_Kill(&compCurFPState, srcreg); - } - } - } - } - - return result; -} - -void CodeGen::genCodeForTreeStackFP_SmpOp(GenTree* tree) -{ -#ifdef DEBUG - if (compiler->verbose) - { - printf("genCodeForTreeStackFP_SmpOp() "); - Compiler::printTreeID(tree); - printf("\n"); - } -#endif // DEBUG - - assert(tree->OperKind() & GTK_SMPOP); - - switch (tree->OperGet()) - { - // Assignment - case GT_ASG: - { - genCodeForTreeStackFP_Asg(tree); - break; - } - - // Arithmetic binops - case GT_ADD: - case GT_SUB: - case GT_MUL: - case GT_DIV: - { - genCodeForTreeStackFP_Arithm(tree); - break; - } - - // Asg-Arithmetic ops - case GT_ASG_ADD: - case GT_ASG_SUB: - case GT_ASG_MUL: - case GT_ASG_DIV: - { - genCodeForTreeStackFP_AsgArithm(tree); - break; - } - - case GT_IND: - case GT_LEA: - { - regMaskTP addrReg; - - // Make sure the address value is 'addressable' */ - addrReg = genMakeAddressable(tree, 0, RegSet::FREE_REG); - - // Load the value onto the FP stack - regNumber reg = regSet.PickRegFloat(); - genLoadStackFP(tree, reg); - - genDoneAddressable(tree, addrReg, RegSet::FREE_REG); - - genCodeForTreeStackFP_DONE(tree, reg); - - break; - } - - case GT_RETURN: - { - GenTree* op1 = tree->gtOp.gtOp1; - assert(op1); - - // Compute the result onto the FP stack - if (op1->gtType == TYP_FLOAT) - { -#if ROUND_FLOAT - bool roundOp1 = false; - - switch (getRoundFloatLevel()) - { - case ROUND_NEVER: - /* No rounding at all */ - break; - - case ROUND_CMP_CONST: - break; - - case ROUND_CMP: - /* Round all comparands and return values*/ - roundOp1 = true; - break; - - case ROUND_ALWAYS: - /* Round everything */ - roundOp1 = true; - break; - - default: - assert(!"Unsupported Round Level"); - break; - } -#endif - genCodeForTreeFlt(op1); - } - else - { - assert(op1->gtType == TYP_DOUBLE); - genCodeForTreeFloat(op1); - -#if ROUND_FLOAT - if ((op1->gtOper == GT_CAST) && (op1->CastFromType() == TYP_LONG)) - genRoundFpExpressionStackFP(op1); -#endif - } - - // kill enregistered variables - compCurFPState.Pop(); - assert(compCurFPState.m_uStackSize == 0); - break; - } - - case GT_COMMA: - { - GenTree* op1 = tree->gtOp.gtOp1; - GenTree* op2 = tree->gtGetOp2IfPresent(); - - if (tree->gtFlags & GTF_REVERSE_OPS) - { - genCodeForTreeFloat(op2); - - regSet.SetUsedRegFloat(op2, true); - - genEvalSideEffects(op1); - - if (op2->gtFlags & GTF_SPILLED) - { - UnspillFloat(op2); - } - - regSet.SetUsedRegFloat(op2, false); - } - else - { - genEvalSideEffects(op1); - genCodeForTreeFloat(op2); - } - - genCodeForTreeStackFP_DONE(tree, op2->gtRegNum); - break; - } - case GT_CAST: - { - genCodeForTreeStackFP_Cast(tree); - break; - } - - case GT_NEG: - { - GenTree* op1 = tree->gtOp.gtOp1; - - // get the tree into a register - genCodeForTreeFloat(op1); - - // Take reg to top of stack - FlatFPX87_MoveToTOS(&compCurFPState, op1->gtRegNum); - - // change the sign - instGen(INS_fchs); - - // mark register that holds tree - genCodeForTreeStackFP_DONE(tree, op1->gtRegNum); - return; - } - case GT_INTRINSIC: - { - assert(compiler->IsMathIntrinsic(tree)); - - GenTree* op1 = tree->gtOp.gtOp1; - - // get tree into a register - genCodeForTreeFloat(op1); - - // Take reg to top of stack - FlatFPX87_MoveToTOS(&compCurFPState, op1->gtRegNum); - - static const instruction mathIns[] = { - INS_fsin, INS_fcos, INS_invalid, INS_fsqrt, INS_fabs, INS_frndint, - }; - - assert(mathIns[CORINFO_INTRINSIC_Sin] == INS_fsin); - assert(mathIns[CORINFO_INTRINSIC_Cos] == INS_fcos); - assert(mathIns[CORINFO_INTRINSIC_Sqrt] == INS_fsqrt); - assert(mathIns[CORINFO_INTRINSIC_Abs] == INS_fabs); - assert(mathIns[CORINFO_INTRINSIC_Round] == INS_frndint); - assert((unsigned)(tree->gtIntrinsic.gtIntrinsicId) < _countof(mathIns)); - instGen(mathIns[tree->gtIntrinsic.gtIntrinsicId]); - - // mark register that holds tree - genCodeForTreeStackFP_DONE(tree, op1->gtRegNum); - - return; - } - case GT_CKFINITE: - { - TempDsc* temp; - int offs; - - GenTree* op1 = tree->gtOp.gtOp1; - - // Offset of the DWord containing the exponent - offs = (op1->gtType == TYP_FLOAT) ? 0 : sizeof(int); - - // get tree into a register - genCodeForTreeFloat(op1); - - // Take reg to top of stack - FlatFPX87_MoveToTOS(&compCurFPState, op1->gtRegNum); - - temp = compiler->tmpGetTemp(op1->TypeGet()); - emitAttr size = EmitSize(op1); - - // Store the value from the FP stack into the temp - getEmitter()->emitIns_S(INS_fst, size, temp->tdTempNum(), 0); - - regNumber reg = regSet.rsPickReg(); - - // Load the DWord containing the exponent into a general reg. - inst_RV_ST(INS_mov, reg, temp, offs, op1->TypeGet(), EA_4BYTE); - compiler->tmpRlsTemp(temp); - - // 'reg' now contains the DWord containing the exponent - regTracker.rsTrackRegTrash(reg); - - // Mask of exponent with all 1's - appropriate for given type - - int expMask; - expMask = (op1->gtType == TYP_FLOAT) ? 0x7F800000 // TYP_FLOAT - : 0x7FF00000; // TYP_DOUBLE - - // Check if the exponent is all 1's - - inst_RV_IV(INS_and, reg, expMask, EA_4BYTE); - inst_RV_IV(INS_cmp, reg, expMask, EA_4BYTE); - - // If exponent was all 1's, we need to throw ArithExcep - genJumpToThrowHlpBlk(EJ_je, SCK_ARITH_EXCPN); - - genUpdateLife(tree); - - genCodeForTreeStackFP_DONE(tree, op1->gtRegNum); - break; - } - default: - NYI("opertype"); - } -} - -void CodeGen::genCodeForTreeStackFP_Cast(GenTree* tree) -{ -#ifdef DEBUG - if (compiler->verbose) - { - printf("genCodeForTreeStackFP_Cast() "); - Compiler::printTreeID(tree); - printf("\n"); - } -#endif // DEBUG - -#if ROUND_FLOAT - bool roundResult = true; -#endif - - regMaskTP addrReg; - TempDsc* temp; - emitAttr size; - - GenTree* op1 = tree->gtOp.gtOp1; - - // If op1 is a comma expression, evaluate the non-last parts, make op1 be the rest. - op1 = genCodeForCommaTree(op1); - - switch (op1->gtType) - { - case TYP_BOOL: - case TYP_BYTE: - case TYP_UBYTE: - case TYP_USHORT: - case TYP_SHORT: - { - - // Operand too small for 'fild', load it into a register - genCodeForTree(op1, 0); - -#if ROUND_FLOAT - // no need to round, can't overflow float or dbl - roundResult = false; -#endif - - // fall through - } - case TYP_INT: - case TYP_BYREF: - case TYP_LONG: - { - // Can't 'fild' a constant, it has to be loaded from memory - switch (op1->gtOper) - { - case GT_CNS_INT: - op1 = genMakeConst(&op1->gtIntCon.gtIconVal, TYP_INT, tree, false); - break; - - case GT_CNS_LNG: - // Our encoder requires fild on m64int to be 64-bit aligned. - op1 = genMakeConst(&op1->gtLngCon.gtLconVal, TYP_LONG, tree, true); - break; - default: - break; - } - - addrReg = genMakeAddressable(op1, 0, RegSet::FREE_REG); - - // Grab register for the cast - regNumber reg = regSet.PickRegFloat(); - genMarkTreeInReg(tree, reg); - compCurFPState.Push(reg); - - // Is the value now sitting in a register? - if (op1->InReg()) - { - // We'll have to store the value into the stack */ - size = EA_ATTR(roundUp(genTypeSize(op1->gtType))); - temp = compiler->tmpGetTemp(op1->TypeGet()); - - // Move the value into the temp - if (op1->gtType == TYP_LONG) - { - regPairNo regPair = op1->gtRegPair; - - // This code is pretty ugly, but straightforward - - if (genRegPairLo(regPair) == REG_STK) - { - regNumber rg1 = genRegPairHi(regPair); - - assert(rg1 != REG_STK); - - /* Move enregistered half to temp */ - - inst_ST_RV(INS_mov, temp, 4, rg1, TYP_LONG); - - /* Move lower half to temp via "high register" */ - - inst_RV_TT(INS_mov, rg1, op1, 0); - inst_ST_RV(INS_mov, temp, 0, rg1, TYP_LONG); - - /* Reload transfer register */ - - inst_RV_ST(INS_mov, rg1, temp, 4, TYP_LONG); - } - else if (genRegPairHi(regPair) == REG_STK) - { - regNumber rg1 = genRegPairLo(regPair); - - assert(rg1 != REG_STK); - - /* Move enregistered half to temp */ - - inst_ST_RV(INS_mov, temp, 0, rg1, TYP_LONG); - - /* Move high half to temp via "low register" */ - - inst_RV_TT(INS_mov, rg1, op1, 4); - inst_ST_RV(INS_mov, temp, 4, rg1, TYP_LONG); - - /* Reload transfer register */ - - inst_RV_ST(INS_mov, rg1, temp, 0, TYP_LONG); - } - else - { - /* Move the value into the temp */ - - inst_ST_RV(INS_mov, temp, 0, genRegPairLo(regPair), TYP_LONG); - inst_ST_RV(INS_mov, temp, 4, genRegPairHi(regPair), TYP_LONG); - } - genDoneAddressable(op1, addrReg, RegSet::FREE_REG); - - /* Load the long from the temp */ - - inst_FS_ST(INS_fildl, size, temp, 0); - } - else - { - /* Move the value into the temp */ - - inst_ST_RV(INS_mov, temp, 0, op1->gtRegNum, TYP_INT); - - genDoneAddressable(op1, addrReg, RegSet::FREE_REG); - - /* Load the integer from the temp */ - - inst_FS_ST(INS_fild, size, temp, 0); - } - - // We no longer need the temp - compiler->tmpRlsTemp(temp); - } - else - { - // Load the value from its address - if (op1->gtType == TYP_LONG) - inst_TT(INS_fildl, op1); - else - inst_TT(INS_fild, op1); - - genDoneAddressable(op1, addrReg, RegSet::FREE_REG); - } - -#if ROUND_FLOAT - /* integer to fp conversions can overflow. roundResult - * is cleared above in cases where it can't - */ - if (roundResult && - ((tree->gtType == TYP_FLOAT) || ((tree->gtType == TYP_DOUBLE) && (op1->gtType == TYP_LONG)))) - genRoundFpExpression(tree); -#endif - - break; - } - case TYP_FLOAT: - { - // This is a cast from float to double. - // Note that conv.r(r4/r8) and conv.r8(r4/r9) are indistinguishable - // as we will generate GT_CAST-TYP_DOUBLE for both. This would - // cause us to truncate precision in either case. However, - // conv.r was needless in the first place, and should have - // been removed */ - genCodeForTreeFloat(op1); // Trucate its precision - - if (op1->gtOper == GT_LCL_VAR || op1->gtOper == GT_LCL_FLD || op1->gtOper == GT_CLS_VAR || - op1->gtOper == GT_IND || op1->gtOper == GT_LEA) - { - // We take advantage here of the fact that we know that our - // codegen will have just loaded this from memory, and that - // therefore, no cast is really needed. - // Ideally we wouldn't do this optimization here, but in - // morphing, however, we need to do this after regalloc, as - // this optimization doesnt apply if what we're loading is a - // regvar - } - else - { - genRoundFpExpressionStackFP(op1, tree->TypeGet()); - } - - // Assign reg to tree - genMarkTreeInReg(tree, op1->gtRegNum); - - break; - } - case TYP_DOUBLE: - { - // This is a cast from double to float or double - // Load the value, store as destType, load back - genCodeForTreeFlt(op1); - - if ((op1->gtOper == GT_LCL_VAR || op1->gtOper == GT_LCL_FLD || op1->gtOper == GT_CLS_VAR || - op1->gtOper == GT_IND || op1->gtOper == GT_LEA) && - tree->TypeGet() == TYP_DOUBLE) - { - // We take advantage here of the fact that we know that our - // codegen will have just loaded this from memory, and that - // therefore, no cast is really needed. - // Ideally we wouldn't do this optimization here, but in - // morphing. However, we need to do this after regalloc, as - // this optimization doesnt apply if what we're loading is a - // regvar - } - else - { - genRoundFpExpressionStackFP(op1, tree->TypeGet()); - } - - // Assign reg to tree - genMarkTreeInReg(tree, op1->gtRegNum); - - break; - } - default: - { - assert(!"unsupported cast"); - break; - } - } -} - -void CodeGen::genCodeForTreeStackFP_Special(GenTree* tree) -{ -#ifdef DEBUG - if (compiler->verbose) - { - printf("genCodeForTreeStackFP_Special() "); - Compiler::printTreeID(tree); - printf("\n"); - } -#endif // DEBUG - - switch (tree->OperGet()) - { - case GT_CALL: - { - genCodeForCall(tree->AsCall(), true); - break; - } - default: - NYI("genCodeForTreeStackFP_Special"); - break; - } -} - -void CodeGen::genCodeForTreeFloat(GenTree* tree, RegSet::RegisterPreference* pref) -{ - // TestTransitions(); - genTreeOps oper; - unsigned kind; - - assert(tree); - assert(tree->gtOper != GT_STMT); - assert(varTypeIsFloating(tree)); - - // What kind of node do we have? - oper = tree->OperGet(); - kind = tree->OperKind(); - - if (kind & GTK_CONST) - { - genCodeForTreeStackFP_Const(tree); - } - else if (kind & GTK_LEAF) - { - genCodeForTreeStackFP_Leaf(tree); - } - else if (kind & GTK_SMPOP) - { - genCodeForTreeStackFP_SmpOp(tree); - } - else - { - genCodeForTreeStackFP_Special(tree); - } - -#ifdef DEBUG - if (verbose) - { - JitDumpFPState(); - } - assert(compCurFPState.IsConsistent()); -#endif -} - -bool CodeGen::genCompInsStackFP(GenTree* tos, GenTree* other) -{ - // assume gensetupop done - - bool bUseFcomip = genUse_fcomip(); - bool bReverse = false; - - // Take op1 to top of the stack - FlatFPX87_MoveToTOS(&compCurFPState, tos->gtRegNum); - - // We pop top of stack if it's not a live regvar - bool bPopTos = !(tos->IsRegVar() && !tos->IsRegVarDeath()) || (tos->InReg()); - bool bPopOther = !(other->IsRegVar() && !other->IsRegVarDeath()) || (other->InReg()); - - assert(tos->IsRegVar() || (tos->InReg())); - - if (!(other->IsRegVar() || (other->InReg()))) - { - // op2 in memory - assert(bPopOther); - - if (bUseFcomip) - { - // We should have space for a load - assert(compCurFPState.m_uStackSize < FP_PHYSICREGISTERS); - - // load from mem, now the comparison will be the other way around - inst_FS_TT(INS_fld, other); - inst_FN(INS_fcomip, 1); - - // pop if we've been asked to do so - if (bPopTos) - { - inst_FS(INS_fstp, 0); - FlatFPX87_Kill(&compCurFPState, tos->gtRegNum); - } - - bReverse = true; - } - else - { - // compare directly with memory - if (bPopTos) - { - inst_FS_TT(INS_fcomp, other); - FlatFPX87_Kill(&compCurFPState, tos->gtRegNum); - } - else - { - inst_FS_TT(INS_fcom, other); - } - } - } - else - { - if (bUseFcomip) - { - if (bPopTos) - { - inst_FN(INS_fcomip, compCurFPState.VirtualToST(other->gtRegNum)); - FlatFPX87_Kill(&compCurFPState, tos->gtRegNum); - } - else - { - inst_FN(INS_fcomi, compCurFPState.VirtualToST(other->gtRegNum)); - } - - if (bPopOther) - { - FlatFPX87_Unload(&compCurFPState, other->gtRegNum); - } - } - else - { - if (bPopTos) - { - inst_FN(INS_fcomp, compCurFPState.VirtualToST(other->gtRegNum)); - FlatFPX87_Kill(&compCurFPState, tos->gtRegNum); - } - else - { - inst_FN(INS_fcom, compCurFPState.VirtualToST(other->gtRegNum)); - } - - if (bPopOther) - { - FlatFPX87_Unload(&compCurFPState, other->gtRegNum); - } - } - } - - if (!bUseFcomip) - { - // oops, we have to put result of compare in eflags - - // Grab EAX for the result of the fnstsw - regSet.rsGrabReg(RBM_EAX); - - // Generate the 'fnstsw' and test its result - inst_RV(INS_fnstsw, REG_EAX, TYP_INT); - regTracker.rsTrackRegTrash(REG_EAX); - instGen(INS_sahf); - } - - return bReverse; -} - -void CodeGen::genCondJumpFltStackFP(GenTree* cond, BasicBlock* jumpTrue, BasicBlock* jumpFalse, bool bDoTransition) -{ - assert(jumpTrue && jumpFalse); - assert(!(cond->gtFlags & GTF_REVERSE_OPS)); // Done in genCondJump() - assert(varTypeIsFloating(cond->gtOp.gtOp1)); - - GenTree* op1 = cond->gtOp.gtOp1; - GenTree* op2 = cond->gtOp.gtOp2; - genTreeOps cmp = cond->OperGet(); - - // Prepare operands. - genSetupForOpStackFP(op1, op2, false, false, true, false); - - GenTree* tos; - GenTree* other; - bool bReverseCmp = false; - - if ((op2->IsRegVar() || (op2->InReg())) && // op2 is in a reg - (compCurFPState.TopVirtual() == (unsigned)op2->gtRegNum && // Is it already at the top of the stack? - (!op2->IsRegVar() || op2->IsRegVarDeath()))) // are we going to pop it off? - { - tos = op2; - other = op1; - bReverseCmp = true; - } - else - { - tos = op1; - other = op2; - bReverseCmp = false; - } - - if (genCompInsStackFP(tos, other)) - { - bReverseCmp = !bReverseCmp; - } - - // do .un comparison - if (cond->gtFlags & GTF_RELOP_NAN_UN) - { - // Generate the first jump (NaN check) - genCondJmpInsStackFP(EJ_jpe, jumpTrue, NULL, bDoTransition); - } - else - { - jumpFalse->bbFlags |= BBF_JMP_TARGET | BBF_HAS_LABEL; - - // Generate the first jump (NaN check) - genCondJmpInsStackFP(EJ_jpe, jumpFalse, NULL, bDoTransition); - } - - /* Generate the second jump (comparison) */ - const static BYTE dblCmpTstJmp2[] = { - EJ_je, // GT_EQ - EJ_jne, // GT_NE - EJ_jb, // GT_LT - EJ_jbe, // GT_LE - EJ_jae, // GT_GE - EJ_ja, // GT_GT - }; - - // Swap comp order if necessary - if (bReverseCmp) - { - cmp = GenTree::SwapRelop(cmp); - } - - genCondJmpInsStackFP((emitJumpKind)dblCmpTstJmp2[cmp - GT_EQ], jumpTrue, jumpFalse, bDoTransition); -} - -BasicBlock* CodeGen::genTransitionBlockStackFP(FlatFPStateX87* pState, BasicBlock* pFrom, BasicBlock* pTarget) -{ - // Fast paths where a transition block is not necessary - if ((pTarget->bbFPStateX87 && FlatFPStateX87::AreEqual(pState, pTarget->bbFPStateX87)) || pState->IsEmpty()) - { - return pTarget; - } - - // We shouldn't have any handlers if we're generating transition blocks, as we don't know - // how to recover them - assert(compiler->compMayHaveTransitionBlocks); - assert(compiler->compHndBBtabCount == 0); - -#ifdef DEBUG - compiler->fgSafeBasicBlockCreation = true; -#endif - - // Create a temp block - BasicBlock* pBlock = compiler->bbNewBasicBlock(BBJ_ALWAYS); - -#ifdef DEBUG - compiler->fgSafeBasicBlockCreation = false; -#endif - - VarSetOps::Assign(compiler, pBlock->bbLiveIn, pFrom->bbLiveOut); - VarSetOps::Assign(compiler, pBlock->bbLiveOut, pFrom->bbLiveOut); - - pBlock->bbJumpDest = pTarget; - pBlock->bbFlags |= BBF_JMP_TARGET; - // - // If either pFrom or pTarget are cold blocks then - // the transition block also must be cold - // - pBlock->bbFlags |= (pFrom->bbFlags & BBF_COLD); - pBlock->bbFlags |= (pTarget->bbFlags & BBF_COLD); - - // The FP state for the block is the same as the current one - pBlock->bbFPStateX87 = FlatFPAllocFPState(pState); - - if ((pBlock->bbFlags & BBF_COLD) || (compiler->fgFirstColdBlock == NULL)) - { - // - // If this block is cold or if all blocks are hot - // then we just insert it at the end of the method. - // - compiler->fgMoveBlocksAfter(pBlock, pBlock, compiler->fgLastBBInMainFunction()); - } - else - { - // - // This block is hot so we need to insert it in the hot region - // of the method. - // - BasicBlock* lastHotBlock = compiler->fgFirstColdBlock->bbPrev; - noway_assert(lastHotBlock != nullptr); - - if (lastHotBlock->bbFallsThrough()) - NO_WAY("Bad fgFirstColdBlock in genTransitionBlockStackFP()"); - - // - // Insert pBlock between lastHotBlock and fgFirstColdBlock - // - compiler->fgInsertBBafter(lastHotBlock, pBlock); - } - - return pBlock; -} - -void CodeGen::genCondJumpLngStackFP(GenTree* cond, BasicBlock* jumpTrue, BasicBlock* jumpFalse) -{ - // For the moment, and so we don't have to deal with the amount of special cases - // we have, will insert a dummy block for jumpTrue (if necessary) that will do the - // transition for us. For the jumpFalse case, we play a trick. For the false case , - // a Long conditional has a fallthrough (least significant DWORD check is false) and - // also has a jump to the fallthrough (bbNext) if the most significant DWORD check - // fails. However, we do want to make an FP transition if we're in the later case, - // So what we do is create a label and make jumpFalse go there. This label is defined - // before doing the FP transition logic at the end of the block, so now both exit paths - // for false condition will go through the transition and then fall through to bbnext. - assert(jumpFalse == compiler->compCurBB->bbNext); - - BasicBlock* pTransition = genCreateTempLabel(); - - genCondJumpLng(cond, jumpTrue, pTransition, true); - - genDefineTempLabel(pTransition); -} - -void CodeGen::genQMarkRegVarTransition(GenTree* nextNode, VARSET_VALARG_TP liveset) -{ - // Kill any vars that may die in the transition - VARSET_TP newLiveSet(VarSetOps::Intersection(compiler, liveset, compiler->optAllFPregVars)); - - regMaskTP liveRegIn = genRegMaskFromLivenessStackFP(newLiveSet); - genCodeForTransitionFromMask(&compCurFPState, liveRegIn); - - unsigned i; - - // Kill all regvars - for (i = REG_FPV0; i < REG_FPCOUNT; i++) - { - if ((genRegMaskFloat((regNumber)i) & regSet.rsMaskRegVarFloat)) - { - - genRegVarDeathStackFP(regSet.genRegVarsFloat[i]); - } - } - - // Born necessary regvars - for (i = 0; i < compiler->lvaTrackedCount; i++) - { - unsigned lclVar = compiler->lvaTrackedToVarNum[i]; - LclVarDsc* varDsc = compiler->lvaTable + lclVar; - - assert(varDsc->lvTracked); - - if (varDsc->lvRegister && VarSetOps::IsMember(compiler, newLiveSet, i)) - { - genRegVarBirthStackFP(varDsc); - } - } -} - -void CodeGen::genQMarkBeforeElseStackFP(QmarkStateStackFP* pState, VARSET_VALARG_TP varsetCond, GenTree* nextNode) -{ - assert(regSet.rsMaskLockedFloat == 0); - - // Save current state at colon - pState->stackState.Init(&compCurFPState); - - // Kill any vars that may die in the transition to then - genQMarkRegVarTransition(nextNode, varsetCond); -} - -void CodeGen::genQMarkAfterElseBlockStackFP(QmarkStateStackFP* pState, VARSET_VALARG_TP varsetCond, GenTree* nextNode) -{ - assert(regSet.rsMaskLockedFloat == 0); - - FlatFPStateX87 tempSwap; - - // Save current state. Now tempFPState will store the target state for the else block - tempSwap.Init(&compCurFPState); - - compCurFPState.Init(&pState->stackState); - - pState->stackState.Init(&tempSwap); - - // Did any regvars die in the then block that are live on entry to the else block? - unsigned i; - for (i = 0; i < compiler->lvaTrackedCount; i++) - { - if (VarSetOps::IsMember(compiler, varsetCond, i) && VarSetOps::IsMember(compiler, compiler->optAllFPregVars, i)) - { - // This variable should be live - unsigned lclnum = compiler->lvaTrackedToVarNum[i]; - LclVarDsc* varDsc = compiler->lvaTable + lclnum; - - if (regSet.genRegVarsFloat[varDsc->lvRegNum] != varDsc) - { - JITDUMP("genQMarkAfterThenBlockStackFP(): Fixing up regvar that was modified in then\n"); - if (regSet.genRegVarsFloat[varDsc->lvRegNum]) - { - genRegVarDeathStackFP(regSet.genRegVarsFloat[varDsc->lvRegNum]); - } - - genRegVarBirthStackFP(varDsc); - } - } - } - - // Kill any vars that may die in the transition - genQMarkRegVarTransition(nextNode, varsetCond); -} - -void CodeGen::genQMarkAfterThenBlockStackFP(QmarkStateStackFP* pState) -{ - JITDUMP("genQMarkAfterThenBlockStackFP()\n"); - assert(regSet.rsMaskLockedFloat == 0); - - // Generate transition to the previous one set by the then block - genCodeForTransitionStackFP(&compCurFPState, &pState->stackState); - - // Update state - compCurFPState.Init(&pState->stackState); -} - -void CodeGenInterface::SetRegVarFloat(regNumber reg, var_types type, LclVarDsc* varDsc) -{ - regMaskTP mask = genRegMaskFloat(reg, type); - - if (varDsc) - { - JITDUMP("marking register %s as a regvar\n", getRegNameFloat(reg, type)); - - assert(mask && ((regSet.rsMaskLockedFloat | regSet.rsMaskRegVarFloat | regSet.rsMaskUsedFloat) & mask) == 0); - - regSet.rsMaskRegVarFloat |= mask; - } - else - { - JITDUMP("unmarking register %s as a regvar\n", getRegNameFloat(reg, type)); - - assert(mask && (regSet.rsMaskRegVarFloat & mask)); - - regSet.rsMaskRegVarFloat &= ~mask; - } - - // Update lookup table - regSet.genRegVarsFloat[reg] = varDsc; -} - -// Generates a conditional jump. It will do the appropiate stack matching for the jmpTrue. -// We don't use jumpFalse anywhere and the integer codebase assumes that it will be bbnext, and that is -// taken care of at the end of the bb code generation. -void CodeGen::genCondJmpInsStackFP(emitJumpKind jumpKind, - BasicBlock* jumpTrue, - BasicBlock* jumpFalse, - bool bDoTransition) -{ - // Assert the condition above. - assert(!jumpFalse || jumpFalse == compiler->compCurBB->bbNext || !bDoTransition); - - // Do the fp stack matching. - if (bDoTransition && !jumpTrue->bbFPStateX87 && - FlatFPSameRegisters(&compCurFPState, genRegMaskFromLivenessStackFP(jumpTrue->bbLiveIn))) - { - // Target block doesn't have state yet, but has the same registers, so - // we allocate the block and generate the normal jump - genCodeForBBTransitionStackFP(jumpTrue); - inst_JMP(jumpKind, jumpTrue); - } - else if (!bDoTransition || compCurFPState.IsEmpty() || // If it's empty, target has to be empty too. - (jumpTrue->bbFPStateX87 && FlatFPStateX87::AreEqual(&compCurFPState, jumpTrue->bbFPStateX87))) - { - // Nothing to do here. Proceed normally and generate the jump - inst_JMP(jumpKind, jumpTrue); - - if (jumpFalse && jumpFalse != compiler->compCurBB->bbNext) - { - inst_JMP(EJ_jmp, jumpFalse); - } - } - else - { - // temporal workaround for stack matching - // do a forward conditional jump, generate the transition and jump to the target - // The payload is an aditional jump instruction, but both jumps will be correctly - // predicted by the processor in the loop case. - BasicBlock* endLabel = NULL; - - endLabel = genCreateTempLabel(); - - inst_JMP(emitter::emitReverseJumpKind(jumpKind), endLabel); - - genCodeForBBTransitionStackFP(jumpTrue); - - inst_JMP(EJ_jmp, jumpTrue); - - genDefineTempLabel(endLabel); - } -} - -void CodeGen::genTableSwitchStackFP(regNumber reg, unsigned jumpCnt, BasicBlock** jumpTab) -{ - // Only come here when we have to do something special for the FPU stack! - // - assert(!compCurFPState.IsEmpty()); - VARSET_TP liveInFP(VarSetOps::MakeEmpty(compiler)); - VARSET_TP liveOutFP(VarSetOps::MakeEmpty(compiler)); - for (unsigned i = 0; i < jumpCnt; i++) - { - VarSetOps::Assign(compiler, liveInFP, jumpTab[i]->bbLiveIn); - VarSetOps::IntersectionD(compiler, liveInFP, compiler->optAllFPregVars); - VarSetOps::Assign(compiler, liveOutFP, compiler->compCurBB->bbLiveOut); - VarSetOps::IntersectionD(compiler, liveOutFP, compiler->optAllFPregVars); - - if (!jumpTab[i]->bbFPStateX87 && VarSetOps::Equal(compiler, liveInFP, liveOutFP)) - { - // Hasn't state yet and regvar set is the same, so just copy state and don't change the jump - jumpTab[i]->bbFPStateX87 = FlatFPAllocFPState(&compCurFPState); - } - else if (jumpTab[i]->bbFPStateX87 && FlatFPStateX87::AreEqual(&compCurFPState, jumpTab[i]->bbFPStateX87)) - { - // Same state, don't change the jump - } - else - { - // We have to do a transition. First check if we can reuse another one - unsigned j; - for (j = 0; j < i; j++) - { - // Has to be already forwarded. If not it can't be targetting the same block - if (jumpTab[j]->bbFlags & BBF_FORWARD_SWITCH) - { - if (jumpTab[i] == jumpTab[j]->bbJumpDest) - { - // yipee, we can reuse this transition block - jumpTab[i] = jumpTab[j]; - break; - } - } - } - - if (j == i) - { - // We will have to create a new transition block - jumpTab[i] = genTransitionBlockStackFP(&compCurFPState, compiler->compCurBB, jumpTab[i]); - - jumpTab[i]->bbFlags |= BBF_FORWARD_SWITCH; - } - } - } - - // Clear flag - for (unsigned i = 0; i < jumpCnt; i++) - { - jumpTab[i]->bbFlags &= ~BBF_FORWARD_SWITCH; - } - - // everything's fixed now, so go down the normal path - return genTableSwitch(reg, jumpCnt, jumpTab); -} - -bool CodeGen::genConstantLoadStackFP(GenTree* tree, bool bOnlyNoMemAccess) -{ - assert(tree->gtOper == GT_CNS_DBL); - - bool bFastConstant = false; - instruction ins_ConstantNN = INS_fldz; // keep compiler happy - - // Both positive 0 and 1 are represnetable in float and double, beware if we add other constants - switch (*((__int64*)&(tree->gtDblCon.gtDconVal))) - { - case 0: - // CAREFUL here!, -0 is different than +0, a -0 shouldn't issue a fldz. - ins_ConstantNN = INS_fldz; - bFastConstant = true; - break; - case I64(0x3ff0000000000000): - ins_ConstantNN = INS_fld1; - bFastConstant = true; - } - - if (bFastConstant == false && bOnlyNoMemAccess) - { - // Caller asked only to generate instructions if it didn't involve memory accesses - return false; - } - - if (bFastConstant) - { - assert(compCurFPState.m_uStackSize <= FP_PHYSICREGISTERS); - instGen(ins_ConstantNN); - } - else - { - GenTree* addr; - if (tree->gtType == TYP_FLOAT || StackFPIsSameAsFloat(tree->gtDblCon.gtDconVal)) - { - float f = forceCastToFloat(tree->gtDblCon.gtDconVal); - addr = genMakeConst(&f, TYP_FLOAT, tree, false); - } - else - { - addr = genMakeConst(&tree->gtDblCon.gtDconVal, tree->gtType, tree, true); - } - - inst_FS_TT(INS_fld, addr); - } - - return true; -} - -// Function called at the end of every statement. For stack based x87 its mission is to -// remove any remaining temps on the stack. -void CodeGen::genEndOfStatement() -{ - unsigned i; - -#ifdef DEBUG - // Sanity check - unsigned uTemps = 0; - for (i = REG_FPV0; i < REG_FPCOUNT; i++) - { - if (compCurFPState.Mapped(i) && // register is mapped - (genRegMaskFloat((regNumber)i) & regSet.rsMaskRegVarFloat) == 0) // but not enregistered - { - uTemps++; - } - } - assert(uTemps <= 1); -#endif - - for (i = REG_FPV0; i < REG_FPCOUNT; i++) - { - if (compCurFPState.Mapped(i) && // register is mapped - (genRegMaskFloat((regNumber)i) & regSet.rsMaskRegVarFloat) == 0) // but not enregistered - { - // remove register from stacks - FlatFPX87_Unload(&compCurFPState, i); - } - } - - assert(ConsistentAfterStatementStackFP()); -} - -bool CodeGen::StackFPIsSameAsFloat(double d) -{ - if (forceCastToFloat(d) == d) - { - JITDUMP("StackFPIsSameAsFloat is true for value %lf\n", d); - return true; - } - else - { - JITDUMP("StackFPIsSameAsFloat is false for value %lf\n", d); - } - - return false; -} - -GenTree* CodeGen::genMakeAddressableStackFP(GenTree* tree, - regMaskTP* regMaskIntPtr, - regMaskTP* regMaskFltPtr, - bool bCollapseConstantDoubles) -{ - *regMaskIntPtr = *regMaskFltPtr = 0; - - switch (tree->OperGet()) - { - case GT_CNS_DBL: - if (tree->gtDblCon.gtDconVal == 0.0 || tree->gtDblCon.gtDconVal == 1.0) - { - // For constants like 0 or 1 don't waste memory - genCodeForTree(tree, 0); - regSet.SetUsedRegFloat(tree, true); - - *regMaskFltPtr = genRegMaskFloat(tree->gtRegNum); - return tree; - } - else - { - GenTree* addr; - if (tree->gtType == TYP_FLOAT || - (bCollapseConstantDoubles && StackFPIsSameAsFloat(tree->gtDblCon.gtDconVal))) - { - float f = forceCastToFloat(tree->gtDblCon.gtDconVal); - addr = genMakeConst(&f, TYP_FLOAT, tree, true); - } - else - { - addr = genMakeConst(&tree->gtDblCon.gtDconVal, tree->gtType, tree, true); - } -#ifdef DEBUG - if (compiler->verbose) - { - printf("Generated new constant in tree "); - Compiler::printTreeID(addr); - printf(" with value %lf\n", tree->gtDblCon.gtDconVal); - } -#endif // DEBUG - tree->ReplaceWith(addr, compiler); - return tree; - } - break; - case GT_REG_VAR: - // We take care about this in genKeepAddressableStackFP - return tree; - case GT_LCL_VAR: - case GT_LCL_FLD: - case GT_CLS_VAR: - return tree; - - case GT_LEA: - if (!genMakeIndAddrMode(tree, tree, false, 0, RegSet::KEEP_REG, regMaskIntPtr, false)) - { - assert(false); - } - genUpdateLife(tree); - return tree; - - case GT_IND: - // Try to make the address directly addressable - - if (genMakeIndAddrMode(tree->gtOp.gtOp1, tree, false, 0, RegSet::KEEP_REG, regMaskIntPtr, false)) - { - genUpdateLife(tree); - return tree; - } - else - { - GenTree* addr = tree; - tree = tree->gtOp.gtOp1; - - genCodeForTree(tree, 0); - regSet.rsMarkRegUsed(tree, addr); - - *regMaskIntPtr = genRegMask(tree->gtRegNum); - return addr; - } - - // fall through - - default: - genCodeForTreeFloat(tree); - regSet.SetUsedRegFloat(tree, true); - - // update mask - *regMaskFltPtr = genRegMaskFloat(tree->gtRegNum); - - return tree; - break; - } -} - -void CodeGen::genKeepAddressableStackFP(GenTree* tree, regMaskTP* regMaskIntPtr, regMaskTP* regMaskFltPtr) -{ - regMaskTP regMaskInt, regMaskFlt; - - regMaskInt = *regMaskIntPtr; - regMaskFlt = *regMaskFltPtr; - - *regMaskIntPtr = *regMaskFltPtr = 0; - - switch (tree->OperGet()) - { - case GT_REG_VAR: - // If register has been spilled, unspill it - if (tree->gtFlags & GTF_SPILLED) - { - UnspillFloat(&compiler->lvaTable[tree->gtLclVarCommon.gtLclNum]); - } - - // If regvar is dying, take it out of the regvar mask - if (tree->IsRegVarDeath()) - { - genRegVarDeathStackFP(tree); - } - genUpdateLife(tree); - - return; - case GT_CNS_DBL: - { - if (tree->gtFlags & GTF_SPILLED) - { - UnspillFloat(tree); - } - - *regMaskFltPtr = genRegMaskFloat(tree->gtRegNum); - - return; - } - case GT_LCL_FLD: - case GT_LCL_VAR: - case GT_CLS_VAR: - genUpdateLife(tree); - return; - case GT_IND: - case GT_LEA: - if (regMaskFlt) - { - // fall through - } - else - { - *regMaskIntPtr = genKeepAddressable(tree, regMaskInt, 0); - *regMaskFltPtr = 0; - return; - } - default: - - *regMaskIntPtr = 0; - if (tree->gtFlags & GTF_SPILLED) - { - UnspillFloat(tree); - } - *regMaskFltPtr = genRegMaskFloat(tree->gtRegNum); - return; - } -} - -void CodeGen::genDoneAddressableStackFP(GenTree* tree, - regMaskTP addrRegInt, - regMaskTP addrRegFlt, - RegSet::KeepReg keptReg) -{ - assert(!(addrRegInt && addrRegFlt)); - - if (addrRegInt) - { - return genDoneAddressable(tree, addrRegInt, keptReg); - } - else if (addrRegFlt) - { - if (keptReg == RegSet::KEEP_REG) - { - for (unsigned i = REG_FPV0; i < REG_FPCOUNT; i++) - { - if (genRegMaskFloat((regNumber)i) & addrRegFlt) - { - regSet.SetUsedRegFloat(tree, false); - } - } - } - } -} - -void CodeGen::FlatFPX87_Kill(FlatFPStateX87* pState, unsigned uVirtual) -{ - JITDUMP("Killing %s\n", regVarNameStackFP((regNumber)uVirtual)); - - assert(pState->TopVirtual() == uVirtual); - pState->Pop(); -} - -void CodeGen::FlatFPX87_PushVirtual(FlatFPStateX87* pState, unsigned uRegister, bool bEmitCode) -{ - JITDUMP("Pushing %s to stack\n", regVarNameStackFP((regNumber)uRegister)); - - pState->Push(uRegister); -} - -unsigned CodeGen::FlatFPX87_Pop(FlatFPStateX87* pState, bool bEmitCode) -{ - assert(pState->m_uStackSize > 0); - - // Update state - unsigned uVirtual = pState->Pop(); - - // Emit instruction - if (bEmitCode) - { - inst_FS(INS_fstp, 0); - } - - return (uVirtual); -} - -unsigned CodeGen::FlatFPX87_Top(FlatFPStateX87* pState, bool bEmitCode) -{ - return pState->TopVirtual(); -} - -void CodeGen::FlatFPX87_Unload(FlatFPStateX87* pState, unsigned uVirtual, bool bEmitCode) -{ - if (uVirtual != pState->TopVirtual()) - { - // We will do an fstp to the right place - - // Update state - unsigned uStack = pState->m_uVirtualMap[uVirtual]; - unsigned uPhysic = pState->StackToST(uStack); - - pState->Unmap(uVirtual); - pState->Associate(pState->TopVirtual(), uStack); - pState->m_uStackSize--; - -#ifdef DEBUG - - pState->m_uStack[pState->m_uStackSize] = (unsigned)-1; -#endif - - // Emit instruction - if (bEmitCode) - { - inst_FS(INS_fstp, uPhysic); - } - } - else - { - // Emit fstp - FlatFPX87_Pop(pState, bEmitCode); - } - - assert(pState->IsConsistent()); -} - -void CodeGenInterface::FlatFPX87_MoveToTOS(FlatFPStateX87* pState, unsigned uVirtual, bool bEmitCode) -{ - assert(!IsUninitialized(uVirtual)); - - JITDUMP("Moving %s to top of stack\n", regVarNameStackFP((regNumber)uVirtual)); - - if (uVirtual != pState->TopVirtual()) - { - FlatFPX87_SwapStack(pState, pState->m_uVirtualMap[uVirtual], pState->TopIndex(), bEmitCode); - } - else - { - JITDUMP("%s already on the top of stack\n", regVarNameStackFP((regNumber)uVirtual)); - } - - assert(pState->IsConsistent()); -} - -void CodeGenInterface::FlatFPX87_SwapStack(FlatFPStateX87* pState, unsigned i, unsigned j, bool bEmitCode) -{ - assert(i != j); - assert(i < pState->m_uStackSize); - assert(j < pState->m_uStackSize); - - JITDUMP("Exchanging ST(%i) and ST(%i)\n", pState->StackToST(i), pState->StackToST(j)); - - // issue actual swaps - int iPhysic = pState->StackToST(i); - int jPhysic = pState->StackToST(j); - - if (bEmitCode) - { - if (iPhysic == 0 || jPhysic == 0) - { - inst_FN(INS_fxch, iPhysic ? iPhysic : jPhysic); - } - else - { - inst_FN(INS_fxch, iPhysic); - inst_FN(INS_fxch, jPhysic); - inst_FN(INS_fxch, iPhysic); - } - } - - // Update State - - // Swap Register file - pState->m_uVirtualMap[pState->m_uStack[i]] = j; - pState->m_uVirtualMap[pState->m_uStack[j]] = i; - - // Swap stack - int temp; - temp = pState->m_uStack[i]; - pState->m_uStack[i] = pState->m_uStack[j]; - pState->m_uStack[j] = temp; - - assert(pState->IsConsistent()); -} - -#ifdef DEBUG - -void CodeGen::JitDumpFPState() -{ - int i; - - if ((regSet.rsMaskUsedFloat != 0) || (regSet.rsMaskRegVarFloat != 0)) - { - printf("FPSTATE\n"); - printf("Used virtual registers: "); - for (i = REG_FPV0; i < REG_FPCOUNT; i++) - { - if (genRegMaskFloat((regNumber)i) & regSet.rsMaskUsedFloat) - { - printf("FPV%i ", i); - } - } - printf("\n"); - - printf("virtual registers holding reg vars: "); - for (i = REG_FPV0; i < REG_FPCOUNT; i++) - { - if (genRegMaskFloat((regNumber)i) & regSet.rsMaskRegVarFloat) - { - printf("FPV%i ", i); - } - } - printf("\n"); - } - compCurFPState.Dump(); -} -#endif - -// -// -// Register allocation -// -struct ChangeToRegVarCallback -{ - unsigned lclnum; - regNumber reg; -}; - -void Compiler::raInitStackFP() -{ - // Reset local/reg interference - for (int i = 0; i < REG_FPCOUNT; i++) - { - VarSetOps::AssignNoCopy(this, raLclRegIntfFloat[i], VarSetOps::MakeEmpty(this)); - } - - VarSetOps::AssignNoCopy(this, optAllFPregVars, VarSetOps::MakeEmpty(this)); - VarSetOps::AssignNoCopy(this, optAllNonFPvars, VarSetOps::MakeEmpty(this)); - VarSetOps::AssignNoCopy(this, optAllFloatVars, VarSetOps::MakeEmpty(this)); - - raCntStkStackFP = 0; - raCntWtdStkDblStackFP = 0; - raCntStkParamDblStackFP = 0; - - VarSetOps::AssignNoCopy(this, raMaskDontEnregFloat, VarSetOps::MakeEmpty(this)); - - // Calculate the set of all tracked FP/non-FP variables - // into compiler->optAllFloatVars and compiler->optAllNonFPvars - unsigned lclNum; - LclVarDsc* varDsc; - - for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++) - { - /* Ignore the variable if it's not tracked */ - - if (!varDsc->lvTracked) - continue; - - /* Get hold of the index and the interference mask for the variable */ - - unsigned varNum = varDsc->lvVarIndex; - - /* add to the set of all tracked FP/non-FP variables */ - - if (varDsc->IsFloatRegType()) - VarSetOps::AddElemD(this, optAllFloatVars, varNum); - else - VarSetOps::AddElemD(this, optAllNonFPvars, varNum); - } -} - -#ifdef DEBUG -void Compiler::raDumpVariableRegIntfFloat() -{ - unsigned i; - unsigned j; - - for (i = REG_FPV0; i < REG_FPCOUNT; i++) - { - if (!VarSetOps::IsEmpty(this, raLclRegIntfFloat[i])) - { - JITDUMP("FPV%u interferes with ", i); - for (j = 0; j < lvaTrackedCount; j++) - { - assert(VarSetOps::IsEmpty(this, VarSetOps::Diff(this, raLclRegIntfFloat[i], optAllFloatVars))); - - if (VarSetOps::IsMember(this, raLclRegIntfFloat[i], j)) - { - JITDUMP("T%02u/V%02u, ", j, lvaTrackedToVarNum[j]); - } - } - JITDUMP("\n"); - } - } -} -#endif - -// Returns the regnum for the variable passed as param takin in account -// the fpvar to register interference mask. If we can't find anything, we -// will return REG_FPNONE -regNumber Compiler::raRegForVarStackFP(unsigned varTrackedIndex) -{ - for (unsigned i = REG_FPV0; i < REG_FPCOUNT; i++) - { - if (!VarSetOps::IsMember(this, raLclRegIntfFloat[i], varTrackedIndex)) - { - return (regNumber)i; - } - } - - return REG_FPNONE; -} - -void Compiler::raAddPayloadStackFP(VARSET_VALARG_TP maskArg, unsigned weight) -{ - VARSET_TP mask(VarSetOps::Intersection(this, maskArg, optAllFloatVars)); - if (VarSetOps::IsEmpty(this, mask)) - { - return; - } - - for (unsigned i = 0; i < lvaTrackedCount; i++) - { - if (VarSetOps::IsMember(this, mask, i)) - { - raPayloadStackFP[i] += weight; - } - } -} - -bool Compiler::raVarIsGreaterValueStackFP(LclVarDsc* lv1, LclVarDsc* lv2) -{ - assert(lv1->lvTracked); - assert(lv2->lvTracked); - - bool bSmall = (compCodeOpt() == SMALL_CODE); - - double weight1 = double(bSmall ? lv1->lvRefCnt : lv1->lvRefCntWtd) - double(raPayloadStackFP[lv1->lvVarIndex]) - - double(raHeightsStackFP[lv1->lvVarIndex][FP_VIRTUALREGISTERS]); - - double weight2 = double(bSmall ? lv2->lvRefCnt : lv2->lvRefCntWtd) - double(raPayloadStackFP[lv2->lvVarIndex]) - - double(raHeightsStackFP[lv2->lvVarIndex][FP_VIRTUALREGISTERS]); - - double diff = weight1 - weight2; - - if (diff) - { - return diff > 0 ? true : false; - } - else - { - return int(lv1->lvRefCnt - lv2->lvRefCnt) ? true : false; - } -} - -#ifdef DEBUG -// Dumps only interesting vars (the ones that are not enregistered yet -void Compiler::raDumpHeightsStackFP() -{ - unsigned i; - unsigned j; - - JITDUMP("raDumpHeightsStackFP():\n"); - JITDUMP("--------------------------------------------------------\n"); - JITDUMP("Weighted Height Table Dump\n "); - for (i = 0; i < FP_VIRTUALREGISTERS; i++) - { - JITDUMP(" %i ", i + 1); - } - - JITDUMP("OVF\n"); - - for (i = 0; i < lvaTrackedCount; i++) - { - if (VarSetOps::IsMember(this, optAllFloatVars, i) && !VarSetOps::IsMember(this, optAllFPregVars, i)) - { - JITDUMP("V%02u/T%02u: ", lvaTrackedToVarNum[i], i); - - for (j = 0; j <= FP_VIRTUALREGISTERS; j++) - { - JITDUMP("%5u ", raHeightsStackFP[i][j]); - } - JITDUMP("\n"); - } - } - - JITDUMP("\nNonweighted Height Table Dump\n "); - for (i = 0; i < FP_VIRTUALREGISTERS; i++) - { - JITDUMP(" %i ", i + 1); - } - - JITDUMP("OVF\n"); - - for (i = 0; i < lvaTrackedCount; i++) - { - if (VarSetOps::IsMember(this, optAllFloatVars, i) && !VarSetOps::IsMember(this, optAllFPregVars, i)) - { - JITDUMP("V%02u/T%02u: ", lvaTrackedToVarNum[i], i); - - for (j = 0; j <= FP_VIRTUALREGISTERS; j++) - { - JITDUMP("%5u ", raHeightsNonWeightedStackFP[i][j]); - } - JITDUMP("\n"); - } - } - JITDUMP("--------------------------------------------------------\n"); -} -#endif - -// Increases heights for tracked variables given in mask. We call this -// function when we enregister a variable and will cause the heights to -// shift one place to the right. -void Compiler::raUpdateHeightsForVarsStackFP(VARSET_VALARG_TP mask) -{ - assert(VarSetOps::IsSubset(this, mask, optAllFloatVars)); - - for (unsigned i = 0; i < lvaTrackedCount; i++) - { - if (VarSetOps::IsMember(this, mask, i)) - { - for (unsigned j = FP_VIRTUALREGISTERS; j > 0; j--) - { - raHeightsStackFP[i][j] = raHeightsStackFP[i][j - 1]; - -#ifdef DEBUG - raHeightsNonWeightedStackFP[i][j] = raHeightsNonWeightedStackFP[i][j - 1]; -#endif - } - - raHeightsStackFP[i][0] = 0; -#ifdef DEBUG - raHeightsNonWeightedStackFP[i][0] = 0; -#endif - } - } - -#ifdef DEBUG - raDumpHeightsStackFP(); -#endif -} - -// This is the prepass we do to adjust refcounts across calls and -// create the height structure. -void Compiler::raEnregisterVarsPrePassStackFP() -{ - BasicBlock* block; - - assert(!VarSetOps::IsEmpty(this, optAllFloatVars)); - - // Initialization of the height table - memset(raHeightsStackFP, 0, sizeof(raHeightsStackFP)); - - // Initialization of the payload table - memset(raPayloadStackFP, 0, sizeof(raPayloadStackFP)); - -#ifdef DEBUG - memset(raHeightsNonWeightedStackFP, 0, sizeof(raHeightsStackFP)); -#endif - - // We will have a quick table with the pointers to the interesting varDscs - // so that we don't have to scan for them for each tree. - unsigned FPVars[lclMAX_TRACKED]; - unsigned numFPVars = 0; - for (unsigned i = 0; i < lvaTrackedCount; i++) - { - if (VarSetOps::IsMember(this, optAllFloatVars, i)) - { - FPVars[numFPVars++] = i; - } - } - - assert(numFPVars == VarSetOps::Count(this, optAllFloatVars)); - - // Things we check here: - // - // We substract 2 for each FP variable that's live across a call, as we will - // have 2 memory accesses to spill and unpsill around it. - // - // - // - VARSET_TP blockLiveOutFloats(VarSetOps::MakeEmpty(this)); - for (block = fgFirstBB; block; block = block->bbNext) - { - compCurBB = block; - /* - This opt fails in the case of a variable that has it's entire lifetime contained in the 'then' of - a qmark. The use mask for the whole qmark won't contain that variable as it variable's value comes - from a def in the else, and the def can't be set for the qmark if the else side of - the qmark doesn't do a def. - - See VSW# 354454 for more info. Leaving the comment and code here just in case we try to be - 'smart' again in the future - - - if (((block->bbVarUse | - block->bbVarDef | - block->bbLiveIn ) & optAllFloatVars) == 0) - { - // Fast way out - continue; - } - */ - VarSetOps::Assign(this, blockLiveOutFloats, block->bbLiveOut); - VarSetOps::IntersectionD(this, blockLiveOutFloats, optAllFloatVars); - if (!VarSetOps::IsEmpty(this, blockLiveOutFloats)) - { - // See comment in compiler.h above declaration of compMayHaveTransitionBlocks - // to understand the reason for this limitation of FP optimizer. - switch (block->bbJumpKind) - { - case BBJ_COND: - { - GenTree* stmt; - stmt = block->bbTreeList->gtPrev; - assert(stmt->gtNext == NULL && stmt->gtStmt.gtStmtExpr->gtOper == GT_JTRUE); - - assert(stmt->gtStmt.gtStmtExpr->gtOp.gtOp1); - GenTree* cond = stmt->gtStmt.gtStmtExpr->gtOp.gtOp1; - - assert(cond->OperIsCompare()); - - if (cond->gtOp.gtOp1->TypeGet() == TYP_LONG) - { - if (compHndBBtabCount > 0) - { - // If we have any handlers we won't enregister whatever is live out of this block - JITDUMP("PERF Warning: Taking out FP candidates due to transition blocks + exception " - "handlers.\n"); - VarSetOps::UnionD(this, raMaskDontEnregFloat, - VarSetOps::Intersection(this, block->bbLiveOut, optAllFloatVars)); - } - else - { - // long conditional jumps can generate transition bloks - compMayHaveTransitionBlocks = true; - } - } - - break; - } - case BBJ_SWITCH: - { - if (compHndBBtabCount > 0) - { - // If we have any handlers we won't enregister whatever is live out of this block - JITDUMP( - "PERF Warning: Taking out FP candidates due to transition blocks + exception handlers.\n"); - VarSetOps::UnionD(this, raMaskDontEnregFloat, - VarSetOps::Intersection(this, block->bbLiveOut, optAllFloatVars)); - } - else - { - // fp vars are live out of the switch, so we may have transition blocks - compMayHaveTransitionBlocks = true; - } - break; - default: - break; - } - } - } - - VARSET_TP liveSet(VarSetOps::MakeCopy(this, block->bbLiveIn)); - for (GenTree* stmt = block->FirstNonPhiDef(); stmt; stmt = stmt->gtNext) - { - assert(stmt->gtOper == GT_STMT); - - unsigned prevHeight = stmt->gtStmt.gtStmtList->gtFPlvl; - for (GenTree* tree = stmt->gtStmt.gtStmtList; tree; tree = tree->gtNext) - { - VarSetOps::AssignNoCopy(this, liveSet, fgUpdateLiveSet(liveSet, tree)); - switch (tree->gtOper) - { - case GT_CALL: - raAddPayloadStackFP(liveSet, block->getBBWeight(this) * 2); - break; - case GT_CAST: - // For cast from long local var to double, decrement the ref count of the long - // to avoid store forwarding stall - if (tree->gtType == TYP_DOUBLE) - { - GenTree* op1 = tree->gtOp.gtOp1; - if (op1->gtOper == GT_LCL_VAR && op1->gtType == TYP_LONG) - { - unsigned int lclNum = op1->gtLclVarCommon.gtLclNum; - assert(lclNum < lvaCount); - LclVarDsc* varDsc = lvaTable + lclNum; - unsigned int weightedRefCnt = varDsc->lvRefCntWtd; - unsigned int refCntDecrement = 2 * block->getBBWeight(this); - if (refCntDecrement > weightedRefCnt) - { - varDsc->lvRefCntWtd = 0; - } - else - { - varDsc->lvRefCntWtd = weightedRefCnt - refCntDecrement; - } - } - } - break; - default: - break; - } - - // Update heights - unsigned height = tree->gtFPlvl; - - if (height != prevHeight) - { - if (height > prevHeight && height < FP_VIRTUALREGISTERS) - { - for (unsigned i = 0; i < numFPVars; i++) - { - if (VarSetOps::IsMember(this, liveSet, FPVars[i])) - { - // The -1 are because we don't care about stack height 0 - // and we will use offset FP_VIRTUALREGISTERS to know what's - // the count when we overflow. we multiply by 2, because that - // is the number of memory accesses we will do for each spill - // (even if we op directly with the spill) - if (compCodeOpt() == SMALL_CODE) - { - raHeightsStackFP[FPVars[i]][height - 1] += 2; - } - else - { - raHeightsStackFP[FPVars[i]][height - 1] += 2 * block->getBBWeight(this); - } - -#ifdef DEBUG - raHeightsNonWeightedStackFP[FPVars[i]][height - 1]++; -#endif - } - } - } - - prevHeight = height; - } - } - } - } - compCurBB = NULL; - - if (compJmpOpUsed) - { - // Disable enregistering of FP vars for methods with jmp op. We have really no - // coverage here. - // The problem with FP enreg vars is that the returning block is marked with having - // all variables live on exit. This works for integer vars, but for FP vars we must - // do the work to unload them. This is fairly straightforward to do, but I'm worried - // by the coverage, so I'll take the conservative aproach of disabling FP enregistering - // and we will fix it if there is demand - JITDUMP("PERF Warning: Disabling FP enregistering due to JMP op!!!!!!!.\n"); - VarSetOps::UnionD(this, raMaskDontEnregFloat, optAllFloatVars); - } - -#ifdef DEBUG - raDumpHeightsStackFP(); -#endif -} - -void Compiler::raSetRegLclBirthDeath(GenTree* tree, VARSET_VALARG_TP lastlife, bool fromLDOBJ) -{ - assert(tree->gtOper == GT_LCL_VAR); - - unsigned lclnum = tree->gtLclVarCommon.gtLclNum; - assert(lclnum < lvaCount); - - LclVarDsc* varDsc = lvaTable + lclnum; - - if (!varDsc->lvTracked) - { - // Not tracked, can't be one of the enreg fp vars - return; - } - - unsigned varIndex = varDsc->lvVarIndex; - - if (!VarSetOps::IsMember(this, optAllFPregVars, varIndex)) - { - // Not one of the enreg fp vars - return; - } - - assert(varDsc->lvRegNum != REG_FPNONE); - assert(!VarSetOps::IsMember(this, raMaskDontEnregFloat, varIndex)); - - unsigned livenessFlags = (tree->gtFlags & GTF_LIVENESS_MASK); - tree->ChangeOper(GT_REG_VAR); - tree->gtFlags |= livenessFlags; - tree->gtRegNum = varDsc->lvRegNum; - tree->gtRegVar.gtRegNum = varDsc->lvRegNum; - tree->gtRegVar.SetLclNum(lclnum); - - // A liveset can change in a lclvar even if the lclvar itself is not - // changing its life. This can happen for lclvars inside qmarks, - // where lclvars die across the colon edge. - // SO, either - // it is marked GTF_VAR_DEATH (already set by fgComputeLife) - // OR it is already live - // OR it is becoming live - // - if ((tree->gtFlags & GTF_VAR_DEATH) == 0) - { - if ((tree->gtFlags & GTF_VAR_DEF) != 0) - - { - tree->gtFlags |= GTF_REG_BIRTH; - } - } - -#ifdef DEBUG - if (verbose) - gtDispTree(tree); -#endif -} - -// In this pass we set the regvars and set the birth and death flags. we do it -// for all enregistered variables at once. -void Compiler::raEnregisterVarsPostPassStackFP() -{ - if (VarSetOps::IsEmpty(this, optAllFPregVars)) - { - // Nothing to fix up. - } - - BasicBlock* block; - - JITDUMP("raEnregisterVarsPostPassStackFP:\n"); - - for (block = fgFirstBB; block; block = block->bbNext) - { - compCurBB = block; - - /* - This opt fails in the case of a variable that has it's entire lifetime contained in the 'then' of - a qmark. The use mask for the whole qmark won't contain that variable as it variable's value comes - from a def in the else, and the def can't be set for the qmark if the else side of - the qmark doesn't do a def. - - See VSW# 354454 for more info. Leaving the comment and code here just in case we try to be - 'smart' again in the future - - - - if (((block->bbVarUse | - block->bbVarDef | - block->bbLiveIn ) & optAllFPregVars) == 0) - { - // Fast way out - continue; - } - */ - - VARSET_TP lastlife(VarSetOps::MakeCopy(this, block->bbLiveIn)); - for (GenTree* stmt = block->FirstNonPhiDef(); stmt; stmt = stmt->gtNext) - { - assert(stmt->gtOper == GT_STMT); - - for (GenTree *tree = stmt->gtStmt.gtStmtList; tree; - VarSetOps::AssignNoCopy(this, lastlife, fgUpdateLiveSet(lastlife, tree)), tree = tree->gtNext) - { - if (tree->gtOper == GT_LCL_VAR) - { - raSetRegLclBirthDeath(tree, lastlife, false); - } - - // Model implicit use (& hence last use) of frame list root at pinvokes. - if (tree->gtOper == GT_CALL) - { - GenTreeCall* call = tree->AsCall(); - if (call->IsUnmanaged() && !opts.ShouldUsePInvokeHelpers()) - { - LclVarDsc* frameVarDsc = &lvaTable[info.compLvFrameListRoot]; - - if (frameVarDsc->lvTracked && ((call->gtCallMoreFlags & GTF_CALL_M_FRAME_VAR_DEATH) != 0)) - { - // Frame var dies here - unsigned varIndex = frameVarDsc->lvVarIndex; - VarSetOps::RemoveElemD(this, lastlife, varIndex); - } - } - } - } - } - - assert(VarSetOps::Equal(this, lastlife, block->bbLiveOut)); - } - compCurBB = NULL; -} - -void Compiler::raGenerateFPRefCounts() -{ - // Update ref counts to stack - assert(raCntWtdStkDblStackFP == 0); - assert(raCntStkParamDblStackFP == 0); - assert(raCntStkStackFP == 0); - - LclVarDsc* varDsc; - unsigned lclNum; - for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++) - { - if (varDsc->lvType == TYP_DOUBLE || - varDsc->lvStructDoubleAlign) // Account for structs (A bit over aggressive here, we should - // account for field accesses, but should be a reasonable - // heuristic). - { - if (varDsc->lvRegister) - { - assert(varDsc->lvTracked); - } - else - { - // Increment tmp access - raCntStkStackFP += varDsc->lvRefCnt; - - if (varDsc->lvIsParam) - { - // Why is this not weighted? - raCntStkParamDblStackFP += varDsc->lvRefCnt; - } - else - { - raCntWtdStkDblStackFP += varDsc->lvRefCntWtd; - } - } - } - } - -#ifdef DEBUG - if ((raCntWtdStkDblStackFP > 0) || (raCntStkParamDblStackFP > 0)) - { - JITDUMP("StackFP double stack weighted ref count: %u ; param ref count: %u\n", raCntWtdStkDblStackFP, - raCntStkParamDblStackFP); - } -#endif -} - -void Compiler::raEnregisterVarsStackFP() -{ - const int FPENREGTHRESHOLD = 1; - const unsigned int FPENREGTHRESHOLD_WEIGHTED = FPENREGTHRESHOLD; - - // Do init - raInitStackFP(); - - if (opts.compDbgCode || opts.MinOpts()) - { - // no enregistering for these options. - return; - } - - if (VarSetOps::IsEmpty(this, optAllFloatVars)) - { - // No floating point vars. bail out - return; - } - - // Do additional pass updating weights and generating height table - raEnregisterVarsPrePassStackFP(); - - // Vars are ordered by weight - LclVarDsc* varDsc; - - // Set an interference with V0 and V1, which we reserve as a temp registers. - // We need only one temp. but we will take the easy way, as by using - // two, we will need to teach codegen how to operate with spilled variables - VarSetOps::Assign(this, raLclRegIntfFloat[REG_FPV0], optAllFloatVars); - VarSetOps::Assign(this, raLclRegIntfFloat[REG_FPV1], optAllFloatVars); - -#ifdef DEBUG - if (codeGen->genStressFloat()) - { - // Lock out registers for stress. - regMaskTP locked = codeGen->genStressLockedMaskFloat(); - for (unsigned i = REG_FPV0; i < REG_FPCOUNT; i++) - { - if (locked & genRegMaskFloat((regNumber)i)) - { - VarSetOps::Assign(this, raLclRegIntfFloat[i], optAllFloatVars); - } - } - } -#endif - - // Build the interesting FP var table - LclVarDsc* fpLclFPVars[lclMAX_TRACKED]; - unsigned numFPVars = 0; - for (unsigned i = 0; i < lvaTrackedCount; i++) - { - if (VarSetOps::IsMember(this, raMaskDontEnregFloat, i)) - { - JITDUMP("Won't enregister V%02i (T%02i) because it's marked as dont enregister\n", lvaTrackedToVarNum[i], - i); - continue; - } - - if (VarSetOps::IsMember(this, optAllFloatVars, i)) - { - varDsc = lvaTable + lvaTrackedToVarNum[i]; - - assert(varDsc->lvTracked); - - if (varDsc->lvDoNotEnregister) - { - JITDUMP("Won't enregister V%02i (T%02i) because it's marked as DoNotEnregister\n", - lvaTrackedToVarNum[i], i); - continue; - } -#if !FEATURE_X87_DOUBLES - if (varDsc->TypeGet() == TYP_FLOAT) - { - JITDUMP("Won't enregister V%02i (T%02i) because it's a TYP_FLOAT and we have disabled " - "FEATURE_X87_DOUBLES\n", - lvaTrackedToVarNum[i], i); - continue; - } -#endif - - fpLclFPVars[numFPVars++] = lvaTable + lvaTrackedToVarNum[i]; - } - } - - unsigned maxRegVars = 0; // Max num of regvars at one time - - for (unsigned sortNum = 0; sortNum < numFPVars; sortNum++) - { -#ifdef DEBUG - { - JITDUMP("\n"); - JITDUMP("FP regvar candidates:\n"); - - for (unsigned i = sortNum; i < numFPVars; i++) - { - varDsc = fpLclFPVars[i]; - unsigned lclNum = varDsc - lvaTable; - unsigned varIndex; - varIndex = varDsc->lvVarIndex; - - JITDUMP("V%02u/T%02u RefCount: %u Weight: %u ; Payload: %u ; Overflow: %u\n", lclNum, varIndex, - varDsc->lvRefCnt, varDsc->lvRefCntWtd, raPayloadStackFP[varIndex], - raHeightsStackFP[varIndex][FP_VIRTUALREGISTERS]); - } - JITDUMP("\n"); - } -#endif - - unsigned min = sortNum; - - // Find the one that will save us most - for (unsigned i = sortNum + 1; i < numFPVars; i++) - { - if (raVarIsGreaterValueStackFP(fpLclFPVars[i], fpLclFPVars[sortNum])) - { - min = i; - } - } - - // Put it at the top of the array - LclVarDsc* temp; - temp = fpLclFPVars[min]; - fpLclFPVars[min] = fpLclFPVars[sortNum]; - fpLclFPVars[sortNum] = temp; - - varDsc = fpLclFPVars[sortNum]; - -#ifdef DEBUG - unsigned lclNum = varDsc - lvaTable; -#endif - unsigned varIndex = varDsc->lvVarIndex; - - assert(VarSetOps::IsMember(this, optAllFloatVars, varIndex)); - - JITDUMP("Candidate for enregistering: V%02u/T%02u RefCount: %u Weight: %u ; Payload: %u ; Overflow: %u\n", - lclNum, varIndex, varDsc->lvRefCnt, varDsc->lvRefCntWtd, raPayloadStackFP[varIndex], - raHeightsStackFP[varIndex][FP_VIRTUALREGISTERS]); - - bool bMeetsThreshold = true; - - if (varDsc->lvRefCnt < FPENREGTHRESHOLD || varDsc->lvRefCntWtd < FPENREGTHRESHOLD_WEIGHTED) - { - bMeetsThreshold = false; - } - - // We don't want to enregister arguments with only one use, as they will be - // loaded in the prolog. Just don't enregister them and load them lazily( - if (varDsc->lvIsParam && - (varDsc->lvRefCnt <= FPENREGTHRESHOLD || varDsc->lvRefCntWtd <= FPENREGTHRESHOLD_WEIGHTED)) - { - bMeetsThreshold = false; - } - - if (!bMeetsThreshold -#ifdef DEBUG - && codeGen->genStressFloat() != 1 -#endif - ) - { - // Doesn't meet bar, do next - JITDUMP("V%02u/T%02u doesnt meet threshold. Won't enregister\n", lclNum, varIndex); - continue; - } - - // We don't want to have problems with overflow (we now have 2 unsigned counters - // that can possibly go to their limits), so we just promote to double here. - // diff - double balance = - double(varDsc->lvRefCntWtd) - - double(raPayloadStackFP[varIndex]) - // Additional costs of enregistering variable - double(raHeightsStackFP[varIndex][FP_VIRTUALREGISTERS]) - // Spilling costs of enregistering variable - double(FPENREGTHRESHOLD_WEIGHTED); - - JITDUMP("balance = %d - %d - %d - %d\n", varDsc->lvRefCntWtd, raPayloadStackFP[varIndex], - raHeightsStackFP[varIndex][FP_VIRTUALREGISTERS], FPENREGTHRESHOLD_WEIGHTED); - - if (balance < 0.0 -#ifdef DEBUG - && codeGen->genStressFloat() != 1 -#endif - ) - { - // Doesn't meet bar, do next - JITDUMP("V%02u/T%02u doesnt meet threshold. Won't enregister\n", lclNum, varIndex); - continue; - } - - regNumber reg = raRegForVarStackFP(varDsc->lvVarIndex); - if (reg == REG_FPNONE) - { - // Didn't make if (interferes with other regvars), do next - JITDUMP("V%02u/T%02u interferes with other enreg vars. Won't enregister\n", lclNum, varIndex); - - continue; - } - - if (lvaIsFieldOfDependentlyPromotedStruct(varDsc)) - { - // Do not enregister if this is a floating field in a struct local of - // promotion type PROMOTION_TYPE_DEPENDENT. - continue; - } - - // Yipee, we will enregister var. - varDsc->lvRegister = true; - varDsc->lvRegNum = reg; - VarSetOps::AddElemD(this, optAllFPregVars, varIndex); - -#ifdef DEBUG - raDumpVariableRegIntfFloat(); - - if (verbose) - { - printf("; "); - gtDispLclVar(lclNum); - printf("V%02u/T%02u (refcnt=%2u,refwtd=%4u%s) enregistered in %s\n", varIndex, varDsc->lvVarIndex, - varDsc->lvRefCnt, varDsc->lvRefCntWtd / 2, (varDsc->lvRefCntWtd & 1) ? ".5" : "", - CodeGen::regVarNameStackFP(varDsc->lvRegNum)); - } - - JITDUMP("\n"); -#endif - - // Create interferences with other variables. - assert(VarSetOps::IsEmpty(this, VarSetOps::Diff(this, raLclRegIntfFloat[(int)reg], optAllFloatVars))); - VARSET_TP intfFloats(VarSetOps::Intersection(this, lvaVarIntf[varIndex], optAllFloatVars)); - - VarSetOps::UnionD(this, raLclRegIntfFloat[reg], intfFloats); - - // Update height tables for variables that interfere with this one. - raUpdateHeightsForVarsStackFP(intfFloats); - - // Update max number of reg vars at once. - maxRegVars = min(REG_FPCOUNT, max(maxRegVars, VarSetOps::Count(this, intfFloats))); - } - - assert(VarSetOps::IsSubset(this, optAllFPregVars, optAllFloatVars)); - assert(VarSetOps::IsEmpty(this, VarSetOps::Intersection(this, optAllFPregVars, raMaskDontEnregFloat))); - - // This is a bit conservative, as they may not all go through a call. - // If we have to, we can fix this. - tmpDoubleSpillMax += maxRegVars; - - // Do pass marking trees as egvars - raEnregisterVarsPostPassStackFP(); - -#ifdef DEBUG - { - JITDUMP("FP enregistration summary\n"); - - unsigned i; - for (i = 0; i < numFPVars; i++) - { - varDsc = fpLclFPVars[i]; - - if (varDsc->lvRegister) - { - unsigned lclNum = varDsc - lvaTable; - unsigned varIndex; - varIndex = varDsc->lvVarIndex; - - JITDUMP("Enregistered V%02u/T%02u in FPV%i RefCount: %u Weight: %u \n", lclNum, varIndex, - varDsc->lvRegNum, varDsc->lvRefCnt, varDsc->lvRefCntWtd); - } - } - JITDUMP("End of FP enregistration summary\n\n"); - } -#endif -} - -#ifdef DEBUG - -regMaskTP CodeGenInterface::genStressLockedMaskFloat() -{ - assert(genStressFloat()); - - // Don't use REG_FPV0 or REG_FPV1, they're reserved - if (genStressFloat() == 1) - { - return genRegMaskFloat(REG_FPV4) | genRegMaskFloat(REG_FPV5) | genRegMaskFloat(REG_FPV6) | - genRegMaskFloat(REG_FPV7); - } - else - { - return genRegMaskFloat(REG_FPV2) | genRegMaskFloat(REG_FPV3) | genRegMaskFloat(REG_FPV4) | - genRegMaskFloat(REG_FPV5) | genRegMaskFloat(REG_FPV6) | genRegMaskFloat(REG_FPV7); - } -} - -#endif - -#endif // FEATURE_STACK_FP_X87 - -#endif // LEGACY_BACKEND diff --git a/src/jit/stacklevelsetter.cpp b/src/jit/stacklevelsetter.cpp index 807c5b0c86..b0b6324f86 100644 --- a/src/jit/stacklevelsetter.cpp +++ b/src/jit/stacklevelsetter.cpp @@ -7,8 +7,6 @@ #pragma hdrstop #endif -#ifndef LEGACY_BACKEND // This file is ONLY used for the RyuJIT backend that uses the linear scan register allocator - #include "stacklevelsetter.h" StackLevelSetter::StackLevelSetter(Compiler* compiler) @@ -270,5 +268,3 @@ void StackLevelSetter::SubStackLevel(unsigned value) assert(currentStackLevel >= value); currentStackLevel -= value; } - -#endif // !LEGACY_BACKEND diff --git a/src/jit/target.h b/src/jit/target.h index 9c64045a83..98b3421e3a 100644 --- a/src/jit/target.h +++ b/src/jit/target.h @@ -29,9 +29,7 @@ /*****************************************************************************/ // The following are intended to capture only those #defines that cannot be replaced // with static const members of Target -#if defined(_TARGET_X86_) && defined(LEGACY_BACKEND) -#define REGMASK_BITS 8 // number of bits used to represent register mask -#elif defined(_TARGET_XARCH_) +#if defined(_TARGET_XARCH_) #define REGMASK_BITS 32 #elif defined(_TARGET_ARM_) @@ -120,7 +118,6 @@ enum _regMask_enum : unsigned #elif defined(_TARGET_X86_) -#ifndef LEGACY_BACKEND enum _regNumber_enum : unsigned { #define REGDEF(name, rnum, mask, sname) REG_##name = rnum, @@ -141,94 +138,10 @@ enum _regMask_enum : unsigned #include "register.h" }; -#else // LEGACY_BACKEND -enum _regNumber_enum : unsigned -{ -#define REGDEF(name, rnum, mask, sname) REG_##name = rnum, -#define REGALIAS(alias, realname) REG_##alias = REG_##realname, -#include "register.h" - - REG_COUNT, - REG_NA = REG_COUNT, - ACTUAL_REG_COUNT = REG_COUNT - 1, // everything but REG_STK (only real regs) - -#define REGDEF(name, rnum, mask, sname) REG_##name = rnum, -#include "registerfp.h" - - REG_FPCOUNT, - REG_FPNONE = REG_FPCOUNT, - -#define REGDEF(name, rnum, mask, sname) REG_##name = rnum, -#include "registerxmm.h" - - REG_XMMCOUNT -}; - -enum _regMask_enum : unsigned -{ - RBM_NONE = 0, - -#define REGDEF(name, rnum, mask, sname) RBM_##name = mask, -#define REGALIAS(alias, realname) RBM_##alias = RBM_##realname, -#include "register.h" - -#define REGDEF(name, rnum, mask, sname) RBM_##name = mask, -#include "registerfp.h" - -#define REGDEF(name, rnum, mask, sname) RBM_##name = mask, -#include "registerxmm.h" -}; - -#endif // LEGACY_BACKEND #else #error Unsupported target architecture #endif -/* The following are used to hold 'long' (64-bit integer) operands */ - -/* - The following yield the number of bits and the mask of a register - number in a register pair. - */ - -#ifdef _TARGET_ARM_ -#define REG_PAIR_NBITS 6 -#else -#define REG_PAIR_NBITS 4 -#endif -#define REG_PAIR_NMASK ((1 << REG_PAIR_NBITS) - 1) - -#ifdef DEBUG -// Under DEBUG, we want to make sure that code doesn't accidentally confuse a reg pair value -// with a simple register number. Thus, we offset the reg pair numbers so they are distinct -// from all register numbers. Note that this increases the minimum size of a regPairNoSmall -// type due to the additional bits used for this offset. -#define REG_PAIR_FIRST (7 << REG_PAIR_NBITS) -#define REG_PAIR_NBITS_DEBUG \ - (REG_PAIR_NBITS + \ - 3) // extra bits needed by the debug shifting (3 instead of 0 because we shift "7", not "1", above). -C_ASSERT(REG_COUNT < REG_PAIR_FIRST); // make sure the register numbers (including REG_NA, ignoring fp/xmm regs on - // x86/x64) are distinct from the pair numbers -#else -#define REG_PAIR_FIRST 0 -#endif - -enum _regPairNo_enum : unsigned -{ -#define PAIRDEF(rlo, rhi) REG_PAIR_##rlo##rhi = REG_##rlo + (REG_##rhi << REG_PAIR_NBITS) + REG_PAIR_FIRST, -#include "regpair.h" - - REG_PAIR_LAST = (REG_COUNT - 1) + ((REG_COUNT - 1) << REG_PAIR_NBITS) + REG_PAIR_FIRST, - - REG_PAIR_NONE = REG_PAIR_LAST + 1 -}; - -enum regPairMask -{ -#define PAIRDEF(rlo, rhi) RBM_PAIR_##rlo##rhi = (RBM_##rlo | RBM_##rhi), -#include "regpair.h" -}; - /*****************************************************************************/ // TODO-Cleanup: The types defined below are mildly confusing: why are there both? @@ -265,36 +178,7 @@ typedef unsigned __int64 regMaskSmall; #endif typedef _regNumber_enum regNumber; -typedef _regPairNo_enum regPairNo; - -// LSRA currently converts freely between regNumber and regPairNo, so make sure they are the same size. -C_ASSERT(sizeof(regPairNo) == sizeof(regNumber)); - -typedef unsigned char regNumberSmall; - -#ifdef DEBUG - -// Under DEBUG, we shift the reg pair numbers to be independent of the regNumber range, -// so we need additional bits. See the definition of REG_PAIR_FIRST for details. - -#if ((2 * REG_PAIR_NBITS) + REG_PAIR_NBITS_DEBUG) <= 16 -C_ASSERT(((2 * REG_PAIR_NBITS) + REG_PAIR_NBITS_DEBUG) > 8); // assert that nobody fits in 8 bits -typedef unsigned short regPairNoSmall; // x86/x64: need 15 bits -#else -C_ASSERT(((2 * REG_PAIR_NBITS) + REG_PAIR_NBITS_DEBUG) <= 32); -typedef unsigned regPairNoSmall; // arm: need 21 bits -#endif - -#else // DEBUG - -#if (2 * REG_PAIR_NBITS) <= 8 -typedef unsigned char regPairNoSmall; // x86/x64: need 8 bits -#else -C_ASSERT((2 * REG_PAIR_NBITS) <= 16); // assert that nobody needs more than 16 bits -typedef unsigned short regPairNoSmall; // arm: need 12 bits -#endif - -#endif // DEBUG +typedef unsigned char regNumberSmall; /*****************************************************************************/ @@ -338,25 +222,14 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits #if defined(_TARGET_X86_) #define CPU_LOAD_STORE_ARCH 0 - -#ifdef LEGACY_BACKEND - #define CPU_LONG_USES_REGPAIR 1 -#else - #define CPU_LONG_USES_REGPAIR 0 // RyuJIT x86 doesn't use the regPairNo field to record register pairs for long - // type tree nodes, and instead either decomposes them (for non-atomic operations) - // or stores multiple regNumber values for operations such as calls where the - // register definitions are effectively "atomic". -#endif // LEGACY_BACKEND - #define CPU_HAS_FP_SUPPORT 1 #define ROUND_FLOAT 1 // round intermed float expression results #define CPU_HAS_BYTE_REGS 1 #define CPU_USES_BLOCK_MOVE 1 -#ifndef LEGACY_BACKEND // TODO-CQ: Fine tune the following xxBlk threshold values: -#define CPBLK_MOVS_LIMIT 16 // When generating code for CpBlk, this is the buffer size + #define CPBLK_MOVS_LIMIT 16 // When generating code for CpBlk, this is the buffer size // threshold to stop generating rep movs and switch to the helper call. // NOTE: Using rep movs is currently disabled since we found it has bad performance // on pre-Ivy Bridge hardware. @@ -375,8 +248,6 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits // always asks for the unrolling limit first so you can say the JIT 'favors' unrolling. // Setting the limit to something lower than that makes lower to never consider it. -#endif // !LEGACY_BACKEND - #ifdef FEATURE_SIMD #define ALIGN_SIMD_TYPES 1 // whether SIMD type locals are to be aligned #endif // FEATURE_SIMD @@ -388,19 +259,11 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits #define FEATURE_TAILCALL_OPT 0 // opportunistic Tail calls (without ".tail" prefix) made as fast tail calls. #define FEATURE_SET_FLAGS 0 // Set to true to force the JIT to mark the trees with GTF_SET_FLAGS when // the flags need to be set -#ifdef LEGACY_BACKEND - #define FEATURE_MULTIREG_ARGS_OR_RET 0 // Support for passing and/or returning single values in more than one register - #define FEATURE_MULTIREG_ARGS 0 // Support for passing a single argument in more than one register - #define FEATURE_MULTIREG_RET 0 // Support for returning a single value in more than one register - #define MAX_PASS_MULTIREG_BYTES 0 // No multireg arguments - #define MAX_RET_MULTIREG_BYTES 0 // No multireg return values -#else #define FEATURE_MULTIREG_ARGS_OR_RET 1 // Support for passing and/or returning single values in more than one register #define FEATURE_MULTIREG_ARGS 0 // Support for passing a single argument in more than one register #define FEATURE_MULTIREG_RET 1 // Support for returning a single value in more than one register #define MAX_PASS_MULTIREG_BYTES 0 // No multireg arguments (note this seems wrong as MAX_ARG_REG_COUNT is 2) #define MAX_RET_MULTIREG_BYTES 8 // Maximum size of a struct that could be returned in more than one register -#endif #define MAX_ARG_REG_COUNT 2 // Maximum registers used to pass an argument. #define MAX_RET_REG_COUNT 2 // Maximum registers used to return a value. @@ -418,25 +281,19 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits // target #define FEATURE_EH 1 // To aid platform bring-up, eliminate exceptional EH clauses (catch, filter, // filter-handler, fault) and directly execute 'finally' clauses. -#if defined(FEATURE_PAL) && !defined(LEGACY_BACKEND) + +#if defined(FEATURE_PAL) #define FEATURE_EH_FUNCLETS 1 -#else // FEATURE_PAL && !LEGACY_BACKEND +#else // !FEATURE_PAL #define FEATURE_EH_FUNCLETS 0 -#endif // FEATURE_PAL && !LEGACY_BACKEND +#endif // !FEATURE_PAL + #define FEATURE_EH_CALLFINALLY_THUNKS 0 // Generate call-to-finally code in "thunks" in the enclosing EH region, // protected by "cloned finally" clauses. -#ifndef LEGACY_BACKEND - #define FEATURE_STACK_FP_X87 0 -#else // LEGACY_BACKEND - #define FEATURE_STACK_FP_X87 1 // Use flat register file model -#endif // LEGACY_BACKEND - #define FEATURE_X87_DOUBLES 0 // FP tree temps always use x87 doubles (when 1) or can be double or float - // (when 0). #define ETW_EBP_FRAMED 1 // if 1 we cannot use EBP as a scratch register and must create EBP based // frames for most methods #define CSE_CONSTS 1 // Enable if we want to CSE constants -#ifndef LEGACY_BACKEND // The following defines are useful for iterating a regNumber #define REG_FIRST REG_EAX #define REG_INT_FIRST REG_EAX @@ -479,27 +336,6 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits #define REGNUM_BITS 6 // number of bits in a REG_* #define TINY_REGNUM_BITS 6 // number used in a tiny instrdesc (same) -#else // LEGACY_BACKEND - #define FEATURE_FP_REGALLOC 0 // Enabled if RegAlloc is used to enregister Floating Point LclVars - - #define FP_STK_SIZE 8 - #define RBM_ALLFLOAT (RBM_FPV0 | RBM_FPV1 | RBM_FPV2 | RBM_FPV3 | RBM_FPV4 | RBM_FPV5 | RBM_FPV6) - #define REG_FP_FIRST REG_FPV0 - #define REG_FP_LAST REG_FPV7 - #define FIRST_FP_ARGREG REG_NA - #define LAST_FP_ARGREG REG_NA - - - #define REGNUM_BITS 3 // number of bits in a REG_* - #define TINY_REGNUM_BITS 3 - #define REGMASK_BITS 8 // number of bits in a REGNUM_MASK - - #define RBM_FLTARG_REGS 0 - #define RBM_FLT_CALLEE_SAVED 0 - #define RBM_FLT_CALLEE_TRASH 0 - -#endif // LEGACY_BACKEND - #define REGSIZE_BYTES 4 // number of bytes in one register #define MIN_ARG_AREA_FOR_CALL 0 // Minimum required outgoing argument space for a call. @@ -525,13 +361,6 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits #define REG_VAR_ORDER REG_EAX,REG_EDX,REG_ECX,REG_ESI,REG_EDI,REG_EBX #define MAX_VAR_ORDER_SIZE 6 -#ifdef LEGACY_BACKEND - #define REG_TMP_ORDER REG_EAX,REG_EDX,REG_ECX,REG_EBX,REG_ESI,REG_EDI - #define REG_TMP_ORDER_COUNT 6 - - #define REG_PREDICT_ORDER REG_EAX,REG_EDX,REG_ECX,REG_EBX,REG_ESI,REG_EDI -#endif // LEGACY_BACKEND - // The order here is fixed: it must agree with an order assumed in eetwain... #define REG_CALLEE_SAVED_ORDER REG_EDI,REG_ESI,REG_EBX,REG_EBP #define RBM_CALLEE_SAVED_ORDER RBM_EDI,RBM_ESI,RBM_EBX,RBM_EBP @@ -560,21 +389,6 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits #define REG_TMP_1 REG_EDX #define RBM_TMP_1 RBM_EDX - #define REG_PAIR_TMP REG_PAIR_EAXEDX - #define REG_PAIR_TMP_REVERSE REG_PAIR_EDXEAX - #define RBM_PAIR_TMP (RBM_EAX|RBM_EDX) - #define REG_PAIR_TMP_LO REG_EAX - #define RBM_PAIR_TMP_LO RBM_EAX - #define REG_PAIR_TMP_HI REG_EDX - #define RBM_PAIR_TMP_HI RBM_EDX - #define PREDICT_PAIR_TMP PREDICT_PAIR_EAXEDX - #define PREDICT_PAIR_TMP_LO PREDICT_REG_EAX - - // Used when calling the 64-bit Variable shift helper - #define REG_LNGARG_0 REG_PAIR_EAXEDX - #define RBM_LNGARG_0 (RBM_EAX|RBM_EDX) - #define PREDICT_PAIR_LNGARG_0 PREDICT_PAIR_EAXEDX - #define REG_LNGARG_LO REG_EAX #define RBM_LNGARG_LO RBM_EAX #define REG_LNGARG_HI REG_EDX @@ -582,12 +396,10 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits // register to hold shift amount #define REG_SHIFT REG_ECX #define RBM_SHIFT RBM_ECX - #define PREDICT_REG_SHIFT PREDICT_REG_ECX // register to hold shift amount when shifting 64-bit values #define REG_SHIFT_LNG REG_ECX #define RBM_SHIFT_LNG RBM_ECX - #define PREDICT_REG_SHIFT_LNG PREDICT_REG_ECX // This is a general scratch register that does not conflict with the argument registers #define REG_SCRATCH REG_EAX @@ -635,11 +447,6 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits #define REG_PINVOKE_SCRATCH REG_EAX // EAX is trashed by CORINFO_HELP_INIT_PINVOKE_FRAME helper #define RBM_PINVOKE_SCRATCH RBM_EAX -#ifdef LEGACY_BACKEND - #define REG_SPILL_CHOICE REG_EAX - #define RBM_SPILL_CHOICE RBM_EAX -#endif // LEGACY_BACKEND - // The following defines are useful for iterating a regNumber #define REG_FIRST REG_EAX #define REG_INT_FIRST REG_EAX @@ -654,7 +461,6 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits // Which register are int and long values returned in ? #define REG_INTRET REG_EAX #define RBM_INTRET RBM_EAX - #define REG_LNGRET REG_PAIR_EAXEDX #define RBM_LNGRET (RBM_EDX|RBM_EAX) #define REG_LNGRET_LO REG_EAX #define RBM_LNGRET_LO RBM_EAX @@ -692,10 +498,8 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits SELECTANY const regNumber intArgRegs [] = {REG_ECX, REG_EDX}; SELECTANY const regMaskTP intArgMasks[] = {RBM_ECX, RBM_EDX}; -#if !FEATURE_STACK_FP_X87 SELECTANY const regNumber fltArgRegs [] = {REG_XMM0, REG_XMM1, REG_XMM2, REG_XMM3}; SELECTANY const regMaskTP fltArgMasks[] = {RBM_XMM0, RBM_XMM1, RBM_XMM2, RBM_XMM3}; -#endif // FEATURE_STACK_FP_X87 #define RBM_ARG_0 RBM_ECX #define RBM_ARG_1 RBM_EDX @@ -724,7 +528,6 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits // TODO-AMD64-CQ: Fine tune the following xxBlk threshold values: #define CPU_LOAD_STORE_ARCH 0 - #define CPU_LONG_USES_REGPAIR 0 #define CPU_HAS_FP_SUPPORT 1 #define ROUND_FLOAT 0 // Do not round intermed float expression results #define CPU_HAS_BYTE_REGS 0 @@ -792,13 +595,11 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits #define FEATURE_EH 1 // To aid platform bring-up, eliminate exceptional EH clauses (catch, filter, filter-handler, fault) and directly execute 'finally' clauses. #define FEATURE_EH_FUNCLETS 1 #define FEATURE_EH_CALLFINALLY_THUNKS 1 // Generate call-to-finally code in "thunks" in the enclosing EH region, protected by "cloned finally" clauses. - #define FEATURE_STACK_FP_X87 0 #ifdef UNIX_AMD64_ABI #define ETW_EBP_FRAMED 1 // if 1 we cannot use EBP as a scratch register and must create EBP based frames for most methods #else // !UNIX_AMD64_ABI #define ETW_EBP_FRAMED 0 // if 1 we cannot use EBP as a scratch register and must create EBP based frames for most methods #endif // !UNIX_AMD64_ABI - #define FEATURE_FP_REGALLOC 0 // Enabled if RegAlloc is used to enregister Floating Point LclVars #define CSE_CONSTS 1 // Enable if we want to CSE constants #define RBM_ALLFLOAT (RBM_XMM0 | RBM_XMM1 | RBM_XMM2 | RBM_XMM3 | RBM_XMM4 | RBM_XMM5 | RBM_XMM6 | RBM_XMM7 | RBM_XMM8 | RBM_XMM9 | RBM_XMM10 | RBM_XMM11 | RBM_XMM12 | RBM_XMM13 | RBM_XMM14 | RBM_XMM15) @@ -936,19 +737,10 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits #define REG_TMP_1 REG_EDX #define RBM_TMP_1 RBM_EDX #endif // !UNIX_AMD64_ABI - #define REG_PAIR_TMP REG_PAIR_EAXEDX - #define RBM_PAIR_TMP (RBM_EAX|RBM_EDX) - #define REG_PAIR_TMP_LO REG_EAX - #define RBM_PAIR_TMP_LO RBM_EAX - #define REG_PAIR_TMP_HI REG_EDX - #define RBM_PAIR_TMP_HI RBM_EDX - #define PREDICT_PAIR_TMP PREDICT_PAIR_RAXRDX - #define PREDICT_PAIR_TMP_LO PREDICT_REG_EAX // register to hold shift amount #define REG_SHIFT REG_ECX #define RBM_SHIFT RBM_ECX - #define PREDICT_REG_SHIFT PREDICT_REG_ECX // This is a general scratch register that does not conflict with the argument registers #define REG_SCRATCH REG_EAX @@ -1015,7 +807,6 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits #define REG_INTRET REG_EAX #define RBM_INTRET RBM_EAX - #define REG_LNGRET REG_EAX #define RBM_LNGRET RBM_EAX #ifdef UNIX_AMD64_ABI @@ -1165,11 +956,6 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits // TODO-ARM-CQ: Check for sdiv/udiv at runtime and generate it if available #define USE_HELPERS_FOR_INT_DIV 1 // BeagleBoard (ARMv7A) doesn't support SDIV/UDIV #define CPU_LOAD_STORE_ARCH 1 -#ifdef LEGACY_BACKEND - #define CPU_LONG_USES_REGPAIR 1 -#else - #define CPU_LONG_USES_REGPAIR 0 -#endif #define CPU_HAS_FP_SUPPORT 1 #define ROUND_FLOAT 0 // Do not round intermed float expression results #define CPU_HAS_BYTE_REGS 0 @@ -1201,9 +987,7 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits #define FEATURE_EH 1 // To aid platform bring-up, eliminate exceptional EH clauses (catch, filter, filter-handler, fault) and directly execute 'finally' clauses. #define FEATURE_EH_FUNCLETS 1 #define FEATURE_EH_CALLFINALLY_THUNKS 0 // Generate call-to-finally code in "thunks" in the enclosing EH region, protected by "cloned finally" clauses. - #define FEATURE_STACK_FP_X87 0 #define ETW_EBP_FRAMED 1 // if 1 we cannot use REG_FP as a scratch register and must setup the frame pointer for most methods - #define FEATURE_FP_REGALLOC 1 // Enabled if RegAlloc is used to enregister Floating Point LclVars #define CSE_CONSTS 1 // Enable if we want to CSE constants #define REG_FP_FIRST REG_F0 @@ -1251,28 +1035,6 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits REG_F24, REG_F25, REG_F26, REG_F27, \ REG_F28, REG_F29, REG_F30, REG_F31, -#ifdef LEGACY_BACKEND - #define MAX_VAR_ORDER_SIZE 32 - - #define REG_TMP_ORDER REG_R3,REG_R2,REG_R1,REG_R0, REG_R4,REG_R5,REG_R6,REG_R7,\ - REG_LR,REG_R12, REG_R8,REG_R9,REG_R10 - #define REG_TMP_ORDER_COUNT 13 - - #define REG_FLT_TMP_ORDER REG_F14, REG_F15, REG_F12, REG_F13, \ - REG_F10, REG_F11, REG_F8, REG_F9, \ - REG_F6, REG_F7, REG_F4, REG_F5, \ - REG_F2, REG_F3, REG_F0, REG_F1, \ - REG_F16, REG_F17, REG_F18, REG_F19, \ - REG_F20, REG_F21, REG_F22, REG_F23, \ - REG_F24, REG_F25, REG_F26, REG_F27, \ - REG_F28, REG_F29, REG_F30, REG_F31, - - #define REG_FLT_TMP_ORDER_COUNT 32 - - #define REG_PREDICT_ORDER REG_LR,REG_R12,REG_R3,REG_R2,REG_R1,REG_R0, \ - REG_R7,REG_R6,REG_R5,REG_R4,REG_R8,REG_R9,REG_R10 -#endif // LEGACY_BACKEND - #define RBM_LOW_REGS (RBM_R0|RBM_R1|RBM_R2|RBM_R3|RBM_R4|RBM_R5|RBM_R6|RBM_R7) #define RBM_HIGH_REGS (RBM_R8|RBM_R9|RBM_R10|RBM_R11|RBM_R12|RBM_SP|RBM_LR|RBM_PC) @@ -1304,38 +1066,17 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits #define REG_TMP_1 REG_R2 #define RBM_TMP_1 RBM_R2 -#ifndef LEGACY_BACKEND // Temporary registers used for the GS cookie check. #define REG_GSCOOKIE_TMP_0 REG_R12 #define REG_GSCOOKIE_TMP_1 REG_LR -#endif // !LEGACY_BACKEND - - // This is the first register pair in REG_TMP_ORDER - #define REG_PAIR_TMP REG_PAIR_R2R3 - #define REG_PAIR_TMP_REVERSE REG_PAIR_R3R2 - #define RBM_PAIR_TMP (RBM_R2|RBM_R3) - #define REG_PAIR_TMP_LO REG_R2 - #define RBM_PAIR_TMP_LO RBM_R2 - #define REG_PAIR_TMP_HI REG_R3 - #define RBM_PAIR_TMP_HI RBM_R3 - #define PREDICT_PAIR_TMP PREDICT_PAIR_R2R3 - #define PREDICT_PAIR_TMP_LO PREDICT_REG_R2 - - // Used when calling the 64-bit Variable shift helper - #define REG_LNGARG_0 REG_PAIR_R0R1 - #define RBM_LNGARG_0 (RBM_R0|RBM_R1) - #define PREDICT_PAIR_LNGARG_0 PREDICT_PAIR_R0R1 - + // register to hold shift amount; no special register is required on the ARM #define REG_SHIFT REG_NA #define RBM_SHIFT RBM_ALLINT - #define PREDICT_REG_SHIFT PREDICT_REG // register to hold shift amount when shifting 64-bit values (this uses a helper call) #define REG_SHIFT_LNG REG_R2 // REG_ARG_2 #define RBM_SHIFT_LNG RBM_R2 // RBM_ARG_2 - #define PREDICT_REG_SHIFT_LNG PREDICT_REG_R2 - // This is a general scratch register that does not conflict with the argument registers #define REG_SCRATCH REG_LR @@ -1399,18 +1140,10 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits #define REG_PINVOKE_COOKIE_PARAM REG_R4 #define RBM_PINVOKE_COOKIE_PARAM RBM_R4 -#ifdef LEGACY_BACKEND - #define PREDICT_REG_PINVOKE_COOKIE_PARAM PREDICT_REG_R4 -#endif // LEGACY_BACKEND - // GenericPInvokeCalliHelper unmanaged target Parameter #define REG_PINVOKE_TARGET_PARAM REG_R12 #define RBM_PINVOKE_TARGET_PARAM RBM_R12 -#ifdef LEGACY_BACKEND - #define PREDICT_REG_PINVOKE_TARGET_PARAM PREDICT_REG_R12 -#endif // LEGACY_BACKEND - // IL stub's secret MethodDesc parameter (JitFlags::JIT_FLAG_PUBLISH_SECRET_PARAM) #define REG_SECRET_STUB_PARAM REG_R12 #define RBM_SECRET_STUB_PARAM RBM_R12 @@ -1427,13 +1160,6 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits #define REG_PINVOKE_SCRATCH REG_R6 #define RBM_PINVOKE_SCRATCH RBM_R6 -#ifdef LEGACY_BACKEND - #define REG_SPILL_CHOICE REG_LR - #define RBM_SPILL_CHOICE RBM_LR - #define REG_SPILL_CHOICE_FLT REG_F14 - #define RBM_SPILL_CHOICE_FLT (RBM_F14|RBM_F15) -#endif // LEGACY_BACKEND - // The following defines are useful for iterating a regNumber #define REG_FIRST REG_R0 #define REG_INT_FIRST REG_R0 @@ -1464,7 +1190,6 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits // Which register are int and long values returned in ? #define REG_INTRET REG_R0 #define RBM_INTRET RBM_R0 - #define REG_LNGRET REG_PAIR_R0R1 #define RBM_LNGRET (RBM_R1|RBM_R0) #define REG_LNGRET_LO REG_R0 #define REG_LNGRET_HI REG_R1 @@ -1550,7 +1275,6 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits #elif defined(_TARGET_ARM64_) #define CPU_LOAD_STORE_ARCH 1 - #define CPU_LONG_USES_REGPAIR 0 #define CPU_HAS_FP_SUPPORT 1 #define ROUND_FLOAT 0 // Do not round intermed float expression results #define CPU_HAS_BYTE_REGS 0 @@ -1587,9 +1311,7 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits #define FEATURE_EH 1 // To aid platform bring-up, eliminate exceptional EH clauses (catch, filter, filter-handler, fault) and directly execute 'finally' clauses. #define FEATURE_EH_FUNCLETS 1 #define FEATURE_EH_CALLFINALLY_THUNKS 1 // Generate call-to-finally code in "thunks" in the enclosing EH region, protected by "cloned finally" clauses. - #define FEATURE_STACK_FP_X87 0 #define ETW_EBP_FRAMED 1 // if 1 we cannot use REG_FP as a scratch register and must setup the frame pointer for most methods - #define FEATURE_FP_REGALLOC 0 // Enabled if RegAlloc is used to enregister Floating Point LclVars #define CSE_CONSTS 1 // Enable if we want to CSE constants #define REG_FP_FIRST REG_V0 @@ -1672,7 +1394,6 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits // register to hold shift amount; no special register is required on ARM64. #define REG_SHIFT REG_NA #define RBM_SHIFT RBM_ALLINT - #define PREDICT_REG_SHIFT PREDICT_REG // This is a general scratch register that does not conflict with the argument registers #define REG_SCRATCH REG_R9 @@ -1790,7 +1511,6 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits // Which register are int and long values returned in ? #define REG_INTRET REG_R0 #define RBM_INTRET RBM_R0 - #define REG_LNGRET REG_R0 #define RBM_LNGRET RBM_R0 // second return register for 16-byte structs #define REG_INTRET_1 REG_R1 @@ -1967,19 +1687,6 @@ public: ARG_ORDER_L2R }; static const enum ArgOrder g_tgtArgOrder; - -#ifdef LEGACY_BACKEND -#if NOGC_WRITE_BARRIERS - static regMaskTP exclude_WriteBarrierReg(regMaskTP mask) - { - unsigned result = (mask & ~RBM_WRITE_BARRIER); - if (result) - return result; - else - return RBM_ALLINT & ~RBM_WRITE_BARRIER; - } -#endif // NOGC_WRITE_BARRIERS -#endif // LEGACY_BACKEND }; #if defined(DEBUG) || defined(LATE_DISASM) @@ -2005,14 +1712,6 @@ inline BOOL isByteReg(regNumber reg) } #endif -#ifdef LEGACY_BACKEND -extern const regNumber raRegTmpOrder[REG_TMP_ORDER_COUNT]; -extern const regNumber rpRegTmpOrder[REG_TMP_ORDER_COUNT]; -#if FEATURE_FP_REGALLOC -extern const regNumber raRegFltTmpOrder[REG_FLT_TMP_ORDER_COUNT]; -#endif -#endif // LEGACY_BACKEND - inline regMaskTP genRegMask(regNumber reg); inline regMaskTP genRegMaskFloat(regNumber reg, var_types type = TYP_DOUBLE); @@ -2219,21 +1918,13 @@ inline regMaskTP genRegMask(regNumber reg) * Map a register number to a floating-point register mask. */ -#if defined(_TARGET_X86_) && defined(LEGACY_BACKEND) -extern const regMaskSmall regFPMasks[REG_FPCOUNT]; -#endif // defined(_TARGET_X86_) && defined(LEGACY_BACKEND) - inline regMaskTP genRegMaskFloat(regNumber reg, var_types type /* = TYP_DOUBLE */) { -#if defined(_TARGET_X86_) && defined(LEGACY_BACKEND) - assert(reg >= REG_FPV0 && reg < REG_FPCOUNT); - assert((unsigned)reg < ArrLen(regFPMasks)); - return regFPMasks[reg]; -#elif defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_) || defined(_TARGET_X86_) +#if defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_) || defined(_TARGET_X86_) assert(genIsValidFloatReg(reg)); assert((unsigned)reg < ArrLen(regMasks)); return regMasks[reg]; -#elif defined _TARGET_ARM_ +#elif defined(_TARGET_ARM_) assert(floatRegCanHoldType(reg, type)); assert(reg >= REG_F0 && reg <= REG_F31); @@ -2299,65 +1990,6 @@ extern const regMaskTP raRbmCalleeSaveOrder[CNT_CALLEE_SAVED]; // This method takes a "compact" bitset of the callee-saved registers, and "expands" it to a full register mask. regMaskSmall genRegMaskFromCalleeSavedMask(unsigned short); -/***************************************************************************** - * - * Returns the register that holds the low 32 bits of the long value given - * by the register pair 'regPair'. - */ -inline regNumber genRegPairLo(regPairNo regPair) -{ - assert(regPair >= REG_PAIR_FIRST && regPair <= REG_PAIR_LAST); - - return (regNumber)((regPair - REG_PAIR_FIRST) & REG_PAIR_NMASK); -} - -/***************************************************************************** - * - * Returns the register that holds the high 32 bits of the long value given - * by the register pair 'regPair'. - */ -inline regNumber genRegPairHi(regPairNo regPair) -{ - assert(regPair >= REG_PAIR_FIRST && regPair <= REG_PAIR_LAST); - - return (regNumber)(((regPair - REG_PAIR_FIRST) >> REG_PAIR_NBITS) & REG_PAIR_NMASK); -} - -/***************************************************************************** - * - * Returns whether regPair is a combination of two "real" registers - * or whether it contains a pseudo register. - * - * In debug it also asserts that reg1 and reg2 are not the same. - */ -bool genIsProperRegPair(regPairNo regPair); - -/***************************************************************************** - * - * Returns the register pair number that corresponds to the given two regs. - */ -inline regPairNo gen2regs2pair(regNumber regLo, regNumber regHi) -{ - assert(regLo != regHi || regLo == REG_STK); - assert(genIsValidReg(regLo) && genIsValidReg(regHi)); - assert(regLo != REG_L_STK && regHi != REG_L_STK); - - regPairNo regPair = (regPairNo)(regLo + (regHi << REG_PAIR_NBITS) + REG_PAIR_FIRST); - - assert(regLo == genRegPairLo(regPair)); - assert(regHi == genRegPairHi(regPair)); - - return regPair; -} - -/*****************************************************************************/ -inline regMaskTP genRegPairMask(regPairNo regPair) -{ - assert(regPair >= REG_PAIR_FIRST && regPair <= REG_PAIR_LAST); - - return genRegMask(genRegPairLo(regPair)) | genRegMask(genRegPairHi(regPair)); -} - /***************************************************************************** * * Assumes that "reg" is of the given "type". Return the next unused reg number after "reg" @@ -2406,21 +2038,6 @@ inline regNumber regNextOfType(regNumber reg, var_types type) * Type checks */ -inline bool isRegPairType(int /* s/b "var_types" */ type) -{ -#if !CPU_LONG_USES_REGPAIR - return false; -#else -#ifdef _TARGET_64BIT_ - return false; -#elif CPU_HAS_FP_SUPPORT - return type == TYP_LONG; -#else - return type == TYP_LONG || type == TYP_DOUBLE; -#endif -#endif // CPU_LONG_USES_REGPAIR -} - inline bool isFloatRegType(int /* s/b "var_types" */ type) { #if CPU_HAS_FP_SUPPORT diff --git a/src/jit/treelifeupdater.cpp b/src/jit/treelifeupdater.cpp index 6cefb8ae4d..1bd3b7cda2 100644 --- a/src/jit/treelifeupdater.cpp +++ b/src/jit/treelifeupdater.cpp @@ -58,29 +58,6 @@ void TreeLifeUpdater::UpdateLifeVar(GenTree* tree) // The ADDR might occur in a context where the address it contributes is eventually // dereferenced, so we can't say that this is not a use or def. } -#if 0 - // TODO-ARM64-Bug?: These asserts don't seem right for ARM64: I don't understand why we have to assert - // two consecutive lclvars (in execution order) can only be observed if the first one is a struct field. - // It seems to me this is code only applicable to the legacy JIT and not RyuJIT (and therefore why it was - // ifdef'ed out for AMD64). - else if (!varDsc->lvIsStructField) - { - GenTree* prevTree; - for (prevTree = tree->gtPrev; - prevTree != NULL && prevTree != compCurLifeTree; - prevTree = prevTree->gtPrev) - { - if ((prevTree->gtOper == GT_LCL_VAR) || (prevTree->gtOper == GT_REG_VAR)) - { - LclVarDsc * prevVarDsc = compiler->lvaTable + prevTree->gtLclVarCommon.gtLclNum; - - // These are the only things for which this method MUST be called - assert(prevVarDsc->lvIsStructField); - } - } - assert(prevTree == compCurLifeTree); - } -#endif // 0 } #endif // !_TARGET_AMD64_ #endif // DEBUG @@ -99,16 +76,12 @@ void TreeLifeUpdater::UpdateLifeVar(GenTree* tree) // if it's "x =..." then variable "x" must have had a previous, original, site to be born. bool isBorn = ((tree->gtFlags & GTF_VAR_DEF) != 0 && (tree->gtFlags & GTF_VAR_USEASG) == 0); bool isDying = ((tree->gtFlags & GTF_VAR_DEATH) != 0); -#ifndef LEGACY_BACKEND - bool spill = ((tree->gtFlags & GTF_SPILL) != 0); -#endif // !LEGACY_BACKEND + bool spill = ((tree->gtFlags & GTF_SPILL) != 0); -#ifndef LEGACY_BACKEND - // For RyuJIT backend, since all tracked vars are register candidates, but not all are in registers at all times, + // Since all tracked vars are register candidates, but not all are in registers at all times, // we maintain two separate sets of variables - the total set of variables that are either // born or dying here, and the subset of those that are on the stack VarSetOps::ClearD(compiler, stackVarDeltaSet); -#endif // !LEGACY_BACKEND if (isBorn || isDying) { @@ -122,26 +95,18 @@ void TreeLifeUpdater::UpdateLifeVar(GenTree* tree) VarSetOps::AddElemD(compiler, varDeltaSet, varDsc->lvVarIndex); if (ForCodeGen) { -#ifndef LEGACY_BACKEND if (isBorn && varDsc->lvIsRegCandidate() && tree->gtHasReg()) { compiler->codeGen->genUpdateVarReg(varDsc, tree); } -#endif // !LEGACY_BACKEND - if (varDsc->lvIsInReg() -#ifndef LEGACY_BACKEND - && tree->gtRegNum != REG_NA -#endif // !LEGACY_BACKEND - ) + if (varDsc->lvIsInReg() && tree->gtRegNum != REG_NA) { compiler->codeGen->genUpdateRegLife(varDsc, isBorn, isDying DEBUGARG(tree)); } -#ifndef LEGACY_BACKEND else { VarSetOps::AddElemD(compiler, stackVarDeltaSet, varDsc->lvVarIndex); } -#endif // !LEGACY_BACKEND } } else if (varDsc->lvPromoted) @@ -174,40 +139,32 @@ void TreeLifeUpdater::UpdateLifeVar(GenTree* tree) // test in this, the common case, where we have no deadTrackedFieldVars. if (fldVarDsc->lvIsInReg()) { -#ifndef LEGACY_BACKEND if (isBorn) { compiler->codeGen->genUpdateVarReg(fldVarDsc, tree); } -#endif // !LEGACY_BACKEND compiler->codeGen->genUpdateRegLife(fldVarDsc, isBorn, isDying DEBUGARG(tree)); } -#ifndef LEGACY_BACKEND else { VarSetOps::AddElemD(compiler, stackVarDeltaSet, fldVarIndex); } -#endif // !LEGACY_BACKEND } } else if (ForCodeGen && VarSetOps::IsMember(compiler, varDeltaSet, fldVarIndex)) { if (compiler->lvaTable[i].lvIsInReg()) { -#ifndef LEGACY_BACKEND if (isBorn) { compiler->codeGen->genUpdateVarReg(fldVarDsc, tree); } -#endif // !LEGACY_BACKEND compiler->codeGen->genUpdateRegLife(fldVarDsc, isBorn, isDying DEBUGARG(tree)); } -#ifndef LEGACY_BACKEND else { VarSetOps::AddElemD(compiler, stackVarDeltaSet, fldVarIndex); } -#endif // !LEGACY_BACKEND } } } @@ -253,8 +210,6 @@ void TreeLifeUpdater::UpdateLifeVar(GenTree* tree) if (ForCodeGen) { -#ifndef LEGACY_BACKEND - // Only add vars to the gcInfo.gcVarPtrSetCur if they are currently on stack, since the // gcInfo.gcTrkStkPtrLcls // includes all TRACKED vars that EVER live on the stack (i.e. are not always in a register). @@ -289,34 +244,10 @@ void TreeLifeUpdater::UpdateLifeVar(GenTree* tree) #endif // DEBUG } -#else // LEGACY_BACKEND - -#ifdef DEBUG - if (compiler->verbose) - { - VarSetOps::Assign(compiler, gcVarPtrSetNew, newLife); - VarSetOps::IntersectionD(compiler, gcVarPtrSetNew, compiler->codeGen->gcInfo.gcTrkStkPtrLcls); - if (!VarSetOps::Equal(compiler, compiler->codeGen->gcInfo.gcVarPtrSetCur, gcVarPtrSetNew)) - { - printf("\t\t\t\t\t\t\tGCvars: "); - dumpConvertedVarSet(compiler, compiler->codeGen->gcInfo.gcVarPtrSetCur); - printf(" => "); - dumpConvertedVarSet(compiler, gcVarPtrSetNew); - printf("\n"); - } - } -#endif // DEBUG - VarSetOps::Assign(compiler, compiler->codeGen->gcInfo.gcVarPtrSetCur, - compiler->codeGen->gcInfo.gcTrkStkPtrLcls); - VarSetOps::IntersectionD(compiler, compiler->codeGen->gcInfo.gcVarPtrSetCur, newLife); - -#endif // LEGACY_BACKEND - compiler->codeGen->siUpdate(); } } -#ifndef LEGACY_BACKEND if (ForCodeGen && spill) { assert(!varDsc->lvPromoted); @@ -335,7 +266,6 @@ void TreeLifeUpdater::UpdateLifeVar(GenTree* tree) } } } -#endif // !LEGACY_BACKEND } //------------------------------------------------------------------------ diff --git a/src/jit/utils.cpp b/src/jit/utils.cpp index 46d704c532..a2016b8059 100644 --- a/src/jit/utils.cpp +++ b/src/jit/utils.cpp @@ -138,27 +138,8 @@ const char* getRegName(regNumber reg, bool isFloat) { return "NA"; } -#if defined(_TARGET_X86_) && defined(LEGACY_BACKEND) - static const char* const regNames[] = { -#define REGDEF(name, rnum, mask, sname) sname, -#include "register.h" - }; - static const char* const floatRegNames[] = { -#define REGDEF(name, rnum, mask, sname) sname, -#include "registerxmm.h" - }; - if (isFloat) - { - assert(reg < ArrLen(floatRegNames)); - return floatRegNames[reg]; - } - else - { - assert(reg < ArrLen(regNames)); - return regNames[reg]; - } -#elif defined(_TARGET_ARM64_) +#if defined(_TARGET_ARM64_) static const char* const regNames[] = { #define REGDEF(name, rnum, mask, xname, wname) xname, #include "register.h" @@ -252,16 +233,6 @@ const char* getRegNameFloat(regNumber reg, var_types type) return regName; } -#elif defined(_TARGET_X86_) && defined(LEGACY_BACKEND) - - static const char* regNamesFloat[] = { -#define REGDEF(name, rnum, mask, sname) sname, -#include "registerxmm.h" - }; - assert((unsigned)reg < ArrLen(regNamesFloat)); - - return regNamesFloat[reg]; - #elif defined(_TARGET_ARM64_) static const char* regNamesFloat[] = { @@ -418,7 +389,6 @@ void dspRegMask(regMaskTP regMask, size_t minSiz) } #endif -#if !FEATURE_STACK_FP_X87 if (strlen(sep) > 0) { // We've already printed something. @@ -465,7 +435,6 @@ void dspRegMask(regMaskTP regMask, size_t minSiz) regPrev = regNum; } -#endif printf("]"); diff --git a/src/jit/utils.h b/src/jit/utils.h index acda8cf989..8dadabb3bc 100644 --- a/src/jit/utils.h +++ b/src/jit/utils.h @@ -390,9 +390,7 @@ public: PhasedVar& operator=(const T& value) { #ifdef DEBUG -#ifndef LEGACY_BACKEND assert(m_writePhase); -#endif // !LEGACY_BACKEND m_initialized = true; #endif // DEBUG m_value = value; @@ -402,9 +400,7 @@ public: PhasedVar& operator&=(const T& value) { #ifdef DEBUG -#ifndef LEGACY_BACKEND assert(m_writePhase); -#endif // !LEGACY_BACKEND m_initialized = true; #endif // DEBUG m_value &= value; diff --git a/src/jit/valuenum.cpp b/src/jit/valuenum.cpp index 09c969e13b..dfc4e67a74 100644 --- a/src/jit/valuenum.cpp +++ b/src/jit/valuenum.cpp @@ -930,11 +930,7 @@ ValueNum ValueNumStore::VNZeroForType(var_types typ) case TYP_ULONG: return VNForLongCon(0); case TYP_FLOAT: -#if FEATURE_X87_DOUBLES - return VNForDoubleCon(0.0); -#else return VNForFloatCon(0.0f); -#endif case TYP_DOUBLE: return VNForDoubleCon(0.0); case TYP_REF: @@ -5981,53 +5977,7 @@ void Compiler::fgValueNumberTree(GenTree* tree, bool evalAsgLhsInd) } else // Must be an "op=" { -#ifndef LEGACY_BACKEND unreached(); -#else - // If the LHS is an IND, we didn't evaluate it when we visited it previously. - // But we didn't know that the parent was an op=. We do now, so go back and evaluate it. - // (We actually check if the effective val is the IND. We will have evaluated any non-last - // args of an LHS comma already -- including their memory effects.) - GenTree* lhsVal = lhs->gtEffectiveVal(/*commaOnly*/ true); - if (lhsVal->OperIsIndir() || (lhsVal->OperGet() == GT_CLS_VAR)) - { - fgValueNumberTree(lhsVal, /*evalAsgLhsInd*/ true); - } - // Now we can make this assertion: - assert(lhsVal->gtVNPair.BothDefined()); - genTreeOps op = GenTree::OpAsgToOper(oper); - if (GenTree::OperIsBinary(op)) - { - ValueNumPair lhsNormVNP; - ValueNumPair lhsExcVNP; - lhsExcVNP.SetBoth(ValueNumStore::VNForEmptyExcSet()); - vnStore->VNPUnpackExc(lhsVal->gtVNPair, &lhsNormVNP, &lhsExcVNP); - assert(rhs->gtVNPair.BothDefined()); - ValueNumPair rhsNormVNP; - ValueNumPair rhsExcVNP; - rhsExcVNP.SetBoth(ValueNumStore::VNForEmptyExcSet()); - vnStore->VNPUnpackExc(rhs->gtVNPair, &rhsNormVNP, &rhsExcVNP); - rhsVNPair = vnStore->VNPWithExc(vnStore->VNPairForFunc(tree->TypeGet(), - GetVNFuncForOper(op, (tree->gtFlags & - GTF_UNSIGNED) != 0), - lhsNormVNP, rhsNormVNP), - vnStore->VNPExcSetUnion(lhsExcVNP, rhsExcVNP)); - } - else - { - // As of now, GT_CHS ==> GT_NEG is the only pattern fitting this. - assert(GenTree::OperIsUnary(op)); - ValueNumPair lhsNormVNP; - ValueNumPair lhsExcVNP; - lhsExcVNP.SetBoth(ValueNumStore::VNForEmptyExcSet()); - vnStore->VNPUnpackExc(lhsVal->gtVNPair, &lhsNormVNP, &lhsExcVNP); - rhsVNPair = vnStore->VNPWithExc(vnStore->VNPairForFunc(tree->TypeGet(), - GetVNFuncForOper(op, (tree->gtFlags & - GTF_UNSIGNED) != 0), - lhsNormVNP), - lhsExcVNP); - } -#endif // !LEGACY_BACKEND } // Is the type being stored different from the type computed by the rhs? diff --git a/src/jit/varset.h b/src/jit/varset.h index 2fd372371b..09fa2f7b61 100644 --- a/src/jit/varset.h +++ b/src/jit/varset.h @@ -72,14 +72,8 @@ typedef BitSetShortLongRep VARSET_TP; // Tested various sizes for max tracked locals. The largest value for which no throughput regression // could be measured was 512. Going to 1024 showed the first throughput regressions. // We anticipate the larger size will be needed to support better inlining. -// There were a number of failures when 512 was used for legacy, so we just retain the 128 value -// for legacy backend. -#if !defined(LEGACY_BACKEND) const unsigned lclMAX_TRACKED = 512; -#else -const unsigned lclMAX_TRACKED = 128; -#endif #define VARSET_REP_IS_CLASS 0 -- cgit v1.2.3