summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/jit/CMakeLists.txt34
-rw-r--r--src/jit/DIRS.proj10
-rw-r--r--src/jit/assertionprop.cpp3
-rw-r--r--src/jit/block.cpp20
-rw-r--r--src/jit/block.h24
-rw-r--r--src/jit/codegen.h49
-rw-r--r--src/jit/codegenarm.cpp4
-rw-r--r--src/jit/codegenarm64.cpp4
-rw-r--r--src/jit/codegenarmarch.cpp4
-rw-r--r--src/jit/codegenclassic.h598
-rw-r--r--src/jit/codegencommon.cpp1016
-rw-r--r--src/jit/codegeninterface.h85
-rw-r--r--src/jit/codegenlegacy.cpp22278
-rw-r--r--src/jit/codegenlinear.cpp36
-rw-r--r--src/jit/codegenlinear.h4
-rw-r--r--src/jit/codegenxarch.cpp4
-rw-r--r--src/jit/compiler.cpp590
-rw-r--r--src/jit/compiler.h464
-rw-r--r--src/jit/compiler.hpp193
-rw-r--r--src/jit/compphases.h5
-rw-r--r--src/jit/crossgen/jit_crossgen.nativeproj3
-rw-r--r--src/jit/decomposelongs.cpp2
-rw-r--r--src/jit/ee_il_dll.cpp4
-rw-r--r--src/jit/emit.cpp78
-rw-r--r--src/jit/emit.h221
-rw-r--r--src/jit/emitarm.cpp76
-rw-r--r--src/jit/emitarm64.cpp2
-rw-r--r--src/jit/emitfmtsxarch.h49
-rw-r--r--src/jit/emitpub.h4
-rw-r--r--src/jit/emitxarch.cpp555
-rw-r--r--src/jit/emitxarch.h84
-rw-r--r--src/jit/error.cpp4
-rw-r--r--src/jit/error.h14
-rw-r--r--src/jit/flowgraph.cpp327
-rw-r--r--src/jit/fp.h73
-rw-r--r--src/jit/gcencode.cpp25
-rw-r--r--src/jit/gcinfo.cpp31
-rw-r--r--src/jit/gentree.cpp881
-rw-r--r--src/jit/gentree.h274
-rw-r--r--src/jit/gtlist.h42
-rw-r--r--src/jit/gtstructs.h18
-rw-r--r--src/jit/importer.cpp118
-rw-r--r--src/jit/inline.h5
-rw-r--r--src/jit/instr.cpp1921
-rw-r--r--src/jit/instrsxarch.h70
-rw-r--r--src/jit/jit.h6
-rw-r--r--src/jit/jit.settings.targets43
-rw-r--r--src/jit/jitconfigvalues.h8
-rw-r--r--src/jit/jiteh.cpp10
-rw-r--r--src/jit/jitgcinfo.h4
-rw-r--r--src/jit/lclvars.cpp188
-rw-r--r--src/jit/legacyjit/.gitmirror1
-rw-r--r--src/jit/legacyjit/CMakeLists.txt65
-rw-r--r--src/jit/legacynonjit/.gitmirror1
-rw-r--r--src/jit/legacynonjit/CMakeLists.txt71
-rw-r--r--src/jit/legacynonjit/legacynonjit.def7
-rw-r--r--src/jit/lir.h2
-rw-r--r--src/jit/liveness.cpp747
-rw-r--r--src/jit/lower.cpp7
-rw-r--r--src/jit/lowerarm.cpp34
-rw-r--r--src/jit/lowerarm64.cpp34
-rw-r--r--src/jit/lowerarmarch.cpp4
-rw-r--r--src/jit/lowerxarch.cpp8
-rw-r--r--src/jit/lsra.cpp7
-rw-r--r--src/jit/lsra.h4
-rw-r--r--src/jit/lsraarm.cpp4
-rw-r--r--src/jit/lsraarm64.cpp4
-rw-r--r--src/jit/lsraarmarch.cpp4
-rw-r--r--src/jit/lsrabuild.cpp10
-rw-r--r--src/jit/lsraxarch.cpp9
-rw-r--r--src/jit/morph.cpp1068
-rw-r--r--src/jit/optcse.cpp11
-rw-r--r--src/jit/optimizer.cpp84
-rw-r--r--src/jit/rangecheck.cpp33
-rw-r--r--src/jit/rationalize.cpp2
-rw-r--r--src/jit/regalloc.cpp6411
-rw-r--r--src/jit/regalloc.h81
-rw-r--r--src/jit/register.h8
-rw-r--r--src/jit/registerfp.cpp1515
-rw-r--r--src/jit/registerfp.h26
-rw-r--r--src/jit/registerxmm.h48
-rw-r--r--src/jit/regset.cpp2760
-rw-r--r--src/jit/regset.h293
-rw-r--r--src/jit/sharedfloat.cpp500
-rw-r--r--src/jit/simd.cpp4
-rw-r--r--src/jit/simdcodegenxarch.cpp7
-rw-r--r--src/jit/stackfp.cpp4506
-rw-r--r--src/jit/stacklevelsetter.cpp4
-rw-r--r--src/jit/target.h405
-rw-r--r--src/jit/treelifeupdater.cpp76
-rw-r--r--src/jit/utils.cpp33
-rw-r--r--src/jit/utils.h4
-rw-r--r--src/jit/valuenum.cpp50
-rw-r--r--src/jit/varset.h6
94 files changed, 783 insertions, 48740 deletions
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 @@
</PropertyGroup>
<ItemGroup Condition="'$(BuildExePhase)' == '1'">
- <!-- x86 and ARM clrjit.dll are built in the JIT32 directory; we build FrankenJit here -->
+ <!-- x86 and ARM clrjit.dll are built in the JIT32 directory -->
<ProjectFile Condition="'$(BuildArchitecture)' != 'i386' and '$(BuildArchitecture)' != 'arm'" Include="dll\jit.nativeproj" />
</ItemGroup>
<!-- Only the main JIT gets built for CoreSys. The other jits (e.g., altjits) do not. -->
<ItemGroup Condition="'$(BuildExePhase)' == '1' and '$(BuildProjectName)' != 'CoreSys'">
- <!-- FrankenJit and FrankenAltJit builds are disabled. We do not test them in CoreCLR and changes from there regularly break legacy_bakend for x86, it is why it is disabled now
- and should be completely deleted soon. -->
- <!-- Build the "FrankenJit" (RyuJIT front-end, legacy back-end) and "FrankenAltjit". These can't conflict with the names of the JIT32 directory outputs. -->
- <!--
- <ProjectFile Condition="'$(BuildArchitecture)' == 'i386' or '$(BuildArchitecture)' == 'arm'" Include="frankenjit\frankenjit.nativeproj" />
- <ProjectFile Condition="'$(BuildArchitecture)' == 'i386'" Include="frankenaltjit\frankenaltjit.nativeproj" />
- -->
-
<!-- This might be useful, to help make sure JIT devs build all configurations of the JIT (including crossgen), but
it appears to cause problems with the build system, and it slows down normal JIT developer productivity by adding a seldom-useful build.
<ProjectFile Include="crossgen\jit_crossgen.nativeproj" />
diff --git a/src/jit/assertionprop.cpp b/src/jit/assertionprop.cpp
index 8193d2902c..3d0aa943fe 100644
--- a/src/jit/assertionprop.cpp
+++ b/src/jit/assertionprop.cpp
@@ -4772,9 +4772,6 @@ Compiler::fgWalkResult Compiler::optVNConstantPropCurStmt(BasicBlock* block, Gen
case GT_RSH:
case GT_RSZ:
case GT_NEG:
-#ifdef LEGACY_BACKEND
- case GT_CHS:
-#endif
case GT_CAST:
case GT_INTRINSIC:
break;
diff --git a/src/jit/block.cpp b/src/jit/block.cpp
index b75aa1ea61..29f3daf05a 100644
--- a/src/jit/block.cpp
+++ b/src/jit/block.cpp
@@ -363,17 +363,10 @@ void BasicBlock::dspFlags()
{
printf("IBC ");
}
-#ifdef LEGACY_BACKEND
- if (bbFlags & BBF_FORWARD_SWITCH)
- {
- printf("fswitch ");
- }
-#else // !LEGACY_BACKEND
if (bbFlags & BBF_IS_LIR)
{
printf("LIR ");
}
-#endif // LEGACY_BACKEND
if (bbFlags & BBF_KEEP_BBJ_ALWAYS)
{
printf("KEEP ");
@@ -636,9 +629,6 @@ bool BasicBlock::CloneBlockState(
to->bbCodeOffs = from->bbCodeOffs;
to->bbCodeOffsEnd = from->bbCodeOffsEnd;
VarSetOps::AssignAllowUninitRhs(compiler, to->bbScope, from->bbScope);
-#if FEATURE_STACK_FP_X87
- to->bbFPStateX87 = from->bbFPStateX87;
-#endif // FEATURE_STACK_FP_X87
to->bbNatLoopNum = from->bbNatLoopNum;
#ifdef DEBUG
to->bbLoopNum = from->bbLoopNum;
@@ -663,9 +653,6 @@ bool BasicBlock::CloneBlockState(
// LIR helpers
void BasicBlock::MakeLIR(GenTree* firstNode, GenTree* lastNode)
{
-#ifdef LEGACY_BACKEND
- unreached();
-#else // !LEGACY_BACKEND
assert(!IsLIR());
assert((firstNode == nullptr) == (lastNode == nullptr));
assert((firstNode == lastNode) || firstNode->Precedes(lastNode));
@@ -673,18 +660,13 @@ void BasicBlock::MakeLIR(GenTree* firstNode, GenTree* lastNode)
m_firstNode = firstNode;
m_lastNode = lastNode;
bbFlags |= BBF_IS_LIR;
-#endif // LEGACY_BACKEND
}
bool BasicBlock::IsLIR()
{
-#ifdef LEGACY_BACKEND
- return false;
-#else // !LEGACY_BACKEND
const bool isLIR = (bbFlags & BBF_IS_LIR) != 0;
assert((bbTreeList == nullptr) || ((isLIR) == !bbTreeList->IsStatement()));
return isLIR;
-#endif // LEGACY_BACKEND
}
//------------------------------------------------------------------------
@@ -1311,12 +1293,10 @@ BasicBlock* Compiler::bbNewBasicBlock(BBjumpKinds jumpKind)
block->bbNum = ++fgBBNumMax;
}
-#ifndef LEGACY_BACKEND
if (compRationalIRForm)
{
block->bbFlags |= BBF_IS_LIR;
}
-#endif // !LEGACY_BACKEND
block->bbRefs = 1;
block->bbWeight = BB_UNITY_WEIGHT;
diff --git a/src/jit/block.h b/src/jit/block.h
index 96c671cd73..ca7bc365fd 100644
--- a/src/jit/block.h
+++ b/src/jit/block.h
@@ -77,10 +77,6 @@ struct BasicBlockList;
struct flowList;
struct EHblkDsc;
-#if FEATURE_STACK_FP_X87
-struct FlatFPStateX87;
-#endif
-
/*****************************************************************************
*
* The following describes a switch block.
@@ -429,17 +425,7 @@ struct BasicBlock : private LIR::Range
#define BBF_COLD 0x10000000 // BB is cold
#define BBF_PROF_WEIGHT 0x20000000 // BB weight is computed from profile data
-
-#ifdef LEGACY_BACKEND
-
-#define BBF_FORWARD_SWITCH 0x40000000 // Aux flag used in FP codegen to know if a jmptable entry has been forwarded
-
-#else // !LEGACY_BACKEND
-
#define BBF_IS_LIR 0x40000000 // Set if the basic block contains LIR (as opposed to HIR)
-
-#endif // LEGACY_BACKEND
-
#define BBF_KEEP_BBJ_ALWAYS 0x80000000 // A special BBJ_ALWAYS block, used by EH code generation. Keep the jump kind
// as BBJ_ALWAYS. Used for the paired BBJ_ALWAYS block following the
// BBJ_CALLFINALLY block, as well as, on x86, the final step block out of a
@@ -473,14 +459,8 @@ struct BasicBlock : private LIR::Range
// Flags a block should not have had before it is split.
-#ifdef LEGACY_BACKEND
-#define BBF_SPLIT_NONEXIST \
- (BBF_CHANGED | BBF_LOOP_HEAD | BBF_LOOP_CALL0 | BBF_LOOP_CALL1 | BBF_RETLESS_CALL | BBF_LOOP_PREHEADER | \
- BBF_COLD | BBF_FORWARD_SWITCH)
-#else // !LEGACY_BACKEND
#define BBF_SPLIT_NONEXIST \
(BBF_CHANGED | BBF_LOOP_HEAD | BBF_LOOP_CALL0 | BBF_LOOP_CALL1 | BBF_RETLESS_CALL | BBF_LOOP_PREHEADER | BBF_COLD)
-#endif // LEGACY_BACKEND
// Flags lost by the top block when a block is split.
// Note, this is a conservative guess.
@@ -999,10 +979,6 @@ struct BasicBlock : private LIR::Range
verTypeVal* bbTypesOut; // list of variable types on output
#endif // VERIFIER
-#if FEATURE_STACK_FP_X87
- FlatFPStateX87* bbFPStateX87; // State of FP stack on entry to the basic block
-#endif // FEATURE_STACK_FP_X87
-
/* The following fields used for loop detection */
typedef unsigned char loopNumber;
diff --git a/src/jit/codegen.h b/src/jit/codegen.h
index d67ad1dca7..d6cd23a4c9 100644
--- a/src/jit/codegen.h
+++ b/src/jit/codegen.h
@@ -49,7 +49,7 @@ public:
bool nogen = false);
private:
-#if defined(_TARGET_XARCH_) && !FEATURE_STACK_FP_X87
+#if defined(_TARGET_XARCH_)
// Bit masks used in negating a float or double number.
// This is to avoid creating more than one data constant for these bitmasks when a
// method has more than one GT_NEG operation on floating point values.
@@ -68,7 +68,7 @@ private:
// Generates SSE41 code for the given tree as a round operation
void genSSE41RoundOp(GenTreeOp* treeNode);
-#endif // defined(_TARGET_XARCH_) && !FEATURE_STACK_FP_X87
+#endif // defined(_TARGET_XARCH_)
void genPrepForCompiler();
@@ -143,16 +143,6 @@ private:
}
#endif // REG_OPT_RSVD
-#ifdef LEGACY_BACKEND
- regNumber findStkLclInReg(unsigned lclNum)
- {
-#ifdef DEBUG
- genInterruptibleUsed = true;
-#endif
- return regTracker.rsLclIsInReg(lclNum);
- }
-#endif
-
//-------------------------------------------------------------------------
bool genUseBlockInit; // true if we plan to block-initialize the local stack frame
@@ -199,16 +189,6 @@ private:
void genGenerateStackProbe();
#endif
-#ifdef LEGACY_BACKEND
- regMaskTP genNewLiveRegMask(GenTree* first, GenTree* second);
-
- // During codegen, determine the LiveSet after tree.
- // Preconditions: must be called during codegen, when compCurLife and
- // compCurLifeTree are being maintained, and tree must occur in the current
- // statement.
- VARSET_VALRET_TP genUpdateLiveSetForward(GenTree* tree);
-#endif
-
//-------------------------------------------------------------------------
void genReportEH();
@@ -260,17 +240,10 @@ protected:
void genCodeForBBlist();
public:
-#ifndef LEGACY_BACKEND
- // genSpillVar is called by compUpdateLifeVar in the !LEGACY_BACKEND case
void genSpillVar(GenTree* tree);
-#endif // !LEGACY_BACKEND
protected:
-#ifndef LEGACY_BACKEND
void genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize, regNumber callTarget = REG_NA);
-#else
- void genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize);
-#endif
void genGCWriteBarrier(GenTree* tgt, GCInfo::WriteBarrierForm wbf);
@@ -284,10 +257,6 @@ protected:
void genExitCode(BasicBlock* block);
-#ifdef LEGACY_BACKEND
- GenTree* genMakeConst(const void* cnsAddr, var_types cnsType, GenTree* cnsTree, bool dblAlign);
-#endif
-
void genJumpToThrowHlpBlk(emitJumpKind jumpKind, SpecialCodeKind codeKind, GenTree* failBlk = nullptr);
void genCheckOverflow(GenTree* tree);
@@ -417,7 +386,7 @@ protected:
#endif // _TARGET_AMD64_
-#if defined(_TARGET_XARCH_) && !FEATURE_STACK_FP_X87
+#if defined(_TARGET_XARCH_)
// Save/Restore callee saved float regs to stack
void genPreserveCalleeSavedFltRegs(unsigned lclFrameSize);
@@ -425,11 +394,9 @@ protected:
// Generate VZeroupper instruction to avoid AVX/SSE transition penalty
void genVzeroupperIfNeeded(bool check256bitOnly = true);
-#endif // _TARGET_XARCH_ && FEATURE_STACK_FP_X87
+#endif // _TARGET_XARCH_
-#if !FEATURE_STACK_FP_X87
void genZeroInitFltRegs(const regMaskTP& initFltRegs, const regMaskTP& initDblRegs, const regNumber& initReg);
-#endif // !FEATURE_STACK_FP_X87
regNumber genGetZeroReg(regNumber initReg, bool* pInitRegZeroed);
@@ -796,11 +763,7 @@ protected:
unsigned genTrnslLocalVarCount;
#endif
-#ifndef LEGACY_BACKEND
#include "codegenlinear.h"
-#else // LEGACY_BACKEND
-#include "codegenclassic.h"
-#endif // LEGACY_BACKEND
/*
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
@@ -819,10 +782,6 @@ protected:
public:
void instInit();
-#ifdef LEGACY_BACKEND
- regNumber genGetZeroRegister();
-#endif
-
void instGen(instruction ins);
#ifdef _TARGET_XARCH_
void instNop(unsigned size);
diff --git a/src/jit/codegenarm.cpp b/src/jit/codegenarm.cpp
index c405e33110..d361c3d15a 100644
--- a/src/jit/codegenarm.cpp
+++ b/src/jit/codegenarm.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_ARM_
#include "codegen.h"
#include "lower.h"
@@ -1702,5 +1700,3 @@ void CodeGen::genCodeForMulLong(GenTreeMultiRegOp* node)
}
#endif // _TARGET_ARM_
-
-#endif // !LEGACY_BACKEND
diff --git a/src/jit/codegenarm64.cpp b/src/jit/codegenarm64.cpp
index ce3ce949e6..eaaf5ba1a8 100644
--- a/src/jit/codegenarm64.cpp
+++ b/src/jit/codegenarm64.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_ARM64_
#include "emit.h"
#include "codegen.h"
@@ -8415,5 +8413,3 @@ void CodeGen::genArm64EmitterUnitTests()
#endif // defined(DEBUG)
#endif // _TARGET_ARM64_
-
-#endif // !LEGACY_BACKEND
diff --git a/src/jit/codegenarmarch.cpp b/src/jit/codegenarmarch.cpp
index c383f09e10..5a2b55525d 100644
--- a/src/jit/codegenarmarch.cpp
+++ b/src/jit/codegenarmarch.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_ARMARCH_ // This file is ONLY used for ARM and ARM64 architectures
#include "codegen.h"
@@ -3897,5 +3895,3 @@ void CodeGen::genStructReturn(GenTree* treeNode)
} // op1 must be multi-reg GT_CALL
}
#endif // _TARGET_ARMARCH_
-
-#endif // !LEGACY_BACKEND
diff --git a/src/jit/codegenclassic.h b/src/jit/codegenclassic.h
deleted file mode 100644
index 7785a25dba..0000000000
--- a/src/jit/codegenclassic.h
+++ /dev/null
@@ -1,598 +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.
-
-//
-// This file contains the members of CodeGen that are defined and used
-// only by the "classic" JIT backend. It is included by CodeGen.h in the
-// definition of the CodeGen class.
-//
-
-#ifndef _CODEGENCLASSIC_H_
-#define _CODEGENCLASSIC_H_
-
-#ifdef LEGACY_BACKEND // Not necessary (it's this way in the #include location), but helpful to IntelliSense
-
-public:
-regNumber genIsEnregisteredIntVariable(GenTree* tree);
-
-void sched_AM(instruction ins,
- emitAttr size,
- regNumber ireg,
- bool rdst,
- GenTree* tree,
- unsigned offs,
- bool cons = false,
- int cval = 0,
- insFlags flags = INS_FLAGS_DONT_CARE);
-
-protected:
-#if FEATURE_STACK_FP_X87
-VARSET_TP genFPregVars; // mask corresponding to genFPregCnt
-unsigned genFPdeadRegCnt; // The dead unpopped part of genFPregCnt
-#endif // FEATURE_STACK_FP_X87
-
-//-------------------------------------------------------------------------
-
-void genSetRegToIcon(regNumber reg, ssize_t val, var_types type = TYP_INT, insFlags flags = INS_FLAGS_DONT_CARE);
-
-regNumber genGetRegSetToIcon(ssize_t val, regMaskTP regBest = 0, var_types type = TYP_INT);
-void genDecRegBy(regNumber reg, ssize_t ival, GenTree* tree);
-void genIncRegBy(regNumber reg, ssize_t ival, GenTree* tree, var_types dstType = TYP_INT, bool ovfl = false);
-
-void genMulRegBy(regNumber reg, ssize_t ival, GenTree* tree, var_types dstType = TYP_INT, bool ovfl = false);
-
-//-------------------------------------------------------------------------
-
-bool genRegTrashable(regNumber reg, GenTree* tree);
-
-//
-// Prolog functions and data (there are a few exceptions for more generally used things)
-//
-
-regMaskTP genPInvokeMethodProlog(regMaskTP initRegs);
-
-void genPInvokeMethodEpilog();
-
-regNumber genPInvokeCallProlog(LclVarDsc* varDsc,
- int argSize,
- CORINFO_METHOD_HANDLE methodToken,
- BasicBlock* returnLabel);
-
-void genPInvokeCallEpilog(LclVarDsc* varDsc, regMaskTP retVal);
-
-regNumber genLclHeap(GenTree* size);
-
-void genDyingVars(VARSET_VALARG_TP beforeSet, VARSET_VALARG_TP afterSet);
-
-bool genContainsVarDeath(GenTree* from, GenTree* to, unsigned varNum);
-
-void genComputeReg(
- GenTree* tree, regMaskTP needReg, RegSet::ExactReg mustReg, RegSet::KeepReg keepReg, bool freeOnly = false);
-
-void genCompIntoFreeReg(GenTree* tree, regMaskTP needReg, RegSet::KeepReg keepReg);
-
-void genReleaseReg(GenTree* tree);
-
-void genRecoverReg(GenTree* tree, regMaskTP needReg, RegSet::KeepReg keepReg);
-
-void genMoveRegPairHalf(GenTree* tree, regNumber dst, regNumber src, int off = 0);
-
-void genMoveRegPair(GenTree* tree, regMaskTP needReg, regPairNo newPair);
-
-void genComputeRegPair(
- GenTree* tree, regPairNo needRegPair, regMaskTP avoidReg, RegSet::KeepReg keepReg, bool freeOnly = false);
-
-void genCompIntoFreeRegPair(GenTree* tree, regMaskTP avoidReg, RegSet::KeepReg keepReg);
-
-void genComputeAddressable(GenTree* tree,
- regMaskTP addrReg,
- RegSet::KeepReg keptReg,
- regMaskTP needReg,
- RegSet::KeepReg keepReg,
- bool freeOnly = false);
-
-void genReleaseRegPair(GenTree* tree);
-
-void genRecoverRegPair(GenTree* tree, regPairNo regPair, RegSet::KeepReg keepReg);
-
-void genEvalIntoFreeRegPair(GenTree* tree, regPairNo regPair, regMaskTP avoidReg);
-
-void genMakeRegPairAvailable(regPairNo regPair);
-
-bool genMakeIndAddrMode(GenTree* addr,
- GenTree* oper,
- bool forLea,
- regMaskTP regMask,
- RegSet::KeepReg keepReg,
- regMaskTP* useMaskPtr,
- bool deferOp = false);
-
-regMaskTP genMakeRvalueAddressable(
- GenTree* tree, regMaskTP needReg, RegSet::KeepReg keepReg, bool forLoadStore, bool smallOK = false);
-
-regMaskTP genMakeAddressable(
- GenTree* tree, regMaskTP needReg, RegSet::KeepReg keepReg, bool smallOK = false, bool deferOK = false);
-
-regMaskTP genMakeAddrArrElem(GenTree* arrElem, GenTree* tree, regMaskTP needReg, RegSet::KeepReg keepReg);
-
-regMaskTP genMakeAddressable2(GenTree* tree,
- regMaskTP needReg,
- RegSet::KeepReg keepReg,
- bool forLoadStore,
- bool smallOK = false,
- bool deferOK = false,
- bool evalSideEffs = false);
-
-bool genStillAddressable(GenTree* tree);
-
-regMaskTP genRestoreAddrMode(GenTree* addr, GenTree* tree, bool lockPhase);
-
-regMaskTP genRestAddressable(GenTree* tree, regMaskTP addrReg, regMaskTP lockMask);
-
-regMaskTP genKeepAddressable(GenTree* tree, regMaskTP addrReg, regMaskTP avoidMask = RBM_NONE);
-
-void genDoneAddressable(GenTree* tree, regMaskTP addrReg, RegSet::KeepReg keptReg);
-
-GenTree* genMakeAddrOrFPstk(GenTree* tree, regMaskTP* regMaskPtr, bool roundResult);
-
-void genEmitGSCookieCheck(bool pushReg);
-
-void genEvalSideEffects(GenTree* tree);
-
-void genCondJump(GenTree* cond, BasicBlock* destTrue = NULL, BasicBlock* destFalse = NULL, bool bStackFPFixup = true);
-
-emitJumpKind genCondSetFlags(GenTree* cond);
-
-void genJCC(genTreeOps cmp, BasicBlock* block, var_types type);
-
-void genJccLongHi(genTreeOps cmp, BasicBlock* jumpTrue, BasicBlock* jumpFalse, bool unsOper = false);
-
-void genJccLongLo(genTreeOps cmp, BasicBlock* jumpTrue, BasicBlock* jumpFalse);
-
-void genCondJumpLng(GenTree* cond, BasicBlock* jumpTrue, BasicBlock* jumpFalse, bool bFPTransition = false);
-
-bool genUse_fcomip();
-
-void genTableSwitch(regNumber reg, unsigned jumpCnt, BasicBlock** jumpTab);
-
-regMaskTP WriteBarrier(GenTree* tgt, GenTree* assignVal, regMaskTP addrReg);
-
-void genCodeForTreeConst(GenTree* tree, regMaskTP destReg, regMaskTP bestReg = RBM_NONE);
-
-void genCodeForTreeLeaf(GenTree* tree, regMaskTP destReg, regMaskTP bestReg = RBM_NONE);
-
-// If "tree" is a comma node, generates code for the left comma arguments,
-// in order, returning the first right argument in the list that is not
-// a comma node.
-GenTree* genCodeForCommaTree(GenTree* tree);
-
-void genCodeForTreeLeaf_GT_JMP(GenTree* tree);
-
-static Compiler::fgWalkPreFn fgIsVarAssignedTo;
-
-void genCodeForQmark(GenTree* tree, regMaskTP destReg, regMaskTP bestReg);
-
-bool genCodeForQmarkWithCMOV(GenTree* tree, regMaskTP destReg, regMaskTP bestReg);
-
-#ifdef _TARGET_XARCH_
-void genCodeForMultEAX(GenTree* tree);
-#endif
-#ifdef _TARGET_ARM_
-void genCodeForMult64(GenTree* tree, regMaskTP destReg, regMaskTP bestReg);
-#endif
-
-void genCodeForTreeSmpBinArithLogOp(GenTree* tree, regMaskTP destReg, regMaskTP bestReg);
-
-void genCodeForTreeSmpBinArithLogAsgOp(GenTree* tree, regMaskTP destReg, regMaskTP bestReg);
-
-void genCodeForUnsignedMod(GenTree* tree, regMaskTP destReg, regMaskTP bestReg);
-
-void genCodeForSignedMod(GenTree* tree, regMaskTP destReg, regMaskTP bestReg);
-
-void genCodeForUnsignedDiv(GenTree* tree, regMaskTP destReg, regMaskTP bestReg);
-
-void genCodeForSignedDiv(GenTree* tree, regMaskTP destReg, regMaskTP bestReg);
-
-void genCodeForGeneralDivide(GenTree* tree, regMaskTP destReg, regMaskTP bestReg);
-
-void genCodeForAsgShift(GenTree* tree, regMaskTP destReg, regMaskTP bestReg);
-
-void genCodeForShift(GenTree* tree, regMaskTP destReg, regMaskTP bestReg);
-
-void genCodeForRelop(GenTree* tree, regMaskTP destReg, regMaskTP bestReg);
-
-void genCodeForCopyObj(GenTree* tree, regMaskTP destReg);
-
-void genCodeForBlkOp(GenTree* tree, regMaskTP destReg);
-
-void genCodeForTreeSmpOp(GenTree* tree, regMaskTP destReg, regMaskTP bestReg = RBM_NONE);
-
-regNumber genIntegerCast(GenTree* tree, regMaskTP needReg, regMaskTP bestReg);
-
-void genCodeForNumericCast(GenTree* tree, regMaskTP destReg, regMaskTP bestReg);
-
-void genCodeForTreeSmpOp_GT_ADDR(GenTree* tree, regMaskTP destReg, regMaskTP bestReg = RBM_NONE);
-
-void genCodeForTreeSmpOpAsg(GenTree* tree);
-
-void genCodeForTreeSmpOpAsg_DONE_ASSG(GenTree* tree, regMaskTP addrReg, regNumber reg, bool ovfl);
-
-void genCodeForTreeSpecialOp(GenTree* tree, regMaskTP destReg, regMaskTP bestReg = RBM_NONE);
-
-void genCodeForTree(GenTree* tree, regMaskTP destReg, regMaskTP bestReg = RBM_NONE);
-
-void genCodeForTree_DONE_LIFE(GenTree* tree, regNumber reg)
-{
- /* We've computed the value of 'tree' into 'reg' */
-
- assert(reg != 0xFEEFFAAFu);
- assert(!IsUninitialized(reg));
-
- genMarkTreeInReg(tree, reg);
-}
-
-void genCodeForTree_DONE_LIFE(GenTree* tree, regPairNo regPair)
-{
- /* We've computed the value of 'tree' into 'regPair' */
-
- genMarkTreeInRegPair(tree, regPair);
-}
-
-void genCodeForTree_DONE(GenTree* tree, regNumber reg)
-{
- /* Check whether this subtree has freed up any variables */
-
- genUpdateLife(tree);
-
- genCodeForTree_DONE_LIFE(tree, reg);
-}
-
-void genCodeForTree_REG_VAR1(GenTree* tree)
-{
- /* Value is already in a register */
-
- regNumber reg = tree->gtRegNum;
-
- gcInfo.gcMarkRegPtrVal(reg, tree->TypeGet());
-
- genCodeForTree_DONE(tree, reg);
-}
-
-void genCodeForTreeLng(GenTree* tree, regMaskTP needReg, regMaskTP avoidReg);
-
-regPairNo genCodeForLongModInt(GenTree* tree, regMaskTP needReg);
-
-unsigned genRegCountForLiveIntEnregVars(GenTree* tree);
-
-#ifdef _TARGET_ARM_
-void genStoreFromFltRetRegs(GenTree* tree);
-void genLoadIntoFltRetRegs(GenTree* tree);
-void genLdStFltRetRegsPromotedVar(LclVarDsc* varDsc, bool isLoadIntoFltReg);
-#endif
-
-#if CPU_HAS_FP_SUPPORT
-void genRoundFpExpression(GenTree* op, var_types type = TYP_UNDEF);
-void genCodeForTreeFlt(GenTree* tree, regMaskTP needReg = RBM_ALLFLOAT, regMaskTP bestReg = RBM_NONE);
-#endif
-
-// FP stuff
-#include "fp.h"
-
-void genCodeForJumpTable(GenTree* tree);
-void genCodeForSwitchTable(GenTree* tree);
-void genCodeForSwitch(GenTree* tree);
-
-size_t genPushArgList(GenTreeCall* call);
-
-#ifdef _TARGET_ARM_
-// We are generating code for a promoted struct local variable. Fill the next slot (register or
-// 4-byte stack slot) with one or more field variables of the promoted struct local -- or 2 such slots
-// if the next field is a 64-bit value.
-// The arguments are:
-// "arg" is the current argument node.
-//
-// "curArgTabEntry" arg table entry pointer for "arg".
-//
-// "promotedStructLocalVarDesc" describes the struct local being copied, assumed non-NULL.
-//
-// "fieldSize" is somewhat misnamed; it must be the element in the struct's GC layout describing the next slot
-// of the struct -- it will be EA_4BYTE, EA_GCREF, or EA_BYREF.
-//
-// "*pNextPromotedStructFieldVar" must be the the local variable number of the next field variable to copy;
-// this location will be updated by the call to reflect the bytes that are copied.
-//
-// "*pBytesOfNextSlotOfCurPromotedStruct" must be the number of bytes within the struct local at which the next
-// slot to be copied starts. This location will be updated by the call to reflect the bytes that are copied.
-//
-// "*pCurRegNum" must be the current argument register number, and will be updated if argument registers are filled.
-//
-// "argOffset" must be the offset of the next slot to be filled in the outgoing argument area, if the argument is to
-// be
-// put in the outgoing arg area of the stack (or else should be INT_MAX if the next slot to be filled is a
-// register).
-// (Strictly speaking, after the addition of "argOffsetOfFirstStackSlot", this arg is redundant, and is only used
-// in assertions, and could be removed.)
-//
-// "fieldOffsetOfFirstStackSlot" must be the offset within the promoted struct local of the first slot that should be
-// copied to the outgoing argument area -- non-zero only in the case of a struct that spans registers and stack
-// slots.
-//
-// "argOffsetOfFirstStackSlot" must be the 4-byte-aligned offset of the first offset in the outgoing argument area
-// which could
-// contain part of the struct. (Explicit alignment may mean it doesn't actually contain part of the struct.)
-//
-// "*deadFieldVarRegs" is an out parameter, the set of registers containing promoted field variables that become dead
-// after
-// this (implicit) use.
-//
-// "*pRegTmp" -- if a temporary register is needed, and this is not REG_STK, uses that register. Otherwise, if it is
-// REG_STK,
-// allocates a register, uses it, and sets "*pRegTmp" to the allocated register.
-//
-// Returns "true" iff it filled two slots with an 8-byte value.
-bool genFillSlotFromPromotedStruct(GenTree* arg,
- fgArgTabEntry* curArgTabEntry,
- LclVarDsc* promotedStructLocalVarDesc,
- emitAttr fieldSize,
- unsigned* pNextPromotedStructFieldVar, // IN/OUT
- unsigned* pBytesOfNextSlotOfCurPromotedStruct, // IN/OUT
- regNumber* pCurRegNum, // IN/OUT
- int argOffset,
- int fieldOffsetOfFirstStackSlot,
- int argOffsetOfFirstStackSlot,
- regMaskTP* deadFieldVarRegs, // OUT
- regNumber* pRegTmp); // IN/OUT
-
-#endif // _TARGET_ARM_
-// Requires that "curr" is a cpblk. If the RHS is a promoted struct local,
-// then returns a regMaskTP representing the set of registers holding
-// fieldVars of the RHS that go dead with this use (as determined by the live set
-// of cpBlk).
-regMaskTP genFindDeadFieldRegs(GenTree* cpBlk);
-
-void SetupLateArgs(GenTreeCall* call);
-
-#ifdef _TARGET_ARM_
-void PushMkRefAnyArg(GenTree* mkRefAnyTree, fgArgTabEntry* curArgTabEntry, regMaskTP regNeedMask);
-#endif // _TARGET_ARM_
-
-regMaskTP genLoadIndirectCallTarget(GenTreeCall* call);
-
-regMaskTP genCodeForCall(GenTreeCall* call, bool valUsed);
-
-GenTree* genGetAddrModeBase(GenTree* tree);
-
-GenTree* genIsAddrMode(GenTree* tree, GenTree** indxPtr);
-
-private:
-bool genIsLocalLastUse(GenTree* tree);
-
-bool genIsRegCandidateLocal(GenTree* tree);
-
-//=========================================================================
-// Debugging support
-//=========================================================================
-
-#if FEATURE_STACK_FP_X87
-/*
-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-XX XX
-XX Flat FP model XX
-XX XX
-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-*/
-
-bool StackFPIsSameAsFloat(double d);
-bool FlatFPSameRegisters(FlatFPStateX87* pState, regMaskTP mask);
-
-// FlatFPStateX87_ functions are the actual verbs to do stuff
-// like doing a transition, loading register, etc. It's also
-// responsible for emitting the x87 code to do so. We keep
-// them in Compiler because we don't want to store a pointer to the
-// emitter.
-void FlatFPX87_Kill(FlatFPStateX87* pState, unsigned iVirtual);
-void FlatFPX87_PushVirtual(FlatFPStateX87* pState, unsigned iRegister, bool bEmitCode = true);
-unsigned FlatFPX87_Pop(FlatFPStateX87* pState, bool bEmitCode = true);
-unsigned FlatFPX87_Top(FlatFPStateX87* pState, bool bEmitCode = true);
-void FlatFPX87_Unload(FlatFPStateX87* pState, unsigned iVirtual, bool bEmitCode = true);
-#endif
-
-// Codegen functions. This is the API that codegen will use
-regMaskTP genPushArgumentStackFP(GenTree* arg);
-void genRoundFpExpressionStackFP(GenTree* op, var_types type = TYP_UNDEF);
-void genCodeForTreeStackFP_Const(GenTree* tree);
-void genCodeForTreeStackFP_Leaf(GenTree* tree);
-void genCodeForTreeStackFP_SmpOp(GenTree* tree);
-void genCodeForTreeStackFP_Special(GenTree* tree);
-void genCodeForTreeStackFP_Cast(GenTree* tree);
-void genCodeForTreeStackFP(GenTree* tree);
-void genCondJumpFltStackFP(GenTree* cond, BasicBlock* jumpTrue, BasicBlock* jumpFalse, bool bDoTransition = true);
-void genCondJumpFloat(GenTree* cond, BasicBlock* jumpTrue, BasicBlock* jumpFalse);
-void genCondJumpLngStackFP(GenTree* cond, BasicBlock* jumpTrue, BasicBlock* jumpFalse);
-
-void genFloatConst(GenTree* tree, RegSet::RegisterPreference* pref);
-void genFloatLeaf(GenTree* tree, RegSet::RegisterPreference* pref);
-void genFloatSimple(GenTree* tree, RegSet::RegisterPreference* pref);
-void genFloatMath(GenTree* tree, RegSet::RegisterPreference* pref);
-void genFloatCheckFinite(GenTree* tree, RegSet::RegisterPreference* pref);
-void genLoadFloat(GenTree* tree, regNumber reg);
-void genFloatAssign(GenTree* tree);
-void genFloatArith(GenTree* tree, RegSet::RegisterPreference* pref);
-void genFloatAsgArith(GenTree* tree);
-
-regNumber genAssignArithFloat(genTreeOps oper, GenTree* dst, regNumber dstreg, GenTree* src, regNumber srcreg);
-
-GenTree* genMakeAddressableFloat(GenTree* tree,
- regMaskTP* regMaskIntPtr,
- regMaskTP* regMaskFltPtr,
- bool bCollapseConstantDoubles = true);
-
-void genCodeForTreeFloat(GenTree* tree, RegSet::RegisterPreference* pref = NULL);
-
-void genCodeForTreeFloat(GenTree* tree, regMaskTP needReg, regMaskTP bestReg);
-
-regNumber genArithmFloat(
- genTreeOps oper, GenTree* dst, regNumber dstreg, GenTree* src, regNumber srcreg, bool bReverse);
-void genCodeForTreeCastFloat(GenTree* tree, RegSet::RegisterPreference* pref);
-void genCodeForTreeCastToFloat(GenTree* tree, RegSet::RegisterPreference* pref);
-void genCodeForTreeCastFromFloat(GenTree* tree, RegSet::RegisterPreference* pref);
-void genKeepAddressableFloat(GenTree* tree, regMaskTP* regMaskIntPtr, regMaskTP* regMaskFltPtr);
-void genDoneAddressableFloat(GenTree* tree, regMaskTP addrRegInt, regMaskTP addrRegFlt, RegSet::KeepReg keptReg);
-void genComputeAddressableFloat(GenTree* tree,
- regMaskTP addrRegInt,
- regMaskTP addrRegFlt,
- RegSet::KeepReg keptReg,
- regMaskTP needReg,
- RegSet::KeepReg keepReg,
- bool freeOnly = false);
-void genRoundFloatExpression(GenTree* op, var_types type);
-
-#if FEATURE_STACK_FP_X87
-// Assumes then block will be generated before else block.
-struct QmarkStateStackFP
-{
- FlatFPStateX87 stackState;
-};
-
-void genQMarkRegVarTransition(GenTree* nextNode, VARSET_VALARG_TP liveset);
-void genQMarkBeforeElseStackFP(QmarkStateStackFP* pState, VARSET_VALARG_TP varsetCond, GenTree* nextNode);
-void genQMarkAfterElseBlockStackFP(QmarkStateStackFP* pState, VARSET_VALARG_TP varsetCond, GenTree* nextNode);
-void genQMarkAfterThenBlockStackFP(QmarkStateStackFP* pState);
-
-#endif
-
-GenTree* genMakeAddressableStackFP(GenTree* tree,
- regMaskTP* regMaskIntPtr,
- regMaskTP* regMaskFltPtr,
- bool bCollapseConstantDoubles = true);
-void genKeepAddressableStackFP(GenTree* tree, regMaskTP* regMaskIntPtr, regMaskTP* regMaskFltPtr);
-void genDoneAddressableStackFP(GenTree* tree, regMaskTP addrRegInt, regMaskTP addrRegFlt, RegSet::KeepReg keptReg);
-
-void genCodeForTreeStackFP_Asg(GenTree* tree);
-void genCodeForTreeStackFP_AsgArithm(GenTree* tree);
-void genCodeForTreeStackFP_Arithm(GenTree* tree);
-void genCodeForTreeStackFP_DONE(GenTree* tree, regNumber reg);
-void genCodeForTreeFloat_DONE(GenTree* tree, regNumber reg);
-
-void genSetupStateStackFP(BasicBlock* block);
-regMaskTP genRegMaskFromLivenessStackFP(VARSET_VALARG_TP varset);
-
-// bReverse means make op1 addressable and codegen for op2.
-// If op1 or op2 are comma expressions, will do code-gen for their non-last comma parts,
-// and set op1 and op2 to the remaining non-comma expressions.
-void genSetupForOpStackFP(
- GenTree*& op1, GenTree*& op2, bool bReverse, bool bMakeOp1Addressable, bool bOp1ReadOnly, bool bOp2ReadOnly);
-
-#if FEATURE_STACK_FP_X87
-
-#ifdef DEBUG
-bool ConsistentAfterStatementStackFP();
-#endif
-
-private:
-void SpillTempsStackFP(regMaskTP canSpillMask);
-void SpillForCallStackFP();
-void UnspillRegVarsStackFp();
-
-// Transition API. Takes care of the stack matching of basicblock boundaries
-void genCodeForPrologStackFP();
-void genCodeForEndBlockTransitionStackFP(BasicBlock* block);
-
-void genCodeForBBTransitionStackFP(BasicBlock* pDst);
-void genCodeForTransitionStackFP(FlatFPStateX87* pSrc, FlatFPStateX87* pDst);
-void genCodeForTransitionFromMask(FlatFPStateX87* pSrc, regMaskTP mask, bool bEmitCode = true);
-BasicBlock* genTransitionBlockStackFP(FlatFPStateX87* pState, BasicBlock* pFrom, BasicBlock* pTarget);
-
-// This is the API codegen will use to emit virtual fp code. In theory, nobody above this API
-// should know about x87 instructions.
-
-int genNumberTemps();
-void genDiscardStackFP(GenTree* tree);
-void genRegRenameWithMasks(regNumber dstReg, regNumber srcReg);
-void genRegVarBirthStackFP(GenTree* tree);
-void genRegVarBirthStackFP(LclVarDsc* varDsc);
-void genRegVarDeathStackFP(GenTree* tree);
-void genRegVarDeathStackFP(LclVarDsc* varDsc);
-void genLoadStackFP(GenTree* tree, regNumber reg);
-void genMovStackFP(GenTree* dst, regNumber dstreg, GenTree* src, regNumber srcreg);
-bool genCompInsStackFP(GenTree* tos, GenTree* other);
-regNumber genArithmStackFP(
- genTreeOps oper, GenTree* dst, regNumber dstreg, GenTree* src, regNumber srcreg, bool bReverse);
-regNumber genAsgArithmStackFP(genTreeOps oper, GenTree* dst, regNumber dstreg, GenTree* src, regNumber srcreg);
-void genCondJmpInsStackFP(emitJumpKind jumpKind,
- BasicBlock* jumpTrue,
- BasicBlock* jumpFalse,
- bool bDoTransition = true);
-void genTableSwitchStackFP(regNumber reg, unsigned jumpCnt, BasicBlock** jumpTab);
-
-void JitDumpFPState();
-#else // !FEATURE_STACK_FP_X87
-void SpillForCallRegisterFP(regMaskTP noSpillMask);
-#endif // !FEATURE_STACK_FP_X87
-
-// When bOnlyNoMemAccess = true, the load will be generated only for constant loading that doesn't
-// involve memory accesses, (ie: fldz for positive zero, or fld1 for 1). Will return true the function
-// did the load
-bool genConstantLoadStackFP(GenTree* tree, bool bOnlyNoMemAccess = false);
-void genEndOfStatement();
-
-#if FEATURE_STACK_FP_X87
-struct genRegVarDiesInSubTreeData
-{
- regNumber reg;
- bool result;
-};
-static Compiler::fgWalkPreFn genRegVarDiesInSubTreeWorker;
-bool genRegVarDiesInSubTree(GenTree* tree, regNumber reg);
-#endif // FEATURE_STACK_FP_X87
-
-// Float spill
-void UnspillFloat(RegSet::SpillDsc* spillDsc);
-void UnspillFloat(GenTree* tree);
-void UnspillFloat(LclVarDsc* varDsc);
-void UnspillFloatMachineDep(RegSet::SpillDsc* spillDsc);
-void UnspillFloatMachineDep(RegSet::SpillDsc* spillDsc, bool useSameReg);
-void RemoveSpillDsc(RegSet::SpillDsc* spillDsc);
-
-protected:
-struct genLivenessSet
-{
- VARSET_TP liveSet;
- VARSET_TP varPtrSet;
- regMaskSmall maskVars;
- regMaskSmall gcRefRegs;
- regMaskSmall byRefRegs;
-
- genLivenessSet() : liveSet(VarSetOps::UninitVal()), varPtrSet(VarSetOps::UninitVal())
- {
- }
-};
-
-void saveLiveness(genLivenessSet* ls);
-void restoreLiveness(genLivenessSet* ls);
-void checkLiveness(genLivenessSet* ls);
-void unspillLiveness(genLivenessSet* ls);
-
-//-------------------------------------------------------------------------
-//
-// If we know that the flags register is set to a value that corresponds
-// to the current value of a register or variable, the following values
-// record that information.
-//
-
-emitLocation genFlagsEqLoc;
-regNumber genFlagsEqReg;
-unsigned genFlagsEqVar;
-
-void genFlagsEqualToNone();
-void genFlagsEqualToReg(GenTree* tree, regNumber reg);
-void genFlagsEqualToVar(GenTree* tree, unsigned var);
-bool genFlagsAreReg(regNumber reg);
-bool genFlagsAreVar(unsigned var);
-
-#endif // LEGACY_BACKEND
-
-#endif // _CODEGENCLASSIC_H_
diff --git a/src/jit/codegencommon.cpp b/src/jit/codegencommon.cpp
index bab4f07d60..62eaf7f5cc 100644
--- a/src/jit/codegencommon.cpp
+++ b/src/jit/codegencommon.cpp
@@ -95,13 +95,13 @@ CodeGenInterface::CodeGenInterface(Compiler* theCompiler)
CodeGen::CodeGen(Compiler* theCompiler) : CodeGenInterface(theCompiler)
{
-#if defined(_TARGET_XARCH_) && !FEATURE_STACK_FP_X87
+#if defined(_TARGET_XARCH_)
negBitmaskFlt = nullptr;
negBitmaskDbl = nullptr;
absBitmaskFlt = nullptr;
absBitmaskDbl = nullptr;
u8ToDblBitmask = nullptr;
-#endif // defined(_TARGET_XARCH_) && !FEATURE_STACK_FP_X87
+#endif // defined(_TARGET_XARCH_)
#if defined(FEATURE_PUT_STRUCT_ARG_STK) && !defined(_TARGET_X86_)
m_stkArgVarNum = BAD_VAR_NUM;
@@ -124,25 +124,8 @@ CodeGen::CodeGen(Compiler* theCompiler) : CodeGenInterface(theCompiler)
compiler->tmpInit();
-#ifdef DEBUG
-#if defined(_TARGET_X86_) && defined(LEGACY_BACKEND)
- // This appears to be x86-specific. It's attempting to make sure all offsets to temps
- // are large. For ARM, this doesn't interact well with our decision about whether to use
- // R10 or not as a reserved register.
- if (regSet.rsStressRegs())
- compiler->tmpIntSpillMax = (SCHAR_MAX / sizeof(int));
-#endif // defined(_TARGET_X86_) && defined(LEGACY_BACKEND)
-#endif // DEBUG
-
instInit();
-#ifdef LEGACY_BACKEND
- // TODO-Cleanup: These used to be set in rsInit() - should they be moved to RegSet??
- // They are also accessed by the register allocators and fgMorphLclVar().
- intRegState.rsCurRegArgNum = 0;
- floatRegState.rsCurRegArgNum = 0;
-#endif // LEGACY_BACKEND
-
#ifdef LATE_DISASM
getDisAssembler().disInit(compiler);
#endif
@@ -154,10 +137,10 @@ CodeGen::CodeGen(Compiler* theCompiler) : CodeGenInterface(theCompiler)
// Shouldn't be used before it is set in genFnProlog()
compiler->compCalleeRegsPushed = UninitializedWord<unsigned>(compiler);
-#if defined(_TARGET_XARCH_) && !FEATURE_STACK_FP_X87
+#if defined(_TARGET_XARCH_)
// Shouldn't be used before it is set in genFnProlog()
compiler->compCalleeFPRegsSavedMask = (regMaskTP)-1;
-#endif // defined(_TARGET_XARCH_) && !FEATURE_STACK_FP_X87
+#endif // defined(_TARGET_XARCH_)
#endif // DEBUG
#ifdef _TARGET_AMD64_
@@ -168,10 +151,6 @@ CodeGen::CodeGen(Compiler* theCompiler) : CodeGenInterface(theCompiler)
compiler->compQuirkForPPPflag = false;
#endif // _TARGET_AMD64_
-#ifdef LEGACY_BACKEND
- genFlagsEqualToNone();
-#endif // LEGACY_BACKEND
-
// Initialize the IP-mapping logic.
compiler->genIPmappingList = nullptr;
compiler->genIPmappingLast = nullptr;
@@ -192,21 +171,8 @@ CodeGen::CodeGen(Compiler* theCompiler) : CodeGenInterface(theCompiler)
void CodeGenInterface::genMarkTreeInReg(GenTree* tree, regNumber reg)
{
tree->gtRegNum = reg;
-#ifdef LEGACY_BACKEND
- tree->SetInReg();
-#endif // LEGACY_BACKEND
}
-#if CPU_LONG_USES_REGPAIR
-void CodeGenInterface::genMarkTreeInRegPair(GenTree* tree, regPairNo regPair)
-{
- tree->gtRegPair = regPair;
-#ifdef LEGACY_BACKEND
- tree->SetInReg();
-#endif // LEGACY_BACKEND
-}
-#endif
-
#if defined(_TARGET_X86_) || defined(_TARGET_ARM_)
//---------------------------------------------------------------------
@@ -349,32 +315,17 @@ void CodeGen::genPrepForCompiler()
VarSetOps::AssignNoCopy(compiler, gcInfo.gcTrkStkPtrLcls, VarSetOps::MakeEmpty(compiler));
- // Figure out which variables live in registers.
// Also, initialize gcTrkStkPtrLcls to include all tracked variables that do not fully live
// in a register (i.e. they live on the stack for all or part of their lifetime).
// Note that lvRegister indicates that a lclVar is in a register for its entire lifetime.
- VarSetOps::AssignNoCopy(compiler, compiler->raRegVarsMask, VarSetOps::MakeEmpty(compiler));
-
unsigned varNum;
LclVarDsc* varDsc;
for (varNum = 0, varDsc = compiler->lvaTable; varNum < compiler->lvaCount; varNum++, varDsc++)
{
- if (varDsc->lvTracked
-#ifndef LEGACY_BACKEND
- || varDsc->lvIsRegCandidate()
-#endif // !LEGACY_BACKEND
- )
+ if (varDsc->lvTracked || varDsc->lvIsRegCandidate())
{
- if (varDsc->lvRegister
-#if FEATURE_STACK_FP_X87
- && !varDsc->IsFloatRegType()
-#endif
- )
- {
- VarSetOps::AddElemD(compiler, compiler->raRegVarsMask, varDsc->lvVarIndex);
- }
- else if (compiler->lvaIsGCTracked(varDsc))
+ if (!varDsc->lvRegister && compiler->lvaIsGCTracked(varDsc))
{
VarSetOps::AddElemD(compiler, gcInfo.gcTrkStkPtrLcls, varDsc->lvVarIndex);
}
@@ -477,45 +428,6 @@ void CodeGenInterface::genUpdateLife(VARSET_VALARG_TP newLife)
compiler->compUpdateLife</*ForCodeGen*/ true>(newLife);
}
-#ifdef LEGACY_BACKEND
-// Returns the liveSet after tree has executed.
-// "tree" MUST occur in the current statement, AFTER the most recent
-// update of compiler->compCurLifeTree and compiler->compCurLife.
-//
-VARSET_VALRET_TP CodeGen::genUpdateLiveSetForward(GenTree* tree)
-{
- VARSET_TP startLiveSet(VarSetOps::MakeCopy(compiler, compiler->compCurLife));
- GenTree* startNode;
- assert(tree != compiler->compCurLifeTree);
- if (compiler->compCurLifeTree == nullptr)
- {
- assert(compiler->compCurStmt != nullptr);
- startNode = compiler->compCurStmt->gtStmt.gtStmtList;
- }
- else
- {
- startNode = compiler->compCurLifeTree->gtNext;
- }
- return compiler->fgUpdateLiveSet(startLiveSet, startNode, tree);
-}
-
-// Determine the registers that are live after "second" has been evaluated,
-// but which are not live after "first".
-// PRECONDITIONS:
-// 1. "first" must occur after compiler->compCurLifeTree in execution order for the current statement
-// 2. "second" must occur after "first" in the current statement
-//
-regMaskTP CodeGen::genNewLiveRegMask(GenTree* first, GenTree* second)
-{
- // First, compute the liveset after "first"
- VARSET_TP firstLiveSet = genUpdateLiveSetForward(first);
- // Now, update the set forward from "first" to "second"
- VARSET_TP secondLiveSet = compiler->fgUpdateLiveSet(firstLiveSet, first->gtNext, second);
- regMaskTP newLiveMask = genLiveMask(VarSetOps::Diff(compiler, secondLiveSet, firstLiveSet));
- return newLiveMask;
-}
-#endif
-
// Return the register mask for the given register variable
// inline
regMaskTP CodeGenInterface::genGetRegMask(const LclVarDsc* varDsc)
@@ -531,10 +443,6 @@ regMaskTP CodeGenInterface::genGetRegMask(const LclVarDsc* varDsc)
else
{
regMask = genRegMask(varDsc->lvRegNum);
- if (isRegPairType(varDsc->lvType))
- {
- regMask |= genRegMask(varDsc->lvOtherReg);
- }
}
return regMask;
}
@@ -571,12 +479,6 @@ regMaskTP CodeGenInterface::genGetRegMask(GenTree* tree)
// inline
void CodeGenInterface::genUpdateRegLife(const LclVarDsc* varDsc, bool isBorn, bool isDying DEBUGARG(GenTree* tree))
{
-#if FEATURE_STACK_FP_X87
- // The stack fp reg vars are handled elsewhere
- if (varTypeIsFloating(varDsc->TypeGet()))
- return;
-#endif
-
regMaskTP regMask = genGetRegMask(varDsc);
#ifdef DEBUG
@@ -815,16 +717,6 @@ void Compiler::compChangeLife(VARSET_VALARG_TP newLife)
// (deadSet INTERSECTION bornSet) == EMPTY
noway_assert(VarSetOps::IsEmptyIntersection(this, deadSet, bornSet));
-#ifdef LEGACY_BACKEND
- // In the LEGACY_BACKEND case, we only consider variables that are fully enregisterd
- // and there may be none.
- VarSetOps::IntersectionD(this, deadSet, raRegVarsMask);
- VarSetOps::IntersectionD(this, bornSet, raRegVarsMask);
- // And all gcTrkStkPtrLcls that are now live will be on the stack
- VarSetOps::AssignNoCopy(this, codeGen->gcInfo.gcVarPtrSetCur,
- VarSetOps::Intersection(this, newLife, codeGen->gcInfo.gcTrkStkPtrLcls));
-#endif // LEGACY_BACKEND
-
VarSetOps::Assign(this, compCurLife, newLife);
// Handle the dying vars first, then the newly live vars.
@@ -854,16 +746,12 @@ void Compiler::compChangeLife(VARSET_VALARG_TP newLife)
}
codeGen->genUpdateRegLife(varDsc, false /*isBorn*/, true /*isDying*/ DEBUGARG(nullptr));
}
-#ifndef LEGACY_BACKEND
// This isn't in a register, so update the gcVarPtrSetCur.
- // (Note that in the LEGACY_BACKEND case gcVarPtrSetCur is updated above unconditionally
- // for all gcTrkStkPtrLcls in newLife, because none of them ever live in a register.)
else if (isGCRef || isByRef)
{
VarSetOps::RemoveElemD(this, codeGen->gcInfo.gcVarPtrSetCur, deadVarIndex);
JITDUMP("\t\t\t\t\t\t\tV%02u becoming dead\n", varNum);
}
-#endif // !LEGACY_BACKEND
}
VarSetOps::Iter bornIter(this, bornSet);
@@ -877,7 +765,6 @@ void Compiler::compChangeLife(VARSET_VALARG_TP newLife)
if (varDsc->lvIsInReg())
{
-#ifndef LEGACY_BACKEND
#ifdef DEBUG
if (VarSetOps::IsMember(this, codeGen->gcInfo.gcVarPtrSetCur, bornVarIndex))
{
@@ -885,7 +772,6 @@ void Compiler::compChangeLife(VARSET_VALARG_TP newLife)
}
#endif // DEBUG
VarSetOps::RemoveElemD(this, codeGen->gcInfo.gcVarPtrSetCur, bornVarIndex);
-#endif // !LEGACY_BACKEND
codeGen->genUpdateRegLife(varDsc, true /*isBorn*/, false /*isDying*/ DEBUGARG(nullptr));
regMaskTP regMask = varDsc->lvRegMask();
if (isGCRef)
@@ -897,14 +783,12 @@ void Compiler::compChangeLife(VARSET_VALARG_TP newLife)
codeGen->gcInfo.gcRegByrefSetCur |= regMask;
}
}
-#ifndef LEGACY_BACKEND
// This isn't in a register, so update the gcVarPtrSetCur
else if (lvaIsGCTracked(varDsc))
{
VarSetOps::AddElemD(this, codeGen->gcInfo.gcVarPtrSetCur, bornVarIndex);
JITDUMP("\t\t\t\t\t\t\tV%02u becoming live\n", varNum);
}
-#endif // !LEGACY_BACKEND
}
codeGen->siUpdate();
@@ -913,189 +797,6 @@ void Compiler::compChangeLife(VARSET_VALARG_TP newLife)
// Need an explicit instantiation.
template void Compiler::compChangeLife<true>(VARSET_VALARG_TP newLife);
-#ifdef LEGACY_BACKEND
-
-/*****************************************************************************
- *
- * Get the mask of integer registers that contain 'live' enregistered
- * local variables after "tree".
- *
- * The output is the mask of integer registers that are currently
- * alive and holding the enregistered local variables.
- */
-regMaskTP CodeGenInterface::genLiveMask(GenTree* tree)
-{
- regMaskTP liveMask = regSet.rsMaskVars;
-
- GenTree* nextNode;
- if (compiler->compCurLifeTree == nullptr)
- {
- assert(compiler->compCurStmt != nullptr);
- nextNode = compiler->compCurStmt->gtStmt.gtStmtList;
- }
- else
- {
- nextNode = compiler->compCurLifeTree->gtNext;
- }
-
- // Theoretically, we should always be able to find "tree" by walking
- // forward in execution order. But unfortunately, there is at least
- // one case (addressing) where a node may be evaluated out of order
- // So, we have to handle that case
- bool outOfOrder = false;
- for (; nextNode != tree->gtNext; nextNode = nextNode->gtNext)
- {
- if (nextNode == nullptr)
- {
- outOfOrder = true;
- break;
- }
- if (nextNode->gtOper == GT_LCL_VAR || nextNode->gtOper == GT_REG_VAR)
- {
- bool isBorn = ((tree->gtFlags & GTF_VAR_DEF) != 0 && (tree->gtFlags & GTF_VAR_USEASG) == 0);
- bool isDying = ((nextNode->gtFlags & GTF_VAR_DEATH) != 0);
- if (isBorn || isDying)
- {
- regMaskTP regMask = genGetRegMask(nextNode);
- if (regMask != RBM_NONE)
- {
- if (isBorn)
- {
- liveMask |= regMask;
- }
- else
- {
- liveMask &= ~(regMask);
- }
- }
- }
- }
- }
- if (outOfOrder)
- {
- assert(compiler->compCurLifeTree != nullptr);
- liveMask = regSet.rsMaskVars;
- // We were unable to find "tree" by traversing forward. We must now go
- // backward from compiler->compCurLifeTree instead. We have to start with compiler->compCurLifeTree,
- // since regSet.rsMaskVars reflects its completed execution
- for (nextNode = compiler->compCurLifeTree; nextNode != tree; nextNode = nextNode->gtPrev)
- {
- assert(nextNode != nullptr);
-
- if (nextNode->gtOper == GT_LCL_VAR || nextNode->gtOper == GT_REG_VAR)
- {
- bool isBorn = ((tree->gtFlags & GTF_VAR_DEF) != 0 && (tree->gtFlags & GTF_VAR_USEASG) == 0);
- bool isDying = ((nextNode->gtFlags & GTF_VAR_DEATH) != 0);
- if (isBorn || isDying)
- {
- regMaskTP regMask = genGetRegMask(nextNode);
- if (regMask != RBM_NONE)
- {
- // We're going backward - so things born are removed
- // and vice versa
- if (isBorn)
- {
- liveMask &= ~(regMask);
- }
- else
- {
- liveMask |= regMask;
- }
- }
- }
- }
- }
- }
- return liveMask;
-}
-
-/*****************************************************************************
- *
- * Get the mask of integer registers that contain 'live' enregistered
- * local variables.
-
- * The input is a liveSet which contains a set of local
- * variables that are currently alive
- *
- * The output is the mask of x86 integer registers that are currently
- * alive and holding the enregistered local variables
- */
-
-regMaskTP CodeGenInterface::genLiveMask(VARSET_VALARG_TP liveSet)
-{
- // Check for the zero LiveSet mask
- if (VarSetOps::IsEmpty(compiler, liveSet))
- {
- return RBM_NONE;
- }
-
- // set if our liveSet matches the one we have cached: genLastLiveSet -> genLastLiveMask
- if (VarSetOps::Equal(compiler, liveSet, genLastLiveSet))
- {
- return genLastLiveMask;
- }
-
- regMaskTP liveMask = 0;
-
- VarSetOps::Iter iter(compiler, liveSet);
- unsigned varIndex = 0;
- while (iter.NextElem(&varIndex))
- {
-
- // If the variable is not enregistered, then it can't contribute to the liveMask
- if (!VarSetOps::IsMember(compiler, compiler->raRegVarsMask, varIndex))
- {
- continue;
- }
-
- // Find the variable in compiler->lvaTable
- unsigned varNum = compiler->lvaTrackedToVarNum[varIndex];
- LclVarDsc* varDsc = compiler->lvaTable + varNum;
-
-#if !FEATURE_FP_REGALLOC
- // If the variable is a floating point type, then it can't contribute to the liveMask
- if (varDsc->IsFloatRegType())
- {
- continue;
- }
-#endif
-
- noway_assert(compiler->lvaTable[varNum].lvRegister);
- regMaskTP regBit;
-
- if (varTypeIsFloating(varDsc->TypeGet()))
- {
- regBit = genRegMaskFloat(varDsc->lvRegNum, varDsc->TypeGet());
- }
- else
- {
- regBit = genRegMask(varDsc->lvRegNum);
-
- // For longs we may have two regs
- if (isRegPairType(varDsc->lvType) && varDsc->lvOtherReg != REG_STK)
- {
- regBit |= genRegMask(varDsc->lvOtherReg);
- }
- }
-
- noway_assert(regBit != 0);
-
- // We should not already have any of these bits set
- noway_assert((liveMask & regBit) == 0);
-
- // Update the liveMask with the register bits that are live
- liveMask |= regBit;
- }
-
- // cache the last mapping between gtLiveSet -> liveMask
- VarSetOps::Assign(compiler, genLastLiveSet, liveSet);
- genLastLiveMask = liveMask;
-
- return liveMask;
-}
-
-#endif
-
/*****************************************************************************
*
* Generate a spill.
@@ -1114,16 +815,6 @@ void CodeGenInterface::reloadReg(var_types type, TempDsc* tmp, regNumber reg)
getEmitter()->emitIns_R_S(ins_Load(type), emitActualTypeSize(type), reg, tmp->tdTempNum(), 0);
}
-#ifdef LEGACY_BACKEND
-#if defined(_TARGET_ARM_) || defined(_TARGET_AMD64_)
-void CodeGenInterface::reloadFloatReg(var_types type, TempDsc* tmp, regNumber reg)
-{
- var_types tmpType = tmp->tdTempType();
- getEmitter()->emitIns_R_S(ins_FloatLoad(type), emitActualTypeSize(tmpType), reg, tmp->tdTempNum(), 0);
-}
-#endif
-#endif // LEGACY_BACKEND
-
// inline
regNumber CodeGenInterface::genGetThisArgReg(GenTreeCall* call) const
{
@@ -1339,13 +1030,6 @@ void CodeGen::genDefineTempLabel(BasicBlock* label)
label->bbEmitCookie =
getEmitter()->emitAddLabel(gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur);
-
-#ifdef LEGACY_BACKEND
- /* gcInfo.gcRegGCrefSetCur does not account for redundant load-suppression
- of GC vars, and the emitter will not know about */
-
- regTracker.rsTrackRegClrPtr();
-#endif
}
/*****************************************************************************
@@ -1595,9 +1279,7 @@ bool CodeGen::genCreateAddrMode(GenTree* addr,
unsigned* cnsPtr,
bool nogen)
{
-#ifndef LEGACY_BACKEND
assert(nogen == true);
-#endif // !LEGACY_BACKEND
/*
The following indirections are valid address modes on x86/x64:
@@ -1699,18 +1381,6 @@ AGAIN:
assert(mul == 0);
#endif
-#ifdef LEGACY_BACKEND
- /* Check both operands as far as being register variables */
-
- if (mode != -1)
- {
- if (op1->gtOper == GT_LCL_VAR)
- genMarkLclVar(op1);
- if (op2->gtOper == GT_LCL_VAR)
- genMarkLclVar(op2);
- }
-#endif // LEGACY_BACKEND
-
/* Special case: keep constants as 'op2' */
if (op1->IsCnsIntOrI())
@@ -1729,31 +1399,7 @@ AGAIN:
cns += op2->gtIntConCommon.IconValue();
-#ifdef LEGACY_BACKEND
- /* Can (and should) we use "add reg, icon" ? */
-
- if (op1->InReg() && mode == 1 && !nogen)
- {
- regNumber reg1 = op1->gtRegNum;
-
- if ((regMask == 0 || (regMask & genRegMask(reg1))) && genRegTrashable(reg1, addr))
- {
- // In case genMarkLclVar(op1) bashed it above and it is
- // the last use of the variable.
-
- genUpdateLife(op1);
-
- /* 'reg1' is trashable, so add "icon" into it */
-
- genIncRegBy(reg1, cns, addr, addr->TypeGet());
-
- genUpdateLife(addr);
- return true;
- }
- }
-#endif // LEGACY_BACKEND
-
-#if defined(_TARGET_ARMARCH_) && !defined(LEGACY_BACKEND)
+#if defined(_TARGET_ARMARCH_)
if (cns == 0)
#endif
{
@@ -1773,7 +1419,7 @@ AGAIN:
goto AGAIN;
-#if SCALED_ADDR_MODES && (!defined(_TARGET_ARMARCH_) || defined(LEGACY_BACKEND))
+#if SCALED_ADDR_MODES && !defined(_TARGET_ARMARCH_)
// TODO-ARM64-CQ, TODO-ARM-CQ: For now we don't try to create a scaled index.
case GT_MUL:
if (op1->gtOverflow())
@@ -1812,220 +1458,104 @@ AGAIN:
}
// op2 is not a constant. So keep on trying.
- CLANG_FORMAT_COMMENT_ANCHOR;
-
-#ifdef LEGACY_BACKEND
- // Does op1 or op2 already sit in a register?
- if (op1->InReg())
- {
- /* op1 is sitting in a register */
- }
- else if (op2->InReg())
- {
- /* op2 is sitting in a register. Keep the enregistered value as op1 */
- tmp = op1;
- op1 = op2;
- op2 = tmp;
+ /* Neither op1 nor op2 are sitting in a register right now */
- noway_assert(rev == false);
- rev = true;
- }
- else
-#endif // LEGACY_BACKEND
+ switch (op1->gtOper)
{
- /* Neither op1 nor op2 are sitting in a register right now */
-
- switch (op1->gtOper)
- {
-#if !defined(_TARGET_ARMARCH_) || defined(LEGACY_BACKEND)
- // TODO-ARM64-CQ, TODO-ARM-CQ: For now we don't try to create a scaled index.
- case GT_ADD:
-
- if (op1->gtOverflow())
- {
- break;
- }
-
- if (op1->gtOp.gtOp2->IsIntCnsFitsInI32() && FitsIn<INT32>(cns + op1->gtOp.gtOp2->gtIntCon.gtIconVal))
- {
- cns += op1->gtOp.gtOp2->gtIntCon.gtIconVal;
- op1 = op1->gtOp.gtOp1;
-
- goto AGAIN;
- }
-
- break;
-
-#if SCALED_ADDR_MODES
-
- case GT_MUL:
-
- if (op1->gtOverflow())
- {
- break;
- }
-
- __fallthrough;
-
- case GT_LSH:
-
- mul = op1->GetScaledIndex();
- if (mul)
- {
- /* 'op1' is a scaled value */
-
- rv1 = op2;
- rv2 = op1->gtOp.gtOp1;
-
- int argScale;
- while ((rv2->gtOper == GT_MUL || rv2->gtOper == GT_LSH) && (argScale = rv2->GetScaledIndex()) != 0)
- {
- if (jitIsScaleIndexMul(argScale * mul))
- {
- mul = mul * argScale;
- rv2 = rv2->gtOp.gtOp1;
- }
- else
- {
- break;
- }
- }
-
- noway_assert(rev == false);
- rev = true;
+#if !defined(_TARGET_ARMARCH_)
+ // TODO-ARM64-CQ, TODO-ARM-CQ: For now we don't try to create a scaled index.
+ case GT_ADD:
- goto FOUND_AM;
- }
+ if (op1->gtOverflow())
+ {
break;
+ }
-#endif // SCALED_ADDR_MODES
-#endif // !_TARGET_ARMARCH || LEGACY_BACKEND
-
- case GT_NOP:
-
- if (!nogen)
- {
- break;
- }
-
+ if (op1->gtOp.gtOp2->IsIntCnsFitsInI32() && FitsIn<INT32>(cns + op1->gtOp.gtOp2->gtIntCon.gtIconVal))
+ {
+ cns += op1->gtOp.gtOp2->gtIntCon.gtIconVal;
op1 = op1->gtOp.gtOp1;
- goto AGAIN;
-
- case GT_COMMA:
-
- if (!nogen)
- {
- break;
- }
- op1 = op1->gtOp.gtOp2;
goto AGAIN;
+ }
- default:
- break;
- }
-
- noway_assert(op2);
- switch (op2->gtOper)
- {
-#if !defined(_TARGET_ARMARCH_) || defined(LEGACY_BACKEND)
- // TODO-ARM64-CQ, TODO-ARM-CQ: For now we don't try to create a scaled index.
- case GT_ADD:
-
- if (op2->gtOverflow())
- {
- break;
- }
+ break;
- if (op2->gtOp.gtOp2->IsIntCnsFitsInI32() && FitsIn<INT32>(cns + op2->gtOp.gtOp2->gtIntCon.gtIconVal))
- {
- cns += op2->gtOp.gtOp2->gtIntCon.gtIconVal;
- op2 = op2->gtOp.gtOp1;
+#if SCALED_ADDR_MODES
- goto AGAIN;
- }
+ case GT_MUL:
+ if (op1->gtOverflow())
+ {
break;
+ }
-#if SCALED_ADDR_MODES
-
- case GT_MUL:
+ __fallthrough;
- if (op2->gtOverflow())
- {
- break;
- }
+ case GT_LSH:
- __fallthrough;
+ mul = op1->GetScaledIndex();
+ if (mul)
+ {
+ /* 'op1' is a scaled value */
- case GT_LSH:
+ rv1 = op2;
+ rv2 = op1->gtOp.gtOp1;
- mul = op2->GetScaledIndex();
- if (mul)
+ int argScale;
+ while ((rv2->gtOper == GT_MUL || rv2->gtOper == GT_LSH) && (argScale = rv2->GetScaledIndex()) != 0)
{
- // 'op2' is a scaled value...is it's argument also scaled?
- int argScale;
- rv2 = op2->gtOp.gtOp1;
- while ((rv2->gtOper == GT_MUL || rv2->gtOper == GT_LSH) && (argScale = rv2->GetScaledIndex()) != 0)
+ if (jitIsScaleIndexMul(argScale * mul))
{
- if (jitIsScaleIndexMul(argScale * mul))
- {
- mul = mul * argScale;
- rv2 = rv2->gtOp.gtOp1;
- }
- else
- {
- break;
- }
+ mul = mul * argScale;
+ rv2 = rv2->gtOp.gtOp1;
+ }
+ else
+ {
+ break;
}
-
- rv1 = op1;
-
- goto FOUND_AM;
}
- break;
-#endif // SCALED_ADDR_MODES
-#endif // !_TARGET_ARMARCH || LEGACY_BACKEND
+ noway_assert(rev == false);
+ rev = true;
- case GT_NOP:
+ goto FOUND_AM;
+ }
+ break;
- if (!nogen)
- {
- break;
- }
+#endif // SCALED_ADDR_MODES
+#endif // !_TARGET_ARMARCH
- op2 = op2->gtOp.gtOp1;
- goto AGAIN;
+ case GT_NOP:
- case GT_COMMA:
+ if (!nogen)
+ {
+ break;
+ }
- if (!nogen)
- {
- break;
- }
+ op1 = op1->gtOp.gtOp1;
+ goto AGAIN;
- op2 = op2->gtOp.gtOp2;
- goto AGAIN;
+ case GT_COMMA:
- default:
+ if (!nogen)
+ {
break;
- }
-
- goto ADD_OP12;
- }
+ }
-#ifdef LEGACY_BACKEND
- // op1 is in a register.
- // Note that this case only occurs during codegen for LEGACY_BACKEND.
+ op1 = op1->gtOp.gtOp2;
+ goto AGAIN;
- // Is op2 an addition or a scaled value?
+ default:
+ break;
+ }
noway_assert(op2);
-
switch (op2->gtOper)
{
+#if !defined(_TARGET_ARMARCH_)
+ // TODO-ARM64-CQ, TODO-ARM-CQ: For now we don't try to create a scaled index.
case GT_ADD:
if (op2->gtOverflow())
@@ -2037,6 +1567,7 @@ AGAIN:
{
cns += op2->gtOp.gtOp2->gtIntCon.gtIconVal;
op2 = op2->gtOp.gtOp1;
+
goto AGAIN;
}
@@ -2058,9 +1589,9 @@ AGAIN:
mul = op2->GetScaledIndex();
if (mul)
{
- rv1 = op1;
- rv2 = op2->gtOp.gtOp1;
+ // 'op2' is a scaled value...is it's argument also scaled?
int argScale;
+ rv2 = op2->gtOp.gtOp1;
while ((rv2->gtOper == GT_MUL || rv2->gtOper == GT_LSH) && (argScale = rv2->GetScaledIndex()) != 0)
{
if (jitIsScaleIndexMul(argScale * mul))
@@ -2074,18 +1605,38 @@ AGAIN:
}
}
+ rv1 = op1;
+
goto FOUND_AM;
}
break;
#endif // SCALED_ADDR_MODES
+#endif // !_TARGET_ARMARCH
+
+ case GT_NOP:
+
+ if (!nogen)
+ {
+ break;
+ }
+
+ op2 = op2->gtOp.gtOp1;
+ goto AGAIN;
+
+ case GT_COMMA:
+
+ if (!nogen)
+ {
+ break;
+ }
+
+ op2 = op2->gtOp.gtOp2;
+ goto AGAIN;
default:
break;
}
-#endif // LEGACY_BACKEND
-
-ADD_OP12:
/* The best we can do "[rv1 + rv2]" or "[rv1 + rv2 + cns]" */
@@ -2097,18 +1648,6 @@ ADD_OP12:
FOUND_AM:
-#ifdef LEGACY_BACKEND
- /* Check for register variables */
-
- if (mode != -1)
- {
- if (rv1 && rv1->gtOper == GT_LCL_VAR)
- genMarkLclVar(rv1);
- if (rv2 && rv2->gtOper == GT_LCL_VAR)
- genMarkLclVar(rv2);
- }
-#endif // LEGACY_BACKEND
-
if (rv2)
{
/* Make sure a GC address doesn't end up in 'rv2' */
@@ -2125,17 +1664,6 @@ FOUND_AM:
}
/* Special case: constant array index (that is range-checked) */
- CLANG_FORMAT_COMMENT_ANCHOR;
-
-#if defined(LEGACY_BACKEND)
- // If we've already placed rv2 in a register, we are probably being called in a context that has already
- // presumed that an addressing mode will be created, even if rv2 is constant, and if we fold we may not find a
- // useful addressing mode (e.g. if we had [mul * rv2 + cns] it might happen to fold to [cns2].
- if (mode == -1 && rv2->InReg())
- {
- fold = false;
- }
-#endif
if (fold)
{
@@ -2237,10 +1765,8 @@ emitJumpKind CodeGen::genJumpKindForOper(genTreeOps cmp, CompareKind compareKind
EJ_jle, // GT_LE
EJ_jge, // GT_GE
EJ_jg, // GT_GT
-#ifndef LEGACY_BACKEND
EJ_je, // GT_TEST_EQ
EJ_jne, // GT_TEST_NE
-#endif
#elif defined(_TARGET_ARMARCH_)
EJ_eq, // GT_EQ
EJ_ne, // GT_NE
@@ -2264,10 +1790,8 @@ emitJumpKind CodeGen::genJumpKindForOper(genTreeOps cmp, CompareKind compareKind
EJ_jbe, // GT_LE
EJ_jae, // GT_GE
EJ_ja, // GT_GT
-#ifndef LEGACY_BACKEND
EJ_je, // GT_TEST_EQ
EJ_jne, // GT_TEST_NE
-#endif
#elif defined(_TARGET_ARMARCH_)
EJ_eq, // GT_EQ
EJ_ne, // GT_NE
@@ -2291,10 +1815,8 @@ emitJumpKind CodeGen::genJumpKindForOper(genTreeOps cmp, CompareKind compareKind
EJ_NONE, // GT_LE
EJ_jns, // GT_GE (S == 0)
EJ_NONE, // GT_GT
-#ifndef LEGACY_BACKEND
EJ_NONE, // GT_TEST_EQ
EJ_NONE, // GT_TEST_NE
-#endif
#elif defined(_TARGET_ARMARCH_)
EJ_eq, // GT_EQ (Z == 1)
EJ_ne, // GT_NE (Z == 0)
@@ -2316,10 +1838,8 @@ emitJumpKind CodeGen::genJumpKindForOper(genTreeOps cmp, CompareKind compareKind
assert(genJCCinsSigned[GT_LE - GT_EQ] == EJ_jle);
assert(genJCCinsSigned[GT_GE - GT_EQ] == EJ_jge);
assert(genJCCinsSigned[GT_GT - GT_EQ] == EJ_jg);
-#ifndef LEGACY_BACKEND
assert(genJCCinsSigned[GT_TEST_EQ - GT_EQ] == EJ_je);
assert(genJCCinsSigned[GT_TEST_NE - GT_EQ] == EJ_jne);
-#endif
assert(genJCCinsUnsigned[GT_EQ - GT_EQ] == EJ_je);
assert(genJCCinsUnsigned[GT_NE - GT_EQ] == EJ_jne);
@@ -2327,10 +1847,8 @@ emitJumpKind CodeGen::genJumpKindForOper(genTreeOps cmp, CompareKind compareKind
assert(genJCCinsUnsigned[GT_LE - GT_EQ] == EJ_jbe);
assert(genJCCinsUnsigned[GT_GE - GT_EQ] == EJ_jae);
assert(genJCCinsUnsigned[GT_GT - GT_EQ] == EJ_ja);
-#ifndef LEGACY_BACKEND
assert(genJCCinsUnsigned[GT_TEST_EQ - GT_EQ] == EJ_je);
assert(genJCCinsUnsigned[GT_TEST_NE - GT_EQ] == EJ_jne);
-#endif
assert(genJCCinsLogical[GT_EQ - GT_EQ] == EJ_je);
assert(genJCCinsLogical[GT_NE - GT_EQ] == EJ_jne);
@@ -2378,7 +1896,6 @@ emitJumpKind CodeGen::genJumpKindForOper(genTreeOps cmp, CompareKind compareKind
return result;
}
-#ifndef LEGACY_BACKEND
#ifdef _TARGET_ARMARCH_
//------------------------------------------------------------------------
// genEmitGSCookieCheck: Generate code to check that the GS cookie
@@ -2427,7 +1944,6 @@ void CodeGen::genEmitGSCookieCheck(bool pushReg)
genDefineTempLabel(gsCheckBlk);
}
#endif // _TARGET_ARMARCH_
-#endif // !LEGACY_BACKEND
/*****************************************************************************
*
@@ -2598,11 +2114,7 @@ void CodeGen::genCheckOverflow(GenTree* tree)
if (jumpKind == EJ_lo)
{
- if ((tree->OperGet() != GT_SUB)
-#ifdef LEGACY_BACKEND
- && (tree->gtOper != GT_ASG_SUB)
-#endif
- )
+ if (tree->OperGet() != GT_SUB)
{
jumpKind = EJ_hs;
}
@@ -2834,9 +2346,7 @@ void CodeGen::genGenerateCode(void** codePtr, ULONG* nativeSizeOfCode)
}
#endif // DEBUG
-#ifndef LEGACY_BACKEND
-
- // For RyuJIT backend, we compute the final frame layout before code generation. This is because LSRA
+ // We compute the final frame layout before code generation. This is because LSRA
// has already computed exactly the maximum concurrent number of spill temps of each type that are
// required during code generation. So, there is nothing left to estimate: we can be precise in the frame
// layout. This helps us generate smaller code, and allocate, after code generation, a smaller amount of
@@ -2846,90 +2356,18 @@ void CodeGen::genGenerateCode(void** codePtr, ULONG* nativeSizeOfCode)
unsigned maxTmpSize = compiler->tmpSize; // This is precise after LSRA has pre-allocated the temps.
-#else // LEGACY_BACKEND
-
- // Estimate the frame size: first, estimate the number of spill temps needed by taking the register
- // predictor spill temp estimates and stress levels into consideration. Then, compute the tentative
- // frame layout using conservative callee-save register estimation (namely, guess they'll all be used
- // and thus saved on the frame).
-
- // Compute the maximum estimated spill temp size.
- unsigned maxTmpSize = sizeof(double) + sizeof(float) + sizeof(__int64) + TARGET_POINTER_SIZE;
-
- maxTmpSize += (compiler->tmpDoubleSpillMax * sizeof(double)) + (compiler->tmpIntSpillMax * sizeof(int));
-
-#ifdef DEBUG
-
- /* When StressRegs is >=1, there will be a bunch of spills not predicted by
- the predictor (see logic in rsPickReg). It will be 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.
- This means that at StressRegs >= 1, we will not be testing the logic
- that sets the maxTmpSize size.
- */
-
- if (regSet.rsStressRegs() >= 1)
- {
- maxTmpSize += (REG_TMP_ORDER_COUNT * REGSIZE_BYTES);
- }
-
- // JIT uses 2 passes when assigning stack variable (i.e. args, temps, and locals) locations in varDsc->lvStkOffs.
- // During the 1st pass (in genGenerateCode), it estimates the maximum possible size for stack temps
- // and put it in maxTmpSize. Then it calculates the varDsc->lvStkOffs for each variable based on this estimation.
- // However during stress mode, we might spill more temps on the stack, which might grow the
- // size of the temp area.
- // This might cause varDsc->lvStkOffs to change during the 2nd pass (in emitEndCodeGen).
- // If the change of varDsc->lvStkOffs crosses the threshold for the instruction size,
- // we will then have a mismatched estimated code size (during the 1st pass) and the actual emitted code size
- // (during the 2nd pass).
- // Also, if STRESS_UNSAFE_BUFFER_CHECKS is turned on, we might reorder the stack variable locations,
- // which could cause the mismatch too.
- //
- // The following code is simply bump the maxTmpSize up to at least BYTE_MAX+1 during the stress mode, so that
- // we don't run into code size problem during stress.
-
- if (getJitStressLevel() != 0)
- {
- if (maxTmpSize < BYTE_MAX + 1)
- {
- maxTmpSize = BYTE_MAX + 1;
- }
- }
-#endif // DEBUG
-
- /* Estimate the offsets of locals/arguments and size of frame */
-
- unsigned lclSize = compiler->lvaFrameSize(Compiler::TENTATIVE_FRAME_LAYOUT);
-
-#ifdef DEBUG
- //
- // Display the local frame offsets that we have tentatively decided upon
- //
- if (verbose)
- {
- compiler->lvaTableDump();
- }
-#endif // DEBUG
-
-#endif // LEGACY_BACKEND
-
getEmitter()->emitBegFN(isFramePointerUsed()
#if defined(DEBUG)
,
(compiler->compCodeOpt() != Compiler::SMALL_CODE) &&
!compiler->opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT)
#endif
-#ifdef LEGACY_BACKEND
,
- lclSize
-#endif // LEGACY_BACKEND
- ,
maxTmpSize);
/* Now generate code for the function */
genCodeForBBlist();
-#ifndef LEGACY_BACKEND
#ifdef DEBUG
// After code generation, dump the frame layout again. It should be the same as before code generation, if code
// generation hasn't touched it (it shouldn't!).
@@ -2938,7 +2376,6 @@ void CodeGen::genGenerateCode(void** codePtr, ULONG* nativeSizeOfCode)
compiler->lvaTableDump();
}
#endif // DEBUG
-#endif // !LEGACY_BACKEND
/* We can now generate the function prolog and epilog */
@@ -3725,11 +3162,7 @@ bool CodeGenInterface::genUseOptimizedWriteBarriers(GenTree* tgt, GenTree* assig
//
CorInfoHelpFunc CodeGenInterface::genWriteBarrierHelperForWriteBarrierForm(GenTree* tgt, GCInfo::WriteBarrierForm wbf)
{
-#ifndef LEGACY_BACKEND
noway_assert(tgt->gtOper == GT_STOREIND);
-#else // LEGACY_BACKEND
- noway_assert(tgt->gtOper == GT_IND || tgt->gtOper == GT_CLS_VAR); // enforced by gcIsWriteBarrierCandidate
-#endif // LEGACY_BACKEND
CorInfoHelpFunc helper = CORINFO_HELP_ASSIGN_REF;
@@ -4281,18 +3714,12 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere
{
#ifdef _TARGET_X86_
noway_assert(varDsc->lvType == TYP_STRUCT);
-#else // !_TARGET_X86_
-#ifndef LEGACY_BACKEND
+#else // !_TARGET_X86_
// For LSRA, it may not be in regArgMaskLive if it has a zero
// refcnt. This is in contrast with the non-LSRA case in which all
// non-tracked args are assumed live on entry.
noway_assert((varDsc->lvRefCnt == 0) || (varDsc->lvType == TYP_STRUCT) ||
(varDsc->lvAddrExposed && compiler->info.compIsVarArgs));
-#else // LEGACY_BACKEND
- noway_assert(
- varDsc->lvType == TYP_STRUCT ||
- (varDsc->lvAddrExposed && (compiler->info.compIsVarArgs || compiler->opts.compUseSoftFP)));
-#endif // LEGACY_BACKEND
#endif // !_TARGET_X86_
}
// Mark it as processed and be done with it
@@ -4471,12 +3898,9 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere
// LSRA allocates registers to incoming parameters in order and will not overwrite
// a register still holding a live parameter.
- CLANG_FORMAT_COMMENT_ANCHOR;
-#ifndef LEGACY_BACKEND
noway_assert(((regArgMaskLive & RBM_FLTARG_REGS) == 0) &&
"Homing of float argument registers with circular dependencies not implemented.");
-#endif // LEGACY_BACKEND
/* Now move the arguments to their locations.
* First consider ones that go on the stack since they may
@@ -4972,15 +4396,15 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere
#ifndef _TARGET_64BIT_
else if (regArgTab[argNum].slot == 2 && genActualType(destMemType) == TYP_LONG)
{
-#ifndef LEGACY_BACKEND
assert(genActualType(varDsc->TypeGet()) == TYP_LONG || genActualType(varDsc->TypeGet()) == TYP_DOUBLE);
if (genActualType(varDsc->TypeGet()) == TYP_DOUBLE)
{
destRegNum = regNum;
}
else
-#endif // !LEGACY_BACKEND
+ {
destRegNum = varDsc->lvOtherReg;
+ }
assert(destRegNum != REG_STK);
}
@@ -5193,12 +4617,6 @@ void CodeGen::genEnregisterIncomingStackArgs()
var_types type = genActualType(varDsc->TypeGet());
-#if FEATURE_STACK_FP_X87
- // Floating point locals are loaded onto the x86-FPU in the next section
- if (varTypeIsFloating(type))
- continue;
-#endif
-
/* Is the variable dead on entry */
if (!VarSetOps::IsMember(compiler, compiler->fgFirstBB->bbLiveIn, varDsc->lvVarIndex))
@@ -5210,61 +4628,11 @@ void CodeGen::genEnregisterIncomingStackArgs()
/* Figure out the home offset of the incoming argument */
- regNumber regNum;
- regNumber otherReg;
-
-#ifndef LEGACY_BACKEND
-#ifdef _TARGET_ARM_
- if (type == TYP_LONG)
- {
- regPairNo regPair = varDsc->lvArgInitRegPair;
- regNum = genRegPairLo(regPair);
- otherReg = genRegPairHi(regPair);
- }
- else
-#endif // _TARGET_ARM_
- {
- regNum = varDsc->lvArgInitReg;
- otherReg = REG_NA;
- }
-#else // LEGACY_BACKEND
- regNum = varDsc->lvRegNum;
- if (type == TYP_LONG)
- {
- otherReg = varDsc->lvOtherReg;
- }
- else
- {
- otherReg = REG_NA;
- }
-#endif // LEGACY_BACKEND
-
+ regNumber regNum = varDsc->lvArgInitReg;
assert(regNum != REG_STK);
-#ifndef _TARGET_64BIT_
- if (type == TYP_LONG)
- {
- /* long - at least the low half must be enregistered */
-
- getEmitter()->emitIns_R_S(ins_Load(TYP_INT), EA_4BYTE, regNum, varNum, 0);
- regTracker.rsTrackRegTrash(regNum);
-
- /* Is the upper half also enregistered? */
-
- if (otherReg != REG_STK)
- {
- getEmitter()->emitIns_R_S(ins_Load(TYP_INT), EA_4BYTE, otherReg, varNum, sizeof(int));
- regTracker.rsTrackRegTrash(otherReg);
- }
- }
- else
-#endif // _TARGET_64BIT_
- {
- /* Loading a single register - this is the easy/common case */
-
- getEmitter()->emitIns_R_S(ins_Load(type), emitTypeSize(type), regNum, varNum, 0);
- regTracker.rsTrackRegTrash(regNum);
- }
+ getEmitter()->emitIns_R_S(ins_Load(type), emitTypeSize(type), regNum, varNum, 0);
+ regTracker.rsTrackRegTrash(regNum);
psiMoveToReg(varNum);
}
@@ -5290,11 +4658,7 @@ void CodeGen::genEnregisterIncomingStackArgs()
*/
void CodeGen::genCheckUseBlockInit()
{
-#ifndef LEGACY_BACKEND // this is called before codegen in RyuJIT backend
assert(!compiler->compGeneratingProlog);
-#else // LEGACY_BACKEND
- assert(compiler->compGeneratingProlog);
-#endif // LEGACY_BACKEND
unsigned initStkLclCnt = 0; // The number of int-sized stack local variables that need to be initialized (variables
// larger than int count for more than 1).
@@ -5372,12 +4736,9 @@ void CodeGen::genCheckUseBlockInit()
{
if (!varDsc->lvRegister)
{
-#ifndef LEGACY_BACKEND
if (!varDsc->lvIsInReg())
-#endif // !LEGACY_BACKEND
{
- // Var is completely on the stack, in the legacy JIT case, or
- // on the stack at entry, in the RyuJIT case.
+ // Var is on the stack at entry.
initStkLclCnt +=
(unsigned)roundUp(compiler->lvaLclSize(varNum), TARGET_POINTER_SIZE) / sizeof(int);
}
@@ -5552,12 +4913,12 @@ void CodeGen::genPushCalleeSavedRegisters()
{
assert(compiler->compGeneratingProlog);
-#if defined(_TARGET_XARCH_) && !FEATURE_STACK_FP_X87
+#if defined(_TARGET_XARCH_)
// x86/x64 doesn't support push of xmm/ymm regs, therefore consider only integer registers for pushing onto stack
// here. Space for float registers to be preserved is stack allocated and saved as part of prolog sequence and not
// here.
regMaskTP rsPushRegs = regSet.rsGetModifiedRegsMask() & RBM_INT_CALLEE_SAVED;
-#else // !defined(_TARGET_XARCH_) || FEATURE_STACK_FP_X87
+#else // !defined(_TARGET_XARCH_)
regMaskTP rsPushRegs = regSet.rsGetModifiedRegsMask() & RBM_CALLEE_SAVED;
#endif
@@ -6311,12 +5672,7 @@ void CodeGen::genFreeLclFrame(unsigned frameSize, /* IN OUT */ bool* pUnwindStar
// Do not use argument registers as scratch registers in the jmp epilog.
grabMask &= ~genJmpCallArgMask();
}
-#ifndef LEGACY_BACKEND
- regNumber tmpReg;
- tmpReg = REG_TMP_0;
-#else // LEGACY_BACKEND
- regNumber tmpReg = regSet.rsGrabReg(grabMask);
-#endif // LEGACY_BACKEND
+ regNumber tmpReg = REG_TMP_0;
instGen_Set_Reg_To_Imm(EA_PTRSIZE, tmpReg, frameSize);
if (*pUnwindStarted)
{
@@ -6418,8 +5774,6 @@ regMaskTP CodeGen::genStackAllocRegisterMask(unsigned frameSize, regMaskTP maskC
#endif // _TARGET_ARM_
-#if !FEATURE_STACK_FP_X87
-
/*****************************************************************************
*
* initFltRegs -- The mask of float regs to be zeroed.
@@ -6515,7 +5869,6 @@ void CodeGen::genZeroInitFltRegs(const regMaskTP& initFltRegs, const regMaskTP&
}
}
}
-#endif // !FEATURE_STACK_FP_X87
/*-----------------------------------------------------------------------------
*
@@ -6761,7 +6114,7 @@ void CodeGen::genPopCalleeSavedRegistersAndFreeLclFrame(bool jmpEpilog)
}
}
-#elif defined(_TARGET_XARCH_) && !FEATURE_STACK_FP_X87
+#elif defined(_TARGET_XARCH_)
void CodeGen::genPopCalleeSavedRegisters(bool jmpEpilog)
{
@@ -7180,28 +6533,10 @@ void CodeGen::genZeroInitFrame(int untrLclHi, int untrLclLo, regNumber initReg,
noway_assert(varTypeIsGC(varDsc->TypeGet()) || (varDsc->TypeGet() == TYP_STRUCT) ||
compiler->info.compInitMem || compiler->opts.compDbgCode);
-#ifndef LEGACY_BACKEND
if (!varDsc->lvOnFrame)
{
continue;
}
-#else // LEGACY_BACKEND
- if (varDsc->lvRegister)
- {
- if (varDsc->lvOnFrame)
- {
- /* This is a partially enregistered TYP_LONG var */
- noway_assert(varDsc->lvOtherReg == REG_STK);
- noway_assert(varDsc->lvType == TYP_LONG);
-
- noway_assert(compiler->info.compInitMem);
-
- getEmitter()->emitIns_S_R(ins_Store(TYP_INT), EA_4BYTE, genGetZeroReg(initReg, pInitRegZeroed),
- varNum, sizeof(int));
- }
- continue;
- }
-#endif // LEGACY_BACKEND
if ((varDsc->TypeGet() == TYP_STRUCT) && !compiler->info.compInitMem &&
(varDsc->lvExactSize >= TARGET_POINTER_SIZE))
@@ -7641,17 +6976,10 @@ void CodeGen::genProfilingEnterCallback(regNumber initReg, bool* pInitRegZeroed)
inst_IV(INS_push, (size_t)compiler->compProfilerMethHnd);
}
#elif defined(_TARGET_ARM_)
-// On Arm arguments are prespilled on stack, which frees r0-r3.
-// For generating Enter callout we would need two registers and one of them has to be r0 to pass profiler handle.
-// The call target register could be any free register.
-#ifdef LEGACY_BACKEND
- regNumber argReg = regSet.rsGrabReg(RBM_PROFILER_ENTER_ARG);
- noway_assert(argReg == REG_PROFILER_ENTER_ARG);
- regSet.rsLockReg(RBM_PROFILER_ENTER_ARG);
-#else // !LEGACY_BACKEND
- regNumber argReg = REG_PROFILER_ENTER_ARG;
-#endif // !LEGACY_BACKEND
-
+ // On Arm arguments are prespilled on stack, which frees r0-r3.
+ // For generating Enter callout we would need two registers and one of them has to be r0 to pass profiler handle.
+ // The call target register could be any free register.
+ regNumber argReg = REG_PROFILER_ENTER_ARG;
regMaskTP argRegMask = genRegMask(argReg);
assert((regSet.rsMaskPreSpillRegArg & argRegMask) != 0);
@@ -7690,11 +7018,6 @@ void CodeGen::genProfilingEnterCallback(regNumber initReg, bool* pInitRegZeroed)
compiler->fgPtrArgCntMax = 1;
}
#elif defined(_TARGET_ARM_)
-#ifdef LEGACY_BACKEND
- // Unlock registers
- regSet.rsUnlockReg(RBM_PROFILER_ENTER_ARG);
-#endif // LEGACY_BACKEND
-
if (initReg == argReg)
{
*pInitRegZeroed = false;
@@ -7896,17 +7219,9 @@ void CodeGen::genProfilingLeaveCallback(unsigned helper /*= CORINFO_HELP_PROF_FC
}
#elif defined(_TARGET_ARM_)
-//
-// Push the profilerHandle
-//
-
-// We could optimize register usage based on return value is int/long/void. But to keep it simple we will lock
-// RBM_PROFILER_RET_USED always.
-#ifdef LEGACY_BACKEND
- regNumber scratchReg = regSet.rsGrabReg(RBM_PROFILER_RET_SCRATCH);
- noway_assert(scratchReg == REG_PROFILER_RET_SCRATCH);
- regSet.rsLockReg(RBM_PROFILER_RET_USED);
-#endif // LEGACY_BACKEND
+ //
+ // Push the profilerHandle
+ //
// Contract between JIT and Profiler Leave callout on arm:
// Return size <= 4 bytes: REG_PROFILER_RET_SCRATCH will contain return value
@@ -7972,10 +7287,6 @@ void CodeGen::genProfilingLeaveCallback(unsigned helper /*= CORINFO_HELP_PROF_FC
gcInfo.gcMarkRegSetNpt(RBM_PROFILER_RET_SCRATCH);
}
-#ifdef LEGACY_BACKEND
- regSet.rsUnlockReg(RBM_PROFILER_RET_USED);
-#endif // LEGACY_BACKEND
-
#else // target
NYI("Emit Profiler Leave callback");
#endif // target
@@ -8235,13 +7546,11 @@ void CodeGen::genFinalizeFrame()
{
JITDUMP("Finalizing stack frame\n");
-#ifndef LEGACY_BACKEND
// Initializations need to happen based on the var locations at the start
// of the first basic block, so load those up. In particular, the determination
// of whether or not to use block init in the prolog is dependent on the variable
// locations on entry to the function.
compiler->m_pLinearScan->recordVarLocationsAtStartOfBB(compiler->fgFirstBB);
-#endif // !LEGACY_BACKEND
genCheckUseBlockInit();
@@ -8395,13 +7704,13 @@ void CodeGen::genFinalizeFrame()
#endif // _TARGET_ARM_
#endif // _TARGET_ARMARCH_
-#if defined(_TARGET_XARCH_) && !FEATURE_STACK_FP_X87
+#if defined(_TARGET_XARCH_)
// Compute the count of callee saved float regs saved on stack.
// On Amd64 we push only integer regs. Callee saved float (xmm6-xmm15)
// regs are stack allocated and preserved in their stack locations.
compiler->compCalleeFPRegsSavedMask = maskCalleeRegsPushed & RBM_FLT_CALLEE_SAVED;
maskCalleeRegsPushed &= ~RBM_FLT_CALLEE_SAVED;
-#endif // defined(_TARGET_XARCH_) && !FEATURE_STACK_FP_X87
+#endif // defined(_TARGET_XARCH_)
compiler->compCalleeRegsPushed = genCountBits(maskCalleeRegsPushed);
@@ -8519,10 +7828,6 @@ void CodeGen::genFnProlog()
genInterruptibleUsed = true;
#endif
-#ifdef LEGACY_BACKEND
- genFinalizeFrame();
-#endif // LEGACY_BACKEND
-
assert(compiler->lvaDoneFrameLayout == Compiler::FINAL_FRAME_LAYOUT);
/* Ready to start on the prolog proper */
@@ -8673,7 +7978,6 @@ void CodeGen::genFnProlog()
}
}
}
-#if !FEATURE_STACK_FP_X87
else if (varDsc->TypeGet() == TYP_DOUBLE)
{
initDblRegs |= regMask;
@@ -8682,7 +7986,6 @@ void CodeGen::genFnProlog()
{
initFltRegs |= regMask;
}
-#endif // !FEATURE_STACK_FP_X87
}
else
{
@@ -8951,10 +8254,10 @@ void CodeGen::genFnProlog()
}
#endif // _TARGET_ARMARCH_
-#if defined(_TARGET_XARCH_) && !FEATURE_STACK_FP_X87
+#if defined(_TARGET_XARCH_)
// Preserve callee saved float regs to stack.
genPreserveCalleeSavedFltRegs(compiler->compLclFrameSize);
-#endif // defined(_TARGET_XARCH_) && !FEATURE_STACK_FP_X87
+#endif // defined(_TARGET_XARCH_)
#ifdef _TARGET_AMD64_
// Establish the AMD64 frame pointer after the OS-reported prolog.
@@ -9045,15 +8348,6 @@ void CodeGen::genFnProlog()
genReportGenericContextArg(initReg, &initRegZeroed);
-#if defined(LEGACY_BACKEND) // in RyuJIT backend this has already been expanded into trees
- if (compiler->info.compCallUnmanaged && !compiler->opts.ShouldUsePInvokeHelpers())
- {
- getEmitter()->emitDisableRandomNops();
- initRegs = genPInvokeMethodProlog(initRegs);
- getEmitter()->emitEnableRandomNops();
- }
-#endif // defined(LEGACY_BACKEND)
-
// The local variable representing the security object must be on the stack frame
// and must be 0 initialized.
noway_assert((compiler->lvaSecurityObject == BAD_VAR_NUM) ||
@@ -9114,10 +8408,8 @@ void CodeGen::genFnProlog()
RegState* regState;
-#ifndef LEGACY_BACKEND
// Update the arg initial register locations.
compiler->lvaUpdateArgsWithInitialReg();
-#endif // !LEGACY_BACKEND
FOREACH_REGISTER_FILE(regState)
{
@@ -9179,7 +8471,6 @@ void CodeGen::genFnProlog()
}
}
-#if !FEATURE_STACK_FP_X87
if (initFltRegs | initDblRegs)
{
// If initReg is not in initRegs then we will use REG_SCRATCH
@@ -9201,15 +8492,6 @@ void CodeGen::genFnProlog()
genZeroInitFltRegs(initFltRegs, initDblRegs, initReg);
}
-#endif // !FEATURE_STACK_FP_X87
-
-#if FEATURE_STACK_FP_X87
- //
- // Here is where we load the enregistered floating point arguments
- // and locals onto the x86-FPU.
- //
- genCodeForPrologStackFP();
-#endif
//-----------------------------------------------------------------------------
@@ -9623,10 +8905,8 @@ void CodeGen::genFnEpilog(BasicBlock* block)
}
#endif
-#if !FEATURE_STACK_FP_X87
// Restore float registers that were saved to stack before SP is modified.
genRestoreCalleeSavedFltRegs(compiler->compLclFrameSize);
-#endif // !FEATURE_STACK_FP_X87
#ifdef JIT32_GCENCODER
// When using the JIT32 GC encoder, we do not start the OS-reported portion of the epilog until after
@@ -10795,11 +10075,9 @@ void CodeGen::genGeneratePrologsAndEpilogs()
}
#endif
-#ifndef LEGACY_BACKEND
// Before generating the prolog, we need to reset the variable locations to what they will be on entry.
// This affects our code that determines which untracked locals need to be zero initialized.
compiler->m_pLinearScan->recordVarLocationsAtStartOfBB(compiler->fgFirstBB);
-#endif // !LEGACY_BACKEND
// Tell the emitter we're done with main code generation, and are going to start prolog and epilog generation.
@@ -10874,53 +10152,7 @@ void CodeGen::genGenerateStackProbe()
}
#endif // STACK_PROBES
-#ifdef LEGACY_BACKEND
-/*****************************************************************************
- *
- * Record the constant and return a tree node that yields its address.
- */
-
-GenTree* CodeGen::genMakeConst(const void* cnsAddr, var_types cnsType, GenTree* cnsTree, bool dblAlign)
-{
- // Assign the constant an offset in the data section
- UNATIVE_OFFSET cnsSize = genTypeSize(cnsType);
- UNATIVE_OFFSET cnum = getEmitter()->emitDataConst(cnsAddr, cnsSize, dblAlign);
-
-#ifdef DEBUG
- if (compiler->opts.dspCode)
- {
- printf(" @%s%02u ", "CNS", cnum);
-
- switch (cnsType)
- {
- case TYP_INT:
- printf("DD %d \n", *(int*)cnsAddr);
- break;
- case TYP_LONG:
- printf("DQ %lld\n", *(__int64*)cnsAddr);
- break;
- case TYP_FLOAT:
- printf("DF %f \n", *(float*)cnsAddr);
- break;
- case TYP_DOUBLE:
- printf("DQ %lf\n", *(double*)cnsAddr);
- break;
-
- default:
- noway_assert(!"unexpected constant type");
- }
- }
-#endif
-
- // Access to inline data is 'abstracted' by a special type of static member
- // (produced by eeFindJitDataOffs) which the emitter recognizes as being a reference
- // to constant data, not a real static field.
-
- return new (compiler, GT_CLS_VAR) GenTreeClsVar(cnsType, compiler->eeFindJitDataOffs(cnum), nullptr);
-}
-#endif // LEGACY_BACKEND
-
-#if defined(_TARGET_XARCH_) && !FEATURE_STACK_FP_X87
+#if defined(_TARGET_XARCH_)
// Save compCalleeFPRegsPushed with the smallest register number saved at [RSP+offset], working
// down the stack to the largest register number stored at [RSP+offset-(genCountBits(regMask)-1)*XMM_REG_SIZE]
// Here offset = 16-byte aligned offset after pushing integer registers.
@@ -11069,7 +10301,7 @@ void CodeGen::genVzeroupperIfNeeded(bool check256bitOnly /* = true*/)
}
}
-#endif // defined(_TARGET_XARCH_) && !FEATURE_STACK_FP_X87
+#endif // defined(_TARGET_XARCH_)
//-----------------------------------------------------------------------------------
// IsMultiRegPassedType: Returns true if the type is returned in multiple registers
@@ -11250,8 +10482,6 @@ instruction CodeGen::genMapShiftInsToShiftByConstantIns(instruction ins, int shi
#endif // _TARGET_XARCH_
-#if !defined(LEGACY_BACKEND)
-
//------------------------------------------------------------------------------------------------ //
// getFirstArgWithStackSlot - returns the first argument with stack slot on the caller's frame.
//
@@ -11313,8 +10543,6 @@ unsigned CodeGen::getFirstArgWithStackSlot()
#endif // _TARGET_X86_
}
-#endif // !LEGACY_BACKEND
-
//------------------------------------------------------------------------
// genSinglePush: Report a change in stack level caused by a single word-sized push instruction
//
@@ -12490,8 +11718,6 @@ const char* CodeGen::siStackVarName(size_t offs, size_t size, unsigned reg, unsi
#endif // defined(LATE_DISASM)
/*****************************************************************************/
-#ifndef LEGACY_BACKEND
-
//------------------------------------------------------------------------
// indirForm: Make a temporary indir we can feed to pattern matching routines
// in cases where we don't want to instantiate all the indirs that happen.
@@ -12713,5 +11939,3 @@ void CodeGen::genReturn(GenTree* treeNode)
}
#endif // PROFILING_SUPPORTED
}
-
-#endif // !LEGACY_BACKEND
diff --git a/src/jit/codegeninterface.h b/src/jit/codegeninterface.h
index 371bb1ea23..e6948ad3ca 100644
--- a/src/jit/codegeninterface.h
+++ b/src/jit/codegeninterface.h
@@ -37,11 +37,8 @@ class emitter;
struct RegState
{
regMaskTP rsCalleeRegArgMaskLiveIn; // mask of register arguments (live on entry to method)
-#ifdef LEGACY_BACKEND
- unsigned rsCurRegArgNum; // current argument number (for caller)
-#endif
- unsigned rsCalleeRegArgCount; // total number of incoming register arguments of this kind (int or float)
- bool rsIsFloat; // true for float argument registers, false for integer argument registers
+ unsigned rsCalleeRegArgCount; // total number of incoming register arguments of this kind (int or float)
+ bool rsIsFloat; // true for float argument registers, false for integer argument registers
};
//-------------------- CodeGenInterface ---------------------------------
@@ -57,13 +54,11 @@ public:
CodeGenInterface(Compiler* theCompiler);
virtual void genGenerateCode(void** codePtr, ULONG* nativeSizeOfCode) = 0;
-#ifndef LEGACY_BACKEND
- // genSpillVar is called by compUpdateLifeVar in the RyuJIT backend case.
+ // genSpillVar is called by compUpdateLifeVar.
// TODO-Cleanup: We should handle the spill directly in CodeGen, rather than
// calling it from compUpdateLifeVar. Then this can be non-virtual.
virtual void genSpillVar(GenTree* tree) = 0;
-#endif // !LEGACY_BACKEND
//-------------------------------------------------------------------------
// The following property indicates whether to align loops.
@@ -105,14 +100,6 @@ public:
// in RegSet::rsUnspillOneReg, it needs to mark the new register as "trash"
RegTracker regTracker;
-public:
-#ifdef LEGACY_BACKEND
- void trashReg(regNumber reg)
- {
- regTracker.rsTrackRegTrash(reg);
- }
-#endif
-
protected:
Compiler* compiler;
bool m_genAlignLoops;
@@ -128,9 +115,7 @@ public:
// Liveness-related fields & methods
public:
void genUpdateRegLife(const LclVarDsc* varDsc, bool isBorn, bool isDying DEBUGARG(GenTree* tree));
-#ifndef LEGACY_BACKEND
void genUpdateVarReg(LclVarDsc* varDsc, GenTree* tree);
-#endif // !LEGACY_BACKEND
protected:
#ifdef DEBUG
@@ -147,11 +132,6 @@ protected:
void genUpdateLife(GenTree* tree);
void genUpdateLife(VARSET_VALARG_TP newLife);
-#ifdef LEGACY_BACKEND
- regMaskTP genLiveMask(GenTree* tree);
- regMaskTP genLiveMask(VARSET_VALARG_TP liveSet);
-#endif
-
TreeLifeUpdater<true>* treeLifeUpdater;
public:
@@ -296,59 +276,11 @@ protected:
#endif
public:
-#if FEATURE_STACK_FP_X87
- FlatFPStateX87 compCurFPState;
- unsigned genFPregCnt; // count of current FP reg. vars (including dead but unpopped ones)
-
- void SetRegVarFloat(regNumber reg, var_types type, LclVarDsc* varDsc);
-
- void inst_FN(instruction ins, unsigned stk);
-
- // Keeps track of the current level of the FP coprocessor stack
- // (excluding FP reg. vars).
- // Do not use directly, instead use the processor agnostic accessor
- // methods below
- //
- unsigned genFPstkLevel;
-
- void genResetFPstkLevel(unsigned newValue = 0);
- unsigned genGetFPstkLevel();
- FlatFPStateX87* FlatFPAllocFPState(FlatFPStateX87* pInitFrom = 0);
-
- void genIncrementFPstkLevel(unsigned inc = 1);
- void genDecrementFPstkLevel(unsigned dec = 1);
-
- static const char* regVarNameStackFP(regNumber reg);
-
- // FlatFPStateX87_ functions are the actual verbs to do stuff
- // like doing a transition, loading register, etc. It's also
- // responsible for emitting the x87 code to do so. We keep
- // them in Compiler because we don't want to store a pointer to the
- // emitter.
- void FlatFPX87_MoveToTOS(FlatFPStateX87* pState, unsigned iVirtual, bool bEmitCode = true);
- void FlatFPX87_SwapStack(FlatFPStateX87* pState, unsigned i, unsigned j, bool bEmitCode = true);
-
-#endif // FEATURE_STACK_FP_X87
-
-#ifndef LEGACY_BACKEND
- regNumber genGetAssignedReg(GenTree* tree);
-#endif // !LEGACY_BACKEND
-
-#ifdef LEGACY_BACKEND
- // Changes GT_LCL_VAR nodes to GT_REG_VAR nodes if possible.
- bool genMarkLclVar(GenTree* tree);
-
- void genBashLclVar(GenTree* tree, unsigned varNum, LclVarDsc* varDsc);
-#endif // LEGACY_BACKEND
-
-public:
unsigned InferStructOpSizeAlign(GenTree* op, unsigned* alignmentWB);
unsigned InferOpSizeAlign(GenTree* op, unsigned* alignmentWB);
void genMarkTreeInReg(GenTree* tree, regNumber reg);
-#if CPU_LONG_USES_REGPAIR
- void genMarkTreeInRegPair(GenTree* tree, regPairNo regPair);
-#endif
+
// Methods to abstract target information
bool validImmForInstr(instruction ins, ssize_t val, insFlags flags = INS_FLAGS_DONT_CARE);
@@ -367,10 +299,6 @@ public:
void reloadReg(var_types type, TempDsc* tmp, regNumber reg);
void reloadFloatReg(var_types type, TempDsc* tmp, regNumber reg);
-#ifdef LEGACY_BACKEND
- void SpillFloat(regNumber reg, bool bIsCall = false);
-#endif // LEGACY_BACKEND
-
// The following method is used by xarch emitter for handling contained tree temps.
TempDsc* getSpillTempDsc(GenTree* tree);
@@ -401,11 +329,6 @@ public:
verbose = value;
}
bool verbose;
-#ifdef LEGACY_BACKEND
- // Stress mode
- int genStressFloat();
- regMaskTP genStressLockedMaskFloat();
-#endif // LEGACY_BACKEND
#endif // DEBUG
// The following is set to true if we've determined that the current method
diff --git a/src/jit/codegenlegacy.cpp b/src/jit/codegenlegacy.cpp
deleted file mode 100644
index ec6ed7fb75..0000000000
--- a/src/jit/codegenlegacy.cpp
+++ /dev/null
@@ -1,22278 +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 CodeGenerator XX
-XX XX
-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-*/
-#include "jitpch.h"
-#ifdef _MSC_VER
-#pragma hdrstop
-#endif
-#include "codegen.h"
-
-#ifdef LEGACY_BACKEND // This file is NOT used for the '!LEGACY_BACKEND' that uses the linear scan register allocator
-
-#ifdef _TARGET_AMD64_
-#error AMD64 must be !LEGACY_BACKEND
-#endif
-
-#ifdef _TARGET_ARM64_
-#error ARM64 must be !LEGACY_BACKEND
-#endif
-
-#include "gcinfo.h"
-#include "emit.h"
-
-#ifndef JIT32_GCENCODER
-#include "gcinfoencoder.h"
-#endif
-
-/*****************************************************************************
- *
- * Determine what variables die between beforeSet and afterSet, and
- * update the liveness globals accordingly:
- * compiler->compCurLife, gcInfo.gcVarPtrSetCur, regSet.rsMaskVars, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur
- */
-
-void CodeGen::genDyingVars(VARSET_VALARG_TP beforeSet, VARSET_VALARG_TP afterSet)
-{
- unsigned varNum;
- LclVarDsc* varDsc;
- regMaskTP regBit;
- VARSET_TP deadSet(VarSetOps::Diff(compiler, beforeSet, afterSet));
-
- if (VarSetOps::IsEmpty(compiler, deadSet))
- return;
-
- /* iterate through the dead variables */
-
- VarSetOps::Iter iter(compiler, deadSet);
- unsigned varIndex = 0;
- while (iter.NextElem(&varIndex))
- {
- varNum = compiler->lvaTrackedToVarNum[varIndex];
- varDsc = compiler->lvaTable + varNum;
-
- /* Remove this variable from the 'deadSet' bit set */
-
- noway_assert(VarSetOps::IsMember(compiler, compiler->compCurLife, varIndex));
-
- VarSetOps::RemoveElemD(compiler, compiler->compCurLife, varIndex);
-
- noway_assert(!VarSetOps::IsMember(compiler, gcInfo.gcTrkStkPtrLcls, varIndex) ||
- VarSetOps::IsMember(compiler, gcInfo.gcVarPtrSetCur, varIndex));
-
- VarSetOps::RemoveElemD(compiler, gcInfo.gcVarPtrSetCur, varIndex);
-
- /* We are done if the variable is not enregistered */
-
- if (!varDsc->lvRegister)
- {
-#ifdef DEBUG
- if (compiler->verbose)
- {
- printf("\t\t\t\t\t\t\tV%02u,T%02u is a dyingVar\n", varNum, varDsc->lvVarIndex);
- }
-#endif
- continue;
- }
-
-#if !FEATURE_FP_REGALLOC
- // We don't do FP-enreg of vars whose liveness changes in GTF_COLON_COND
- if (!varDsc->IsFloatRegType())
-#endif
- {
- /* Get hold of the appropriate register bit(s) */
-
- if (varTypeIsFloating(varDsc->TypeGet()))
- {
- regBit = genRegMaskFloat(varDsc->lvRegNum, varDsc->TypeGet());
- }
- else
- {
- regBit = genRegMask(varDsc->lvRegNum);
- if (isRegPairType(varDsc->lvType) && varDsc->lvOtherReg != REG_STK)
- regBit |= genRegMask(varDsc->lvOtherReg);
- }
-
-#ifdef DEBUG
- if (compiler->verbose)
- {
- printf("\t\t\t\t\t\t\tV%02u,T%02u in reg %s is a dyingVar\n", varNum, varDsc->lvVarIndex,
- compiler->compRegVarName(varDsc->lvRegNum));
- }
-#endif
- noway_assert((regSet.rsMaskVars & regBit) != 0);
-
- regSet.RemoveMaskVars(regBit);
-
- // Remove GC tracking if any for this register
-
- if ((regBit & regSet.rsMaskUsed) == 0) // The register may be multi-used
- gcInfo.gcMarkRegSetNpt(regBit);
- }
- }
-}
-
-/*****************************************************************************
- *
- * Change the given enregistered local variable node to a register variable node
- */
-
-void CodeGenInterface::genBashLclVar(GenTree* tree, unsigned varNum, LclVarDsc* varDsc)
-{
- noway_assert(tree->gtOper == GT_LCL_VAR);
- noway_assert(varDsc->lvRegister);
-
- if (isRegPairType(varDsc->lvType))
- {
- /* Check for the case of a variable that was narrowed to an int */
-
- if (isRegPairType(tree->gtType))
- {
- genMarkTreeInRegPair(tree, gen2regs2pair(varDsc->lvRegNum, varDsc->lvOtherReg));
- return;
- }
-
- noway_assert(tree->gtFlags & GTF_VAR_CAST);
- noway_assert(tree->gtType == TYP_INT);
- }
- else
- {
- noway_assert(!isRegPairType(tree->gtType));
- }
-
- /* It's a register variable -- modify the node */
-
- unsigned livenessFlags = (tree->gtFlags & GTF_LIVENESS_MASK);
-
- ValueNumPair vnp = tree->gtVNPair; // Save the ValueNumPair
- tree->SetOper(GT_REG_VAR);
- tree->gtVNPair = vnp; // Preserve the ValueNumPair, as SetOper will clear it.
-
- tree->gtFlags |= livenessFlags;
- tree->SetInReg();
- tree->gtRegNum = varDsc->lvRegNum;
- tree->gtRegVar.gtRegNum = varDsc->lvRegNum;
- tree->gtRegVar.SetLclNum(varNum);
-}
-
-// inline
-void CodeGen::saveLiveness(genLivenessSet* ls)
-{
- VarSetOps::Assign(compiler, ls->liveSet, compiler->compCurLife);
- VarSetOps::Assign(compiler, ls->varPtrSet, gcInfo.gcVarPtrSetCur);
- ls->maskVars = (regMaskSmall)regSet.rsMaskVars;
- ls->gcRefRegs = (regMaskSmall)gcInfo.gcRegGCrefSetCur;
- ls->byRefRegs = (regMaskSmall)gcInfo.gcRegByrefSetCur;
-}
-
-// inline
-void CodeGen::restoreLiveness(genLivenessSet* ls)
-{
- VarSetOps::Assign(compiler, compiler->compCurLife, ls->liveSet);
- VarSetOps::Assign(compiler, gcInfo.gcVarPtrSetCur, ls->varPtrSet);
- regSet.rsMaskVars = ls->maskVars;
- gcInfo.gcRegGCrefSetCur = ls->gcRefRegs;
- gcInfo.gcRegByrefSetCur = ls->byRefRegs;
-}
-
-// inline
-void CodeGen::checkLiveness(genLivenessSet* ls)
-{
- assert(VarSetOps::Equal(compiler, compiler->compCurLife, ls->liveSet));
- assert(VarSetOps::Equal(compiler, gcInfo.gcVarPtrSetCur, ls->varPtrSet));
- assert(regSet.rsMaskVars == ls->maskVars);
- assert(gcInfo.gcRegGCrefSetCur == ls->gcRefRegs);
- assert(gcInfo.gcRegByrefSetCur == ls->byRefRegs);
-}
-
-// inline
-bool CodeGenInterface::genMarkLclVar(GenTree* tree)
-{
- unsigned varNum;
- LclVarDsc* varDsc;
-
- assert(tree->gtOper == GT_LCL_VAR);
-
- /* Does the variable live in a register? */
-
- varNum = tree->gtLclVarCommon.gtLclNum;
- assert(varNum < compiler->lvaCount);
- varDsc = compiler->lvaTable + varNum;
-
- // Retype byref-typed appearances of intptr-typed lclVars as type intptr.
- if ((varDsc->TypeGet() == TYP_I_IMPL) && (tree->TypeGet() == TYP_BYREF))
- {
- tree->gtType = TYP_I_IMPL;
- }
-
- if (varDsc->lvRegister)
- {
- genBashLclVar(tree, varNum, varDsc);
- return true;
- }
- else
- {
- return false;
- }
-}
-
-// inline
-GenTree* CodeGen::genGetAddrModeBase(GenTree* tree)
-{
- bool rev;
- unsigned mul;
- unsigned cns;
- GenTree* adr;
- GenTree* idx;
-
- if (genCreateAddrMode(tree, // address
- 0, // mode
- false, // fold
- RBM_NONE, // reg mask
- &rev, // reverse ops
- &adr, // base addr
- &idx, // index val
-#if SCALED_ADDR_MODES
- &mul, // scaling
-#endif
- &cns, // displacement
- true)) // don't generate code
- return adr;
- else
- return NULL;
-}
-
-#if FEATURE_STACK_FP_X87
-// inline
-void CodeGenInterface::genResetFPstkLevel(unsigned newValue /* = 0 */)
-{
- genFPstkLevel = newValue;
-}
-
-// inline
-unsigned CodeGenInterface::genGetFPstkLevel()
-{
- return genFPstkLevel;
-}
-
-// inline
-void CodeGenInterface::genIncrementFPstkLevel(unsigned inc /* = 1 */)
-{
- noway_assert((inc == 0) || genFPstkLevel + inc > genFPstkLevel);
- genFPstkLevel += inc;
-}
-
-// inline
-void CodeGenInterface::genDecrementFPstkLevel(unsigned dec /* = 1 */)
-{
- noway_assert((dec == 0) || genFPstkLevel - dec < genFPstkLevel);
- genFPstkLevel -= dec;
-}
-
-#endif // FEATURE_STACK_FP_X87
-
-/*****************************************************************************
- *
- * Generate code that will set the given register to the integer constant.
- */
-
-void CodeGen::genSetRegToIcon(regNumber reg, ssize_t val, var_types type, insFlags flags)
-{
- noway_assert(type != TYP_REF || val == NULL);
-
- /* Does the reg already hold this constant? */
-
- if (!regTracker.rsIconIsInReg(val, reg))
- {
- if (val == 0)
- {
- instGen_Set_Reg_To_Zero(emitActualTypeSize(type), reg, flags);
- }
-#ifdef _TARGET_ARM_
- // If we can set a register to a constant with a small encoding, then do that.
- else if (arm_Valid_Imm_For_Small_Mov(reg, val, flags))
- {
- instGen_Set_Reg_To_Imm(emitActualTypeSize(type), reg, val, flags);
- }
-#endif
- else
- {
- /* See if a register holds the value or a close value? */
- bool constantLoaded = false;
- ssize_t delta;
- regNumber srcReg = regTracker.rsIconIsInReg(val, &delta);
-
- if (srcReg != REG_NA)
- {
- if (delta == 0)
- {
- inst_RV_RV(INS_mov, reg, srcReg, type, emitActualTypeSize(type), flags);
- constantLoaded = true;
- }
- else
- {
-#if defined(_TARGET_XARCH_)
- /* delta should fit inside a byte */
- if (delta == (signed char)delta)
- {
- /* use an lea instruction to set reg */
- getEmitter()->emitIns_R_AR(INS_lea, emitTypeSize(type), reg, srcReg, (int)delta);
- constantLoaded = true;
- }
-#elif defined(_TARGET_ARM_)
- /* We found a register 'regS' that has the value we need, modulo a small delta.
- That is, the value we need is 'regS + delta'.
- We one to generate one of the following instructions, listed in order of preference:
-
- adds regD, delta ; 2 bytes. if regD == regS, regD is a low register, and
- 0<=delta<=255
- subs regD, delta ; 2 bytes. if regD == regS, regD is a low register, and
- -255<=delta<=0
- adds regD, regS, delta ; 2 bytes. if regD and regS are low registers and 0<=delta<=7
- subs regD, regS, delta ; 2 bytes. if regD and regS are low registers and -7<=delta<=0
- mov regD, icon ; 4 bytes. icon is a wacky Thumb 12-bit immediate.
- movw regD, icon ; 4 bytes. 0<=icon<=65535
- add.w regD, regS, delta ; 4 bytes. delta is a wacky Thumb 12-bit immediate.
- sub.w regD, regS, delta ; 4 bytes. delta is a wacky Thumb 12-bit immediate.
- addw regD, regS, delta ; 4 bytes. 0<=delta<=4095
- subw regD, regS, delta ; 4 bytes. -4095<=delta<=0
-
- If it wasn't for the desire to generate the "mov reg,icon" forms if possible (and no bigger
- than necessary), this would be a lot simpler. Note that we might set the overflow flag: we
- can have regS containing the largest signed int 0x7fffffff and need the smallest signed int
- 0x80000000. In this case, delta will be 1.
- */
-
- bool useAdd = false;
- regMaskTP regMask = genRegMask(reg);
- regMaskTP srcRegMask = genRegMask(srcReg);
-
- if ((flags != INS_FLAGS_NOT_SET) && (reg == srcReg) && (regMask & RBM_LOW_REGS) &&
- (unsigned_abs(delta) <= 255))
- {
- useAdd = true;
- }
- else if ((flags != INS_FLAGS_NOT_SET) && (regMask & RBM_LOW_REGS) && (srcRegMask & RBM_LOW_REGS) &&
- (unsigned_abs(delta) <= 7))
- {
- useAdd = true;
- }
- else if (arm_Valid_Imm_For_Mov(val))
- {
- // fall through to general "!constantLoaded" case below
- }
- else if (arm_Valid_Imm_For_Add(delta, flags))
- {
- useAdd = true;
- }
-
- if (useAdd)
- {
- getEmitter()->emitIns_R_R_I(INS_add, EA_4BYTE, reg, srcReg, delta, flags);
- constantLoaded = true;
- }
-#else
- assert(!"Codegen missing");
-#endif
- }
- }
-
- if (!constantLoaded) // Have we loaded it yet?
- {
-#ifdef _TARGET_X86_
- if (val == -1)
- {
- /* or reg,-1 takes 3 bytes */
- inst_RV_IV(INS_OR, reg, val, emitActualTypeSize(type));
- }
- else
- /* For SMALL_CODE it is smaller to push a small immediate and
- then pop it into the dest register */
- if ((compiler->compCodeOpt() == Compiler::SMALL_CODE) && val == (signed char)val)
- {
- /* "mov" has no s(sign)-bit and so always takes 6 bytes,
- whereas push+pop takes 2+1 bytes */
-
- inst_IV(INS_push, val);
- genSinglePush();
-
- inst_RV(INS_pop, reg, type);
- genSinglePop();
- }
- else
-#endif // _TARGET_X86_
- {
- instGen_Set_Reg_To_Imm(emitActualTypeSize(type), reg, val, flags);
- }
- }
- }
- }
- regTracker.rsTrackRegIntCns(reg, val);
- gcInfo.gcMarkRegPtrVal(reg, type);
-}
-
-/*****************************************************************************
- *
- * Find an existing register set to the given integer constant, or
- * pick a register and generate code that will set it to the integer constant.
- *
- * If no existing register is set to the constant, it will use regSet.rsPickReg(regBest)
- * to pick some register to set. NOTE that this means the returned regNumber
- * might *not* be in regBest. It also implies that you should lock any registers
- * you don't want spilled (not just mark as used).
- *
- */
-
-regNumber CodeGen::genGetRegSetToIcon(ssize_t val, regMaskTP regBest /* = 0 */, var_types type /* = TYP_INT */)
-{
- regNumber regCns;
-#if REDUNDANT_LOAD
-
- // Is there already a register with zero that we can use?
- regCns = regTracker.rsIconIsInReg(val);
-
- if (regCns == REG_NA)
-#endif
- {
- // If not, grab a register to hold the constant, preferring
- // any register besides RBM_TMP_0 so it can hopefully be re-used
- regCns = regSet.rsPickReg(regBest, regBest & ~RBM_TMP_0);
-
- // Now set the constant
- genSetRegToIcon(regCns, val, type);
- }
-
- // NOTE: there is guarantee that regCns is in regBest's mask
- return regCns;
-}
-
-/*****************************************************************************/
-/*****************************************************************************
- *
- * Add the given constant to the specified register.
- * 'tree' is the resulting tree
- */
-
-void CodeGen::genIncRegBy(regNumber reg, ssize_t ival, GenTree* tree, var_types dstType, bool ovfl)
-{
- bool setFlags = (tree != NULL) && tree->gtSetFlags();
-
-#ifdef _TARGET_XARCH_
- /* First check to see if we can generate inc or dec instruction(s) */
- /* But avoid inc/dec on P4 in general for fast code or inside loops for blended code */
- if (!ovfl && !compiler->optAvoidIncDec(compiler->compCurBB->getBBWeight(compiler)))
- {
- emitAttr size = emitTypeSize(dstType);
-
- switch (ival)
- {
- case 2:
- inst_RV(INS_inc, reg, dstType, size);
- __fallthrough;
- case 1:
- inst_RV(INS_inc, reg, dstType, size);
-
- goto UPDATE_LIVENESS;
-
- case -2:
- inst_RV(INS_dec, reg, dstType, size);
- __fallthrough;
- case -1:
- inst_RV(INS_dec, reg, dstType, size);
-
- goto UPDATE_LIVENESS;
- }
- }
-#endif
- {
- insFlags flags = setFlags ? INS_FLAGS_SET : INS_FLAGS_DONT_CARE;
- inst_RV_IV(INS_add, reg, ival, emitActualTypeSize(dstType), flags);
- }
-
-#ifdef _TARGET_XARCH_
-UPDATE_LIVENESS:
-#endif
-
- if (setFlags)
- genFlagsEqualToReg(tree, reg);
-
- regTracker.rsTrackRegTrash(reg);
-
- gcInfo.gcMarkRegSetNpt(genRegMask(reg));
-
- if (tree != NULL)
- {
- if (!tree->OperIsAssignment())
- {
- genMarkTreeInReg(tree, reg);
- if (varTypeIsGC(tree->TypeGet()))
- gcInfo.gcMarkRegSetByref(genRegMask(reg));
- }
- }
-}
-
-/*****************************************************************************
- *
- * Subtract the given constant from the specified register.
- * Should only be used for unsigned sub with overflow. Else
- * genIncRegBy() can be used using -ival. We shouldn't use genIncRegBy()
- * for these cases as the flags are set differently, and the following
- * check for overflow won't work correctly.
- * 'tree' is the resulting tree.
- */
-
-void CodeGen::genDecRegBy(regNumber reg, ssize_t ival, GenTree* tree)
-{
- noway_assert((tree->gtFlags & GTF_OVERFLOW) &&
- ((tree->gtFlags & GTF_UNSIGNED) || ival == ((tree->gtType == TYP_INT) ? INT32_MIN : SSIZE_T_MIN)));
- noway_assert(tree->gtType == TYP_INT || tree->gtType == TYP_I_IMPL);
-
- regTracker.rsTrackRegTrash(reg);
-
- noway_assert(!varTypeIsGC(tree->TypeGet()));
- gcInfo.gcMarkRegSetNpt(genRegMask(reg));
-
- insFlags flags = tree->gtSetFlags() ? INS_FLAGS_SET : INS_FLAGS_DONT_CARE;
- inst_RV_IV(INS_sub, reg, ival, emitActualTypeSize(tree->TypeGet()), flags);
-
- if (tree->gtSetFlags())
- genFlagsEqualToReg(tree, reg);
-
- if (tree)
- {
- genMarkTreeInReg(tree, reg);
- }
-}
-
-/*****************************************************************************
- *
- * Multiply the specified register by the given value.
- * 'tree' is the resulting tree
- */
-
-void CodeGen::genMulRegBy(regNumber reg, ssize_t ival, GenTree* tree, var_types dstType, bool ovfl)
-{
- noway_assert(genActualType(dstType) == TYP_INT || genActualType(dstType) == TYP_I_IMPL);
-
- regTracker.rsTrackRegTrash(reg);
-
- if (tree)
- {
- genMarkTreeInReg(tree, reg);
- }
-
- bool use_shift = false;
- unsigned shift_by = 0;
-
- if ((dstType >= TYP_INT) && !ovfl && (ival > 0) && ((ival & (ival - 1)) == 0))
- {
- use_shift = true;
- BitScanForwardPtr((ULONG*)&shift_by, (ULONG)ival);
- }
-
- if (use_shift)
- {
- if (shift_by != 0)
- {
- insFlags flags = tree->gtSetFlags() ? INS_FLAGS_SET : INS_FLAGS_DONT_CARE;
- inst_RV_SH(INS_SHIFT_LEFT_LOGICAL, emitTypeSize(dstType), reg, shift_by, flags);
- if (tree->gtSetFlags())
- genFlagsEqualToReg(tree, reg);
- }
- }
- else
- {
- instruction ins;
-#ifdef _TARGET_XARCH_
- ins = getEmitter()->inst3opImulForReg(reg);
-#else
- ins = INS_mul;
-#endif
-
- inst_RV_IV(ins, reg, ival, emitActualTypeSize(dstType));
- }
-}
-
-/*****************************************************************************/
-/*****************************************************************************/
-/*****************************************************************************
- *
- * Compute the value 'tree' into a register that's in 'needReg'
- * (or any free register if 'needReg' is RBM_NONE).
- *
- * Note that 'needReg' is just a recommendation unless mustReg==RegSet::EXACT_REG.
- * If keepReg==RegSet::KEEP_REG, we mark the register as being used.
- *
- * If you require that the register returned is trashable, pass true for 'freeOnly'.
- */
-
-void CodeGen::genComputeReg(
- GenTree* tree, regMaskTP needReg, RegSet::ExactReg mustReg, RegSet::KeepReg keepReg, bool freeOnly)
-{
- noway_assert(tree->gtType != TYP_VOID);
-
- regNumber reg;
- regNumber rg2;
-
-#if FEATURE_STACK_FP_X87
- noway_assert(genActualType(tree->gtType) == TYP_INT || genActualType(tree->gtType) == TYP_I_IMPL ||
- genActualType(tree->gtType) == TYP_REF || tree->gtType == TYP_BYREF);
-#elif defined(_TARGET_ARM_)
- noway_assert(genActualType(tree->gtType) == TYP_INT || genActualType(tree->gtType) == TYP_I_IMPL ||
- genActualType(tree->gtType) == TYP_REF || tree->gtType == TYP_BYREF ||
- genActualType(tree->gtType) == TYP_FLOAT || genActualType(tree->gtType) == TYP_DOUBLE ||
- genActualType(tree->gtType) == TYP_STRUCT);
-#else
- noway_assert(genActualType(tree->gtType) == TYP_INT || genActualType(tree->gtType) == TYP_I_IMPL ||
- genActualType(tree->gtType) == TYP_REF || tree->gtType == TYP_BYREF ||
- genActualType(tree->gtType) == TYP_FLOAT || genActualType(tree->gtType) == TYP_DOUBLE);
-#endif
-
- /* Generate the value, hopefully into the right register */
-
- genCodeForTree(tree, needReg);
- noway_assert(tree->InReg());
-
- // There is a workaround in genCodeForTreeLng() that changes the type of the
- // tree of a GT_MUL with 64 bit result to TYP_INT from TYP_LONG, then calls
- // genComputeReg(). genCodeForTree(), above, will put the result in gtRegPair for ARM,
- // or leave it in EAX/EDX for x86, but only set EAX as gtRegNum. There's no point
- // running the rest of this code, because anything looking at gtRegNum on ARM or
- // attempting to move from EAX/EDX will be wrong.
- if ((tree->OperGet() == GT_MUL) && (tree->gtFlags & GTF_MUL_64RSLT))
- goto REG_OK;
-
- reg = tree->gtRegNum;
-
- /* Did the value end up in an acceptable register? */
-
- if ((mustReg == RegSet::EXACT_REG) && needReg && !(genRegMask(reg) & needReg))
- {
- /* Not good enough to satisfy the caller's orders */
-
- if (varTypeIsFloating(tree))
- {
- RegSet::RegisterPreference pref(needReg, RBM_NONE);
- rg2 = regSet.PickRegFloat(tree->TypeGet(), &pref);
- }
- else
- {
- rg2 = regSet.rsGrabReg(needReg);
- }
- }
- else
- {
- /* Do we have to end up with a free register? */
-
- if (!freeOnly)
- goto REG_OK;
-
- /* Did we luck out and the value got computed into an unused reg? */
-
- if (genRegMask(reg) & regSet.rsRegMaskFree())
- goto REG_OK;
-
- /* Register already in use, so spill previous value */
-
- if ((mustReg == RegSet::EXACT_REG) && needReg && (genRegMask(reg) & needReg))
- {
- rg2 = regSet.rsGrabReg(needReg);
- if (rg2 == reg)
- {
- gcInfo.gcMarkRegPtrVal(reg, tree->TypeGet());
- tree->gtRegNum = reg;
- goto REG_OK;
- }
- }
- else
- {
- /* OK, let's find a trashable home for the value */
-
- regMaskTP rv1RegUsed;
-
- regSet.rsLockReg(genRegMask(reg), &rv1RegUsed);
- rg2 = regSet.rsPickReg(needReg);
- regSet.rsUnlockReg(genRegMask(reg), rv1RegUsed);
- }
- }
-
- noway_assert(reg != rg2);
-
- /* Update the value in the target register */
-
- regTracker.rsTrackRegCopy(rg2, reg);
-
- inst_RV_RV(ins_Copy(tree->TypeGet()), rg2, reg, tree->TypeGet());
-
- /* The value has been transferred to 'reg' */
-
- if ((genRegMask(reg) & regSet.rsMaskUsed) == 0)
- gcInfo.gcMarkRegSetNpt(genRegMask(reg));
-
- gcInfo.gcMarkRegPtrVal(rg2, tree->TypeGet());
-
- /* The value is now in an appropriate register */
-
- tree->gtRegNum = rg2;
-
-REG_OK:
-
- /* Does the caller want us to mark the register as used? */
-
- if (keepReg == RegSet::KEEP_REG)
- {
- /* In case we're computing a value into a register variable */
-
- genUpdateLife(tree);
-
- /* Mark the register as 'used' */
-
- regSet.rsMarkRegUsed(tree);
- }
-}
-
-/*****************************************************************************
- *
- * Same as genComputeReg(), the only difference being that the result is
- * guaranteed to end up in a trashable register.
- */
-
-// inline
-void CodeGen::genCompIntoFreeReg(GenTree* tree, regMaskTP needReg, RegSet::KeepReg keepReg)
-{
- genComputeReg(tree, needReg, RegSet::ANY_REG, keepReg, true);
-}
-
-/*****************************************************************************
- *
- * The value 'tree' was earlier computed into a register; free up that
- * register (but also make sure the value is presently in a register).
- */
-
-void CodeGen::genReleaseReg(GenTree* tree)
-{
- if (tree->gtFlags & GTF_SPILLED)
- {
- /* The register has been spilled -- reload it */
-
- regSet.rsUnspillReg(tree, 0, RegSet::FREE_REG);
- return;
- }
-
- regSet.rsMarkRegFree(genRegMask(tree->gtRegNum));
-}
-
-/*****************************************************************************
- *
- * The value 'tree' was earlier computed into a register. Check whether that
- * register has been spilled (and reload it if so), and if 'keepReg' is RegSet::FREE_REG,
- * free the register. The caller shouldn't need to be setting GCness of the register
- * where tree will be recovered to, so we disallow keepReg==RegSet::FREE_REG for GC type trees.
- */
-
-void CodeGen::genRecoverReg(GenTree* tree, regMaskTP needReg, RegSet::KeepReg keepReg)
-{
- if (tree->gtFlags & GTF_SPILLED)
- {
- /* The register has been spilled -- reload it */
-
- regSet.rsUnspillReg(tree, needReg, keepReg);
- return;
- }
- else if (needReg && (needReg & genRegMask(tree->gtRegNum)) == 0)
- {
- /* We need the tree in another register. So move it there */
-
- noway_assert(tree->InReg());
- regNumber oldReg = tree->gtRegNum;
-
- /* Pick an acceptable register */
-
- regNumber reg = regSet.rsGrabReg(needReg);
-
- /* Copy the value */
-
- inst_RV_RV(INS_mov, reg, oldReg, tree->TypeGet());
- tree->gtRegNum = reg;
-
- gcInfo.gcMarkRegPtrVal(tree);
- regSet.rsMarkRegUsed(tree);
- regSet.rsMarkRegFree(oldReg, tree);
-
- regTracker.rsTrackRegCopy(reg, oldReg);
- }
-
- /* Free the register if the caller desired so */
-
- if (keepReg == RegSet::FREE_REG)
- {
- regSet.rsMarkRegFree(genRegMask(tree->gtRegNum));
- // Can't use RegSet::FREE_REG on a GC type
- noway_assert(!varTypeIsGC(tree->gtType));
- }
- else
- {
- noway_assert(regSet.rsMaskUsed & genRegMask(tree->gtRegNum));
- }
-}
-
-/*****************************************************************************
- *
- * Move one half of a register pair to its new regPair(half).
- */
-
-// inline
-void CodeGen::genMoveRegPairHalf(GenTree* tree, regNumber dst, regNumber src, int off)
-{
- if (src == REG_STK)
- {
- // handle long to unsigned long overflow casts
- while (tree->gtOper == GT_CAST)
- {
- noway_assert(tree->gtType == TYP_LONG);
- tree = tree->gtCast.CastOp();
- }
- noway_assert(tree->gtEffectiveVal()->gtOper == GT_LCL_VAR);
- noway_assert(tree->gtType == TYP_LONG);
- inst_RV_TT(ins_Load(TYP_INT), dst, tree, off);
- regTracker.rsTrackRegTrash(dst);
- }
- else
- {
- regTracker.rsTrackRegCopy(dst, src);
- inst_RV_RV(INS_mov, dst, src, TYP_INT);
- }
-}
-
-/*****************************************************************************
- *
- * The given long value is in a register pair, but it's not an acceptable
- * one. We have to move the value into a register pair in 'needReg' (if
- * non-zero) or the pair 'newPair' (when 'newPair != REG_PAIR_NONE').
- *
- * Important note: if 'needReg' is non-zero, we assume the current pair
- * has not been marked as free. If, OTOH, 'newPair' is specified, we
- * assume that the current register pair is marked as used and free it.
- */
-
-void CodeGen::genMoveRegPair(GenTree* tree, regMaskTP needReg, regPairNo newPair)
-{
- regPairNo oldPair;
-
- regNumber oldLo;
- regNumber oldHi;
- regNumber newLo;
- regNumber newHi;
-
- /* Either a target set or a specific pair may be requested */
-
- noway_assert((needReg != 0) != (newPair != REG_PAIR_NONE));
-
- /* Get hold of the current pair */
-
- oldPair = tree->gtRegPair;
- noway_assert(oldPair != newPair);
-
- /* Are we supposed to move to a specific pair? */
-
- if (newPair != REG_PAIR_NONE)
- {
- regMaskTP oldMask = genRegPairMask(oldPair);
- regMaskTP loMask = genRegMask(genRegPairLo(newPair));
- regMaskTP hiMask = genRegMask(genRegPairHi(newPair));
- regMaskTP overlap = oldMask & (loMask | hiMask);
-
- /* First lock any registers that are in both pairs */
-
- noway_assert((regSet.rsMaskUsed & overlap) == overlap);
- noway_assert((regSet.rsMaskLock & overlap) == 0);
- regSet.rsMaskLock |= overlap;
-
- /* Make sure any additional registers we need are free */
-
- if ((loMask & regSet.rsMaskUsed) != 0 && (loMask & oldMask) == 0)
- {
- regSet.rsGrabReg(loMask);
- }
-
- if ((hiMask & regSet.rsMaskUsed) != 0 && (hiMask & oldMask) == 0)
- {
- regSet.rsGrabReg(hiMask);
- }
-
- /* Unlock those registers we have temporarily locked */
-
- noway_assert((regSet.rsMaskUsed & overlap) == overlap);
- noway_assert((regSet.rsMaskLock & overlap) == overlap);
- regSet.rsMaskLock -= overlap;
-
- /* We can now free the old pair */
-
- regSet.rsMarkRegFree(oldMask);
- }
- else
- {
- /* Pick the new pair based on the caller's stated preference */
-
- newPair = regSet.rsGrabRegPair(needReg);
- }
-
- // If grabbed pair is the same as old one we're done
- if (newPair == oldPair)
- {
- noway_assert((oldLo = genRegPairLo(oldPair), oldHi = genRegPairHi(oldPair), newLo = genRegPairLo(newPair),
- newHi = genRegPairHi(newPair), newLo != REG_STK && newHi != REG_STK));
- return;
- }
-
- /* Move the values from the old pair into the new one */
-
- oldLo = genRegPairLo(oldPair);
- oldHi = genRegPairHi(oldPair);
- newLo = genRegPairLo(newPair);
- newHi = genRegPairHi(newPair);
-
- noway_assert(newLo != REG_STK && newHi != REG_STK);
-
- /* Careful - the register pairs might overlap */
-
- if (newLo == oldLo)
- {
- /* The low registers are identical, just move the upper half */
-
- noway_assert(newHi != oldHi);
- genMoveRegPairHalf(tree, newHi, oldHi, sizeof(int));
- }
- else
- {
- /* The low registers are different, are the upper ones the same? */
-
- if (newHi == oldHi)
- {
- /* Just move the lower half, then */
- genMoveRegPairHalf(tree, newLo, oldLo, 0);
- }
- else
- {
- /* Both sets are different - is there an overlap? */
-
- if (newLo == oldHi)
- {
- /* Are high and low simply swapped ? */
-
- if (newHi == oldLo)
- {
-#ifdef _TARGET_ARM_
- /* Let's use XOR swap to reduce register pressure. */
- inst_RV_RV(INS_eor, oldLo, oldHi);
- inst_RV_RV(INS_eor, oldHi, oldLo);
- inst_RV_RV(INS_eor, oldLo, oldHi);
-#else
- inst_RV_RV(INS_xchg, oldHi, oldLo);
-#endif
- regTracker.rsTrackRegSwap(oldHi, oldLo);
- }
- else
- {
- /* New lower == old higher, so move higher half first */
-
- noway_assert(newHi != oldLo);
- genMoveRegPairHalf(tree, newHi, oldHi, sizeof(int));
- genMoveRegPairHalf(tree, newLo, oldLo, 0);
- }
- }
- else
- {
- /* Move lower half first */
- genMoveRegPairHalf(tree, newLo, oldLo, 0);
- genMoveRegPairHalf(tree, newHi, oldHi, sizeof(int));
- }
- }
- }
-
- /* Record the fact that we're switching to another pair */
-
- tree->gtRegPair = newPair;
-}
-
-/*****************************************************************************
- *
- * Compute the value 'tree' into the register pair specified by 'needRegPair'
- * if 'needRegPair' is REG_PAIR_NONE then use any free register pair, avoid
- * those in avoidReg.
- * If 'keepReg' is set to RegSet::KEEP_REG then we mark both registers that the
- * value ends up in as being used.
- */
-
-void CodeGen::genComputeRegPair(
- GenTree* tree, regPairNo needRegPair, regMaskTP avoidReg, RegSet::KeepReg keepReg, bool freeOnly)
-{
- regMaskTP regMask;
- regPairNo regPair;
- regMaskTP tmpMask;
- regMaskTP tmpUsedMask;
- regNumber rLo;
- regNumber rHi;
-
- noway_assert(isRegPairType(tree->gtType));
-
- if (needRegPair == REG_PAIR_NONE)
- {
- if (freeOnly)
- {
- regMask = regSet.rsRegMaskFree() & ~avoidReg;
- if (genMaxOneBit(regMask))
- regMask = regSet.rsRegMaskFree();
- }
- else
- {
- regMask = RBM_ALLINT & ~avoidReg;
- }
-
- if (genMaxOneBit(regMask))
- regMask = regSet.rsRegMaskCanGrab();
- }
- else
- {
- regMask = genRegPairMask(needRegPair);
- }
-
- /* Generate the value, hopefully into the right register pair */
-
- genCodeForTreeLng(tree, regMask, avoidReg);
-
- noway_assert(tree->InReg());
-
- regPair = tree->gtRegPair;
- tmpMask = genRegPairMask(regPair);
-
- rLo = genRegPairLo(regPair);
- rHi = genRegPairHi(regPair);
-
- /* At least one half is in a real register */
-
- noway_assert(rLo != REG_STK || rHi != REG_STK);
-
- /* Did the value end up in an acceptable register pair? */
-
- if (needRegPair != REG_PAIR_NONE)
- {
- if (needRegPair != regPair)
- {
- /* This is a workaround. If we specify a regPair for genMoveRegPair */
- /* it expects the source pair being marked as used */
- regSet.rsMarkRegPairUsed(tree);
- genMoveRegPair(tree, 0, needRegPair);
- }
- }
- else if (freeOnly)
- {
- /* Do we have to end up with a free register pair?
- Something might have gotten freed up above */
- bool mustMoveReg = false;
-
- regMask = regSet.rsRegMaskFree() & ~avoidReg;
-
- if (genMaxOneBit(regMask))
- regMask = regSet.rsRegMaskFree();
-
- if ((tmpMask & regMask) != tmpMask || rLo == REG_STK || rHi == REG_STK)
- {
- /* Note that we must call genMoveRegPair if one of our registers
- comes from the used mask, so that it will be properly spilled. */
-
- mustMoveReg = true;
- }
-
- if (genMaxOneBit(regMask))
- regMask |= regSet.rsRegMaskCanGrab() & ~avoidReg;
-
- if (genMaxOneBit(regMask))
- regMask |= regSet.rsRegMaskCanGrab();
-
- /* Did the value end up in a free register pair? */
-
- if (mustMoveReg)
- {
- /* We'll have to move the value to a free (trashable) pair */
- genMoveRegPair(tree, regMask, REG_PAIR_NONE);
- }
- }
- else
- {
- noway_assert(needRegPair == REG_PAIR_NONE);
- noway_assert(!freeOnly);
-
- /* it is possible to have tmpMask also in the regSet.rsMaskUsed */
- tmpUsedMask = tmpMask & regSet.rsMaskUsed;
- tmpMask &= ~regSet.rsMaskUsed;
-
- /* Make sure that the value is in "real" registers*/
- if (rLo == REG_STK)
- {
- /* Get one of the desired registers, but exclude rHi */
-
- regSet.rsLockReg(tmpMask);
- regSet.rsLockUsedReg(tmpUsedMask);
-
- regNumber reg = regSet.rsPickReg(regMask);
-
- regSet.rsUnlockUsedReg(tmpUsedMask);
- regSet.rsUnlockReg(tmpMask);
-
- inst_RV_TT(ins_Load(TYP_INT), reg, tree, 0);
-
- tree->gtRegPair = gen2regs2pair(reg, rHi);
-
- regTracker.rsTrackRegTrash(reg);
- gcInfo.gcMarkRegSetNpt(genRegMask(reg));
- }
- else if (rHi == REG_STK)
- {
- /* Get one of the desired registers, but exclude rLo */
-
- regSet.rsLockReg(tmpMask);
- regSet.rsLockUsedReg(tmpUsedMask);
-
- regNumber reg = regSet.rsPickReg(regMask);
-
- regSet.rsUnlockUsedReg(tmpUsedMask);
- regSet.rsUnlockReg(tmpMask);
-
- inst_RV_TT(ins_Load(TYP_INT), reg, tree, 4);
-
- tree->gtRegPair = gen2regs2pair(rLo, reg);
-
- regTracker.rsTrackRegTrash(reg);
- gcInfo.gcMarkRegSetNpt(genRegMask(reg));
- }
- }
-
- /* Does the caller want us to mark the register as used? */
-
- if (keepReg == RegSet::KEEP_REG)
- {
- /* In case we're computing a value into a register variable */
-
- genUpdateLife(tree);
-
- /* Mark the register as 'used' */
-
- regSet.rsMarkRegPairUsed(tree);
- }
-}
-
-/*****************************************************************************
- *
- * Same as genComputeRegPair(), the only difference being that the result
- * is guaranteed to end up in a trashable register pair.
- */
-
-// inline
-void CodeGen::genCompIntoFreeRegPair(GenTree* tree, regMaskTP avoidReg, RegSet::KeepReg keepReg)
-{
- genComputeRegPair(tree, REG_PAIR_NONE, avoidReg, keepReg, true);
-}
-
-/*****************************************************************************
- *
- * The value 'tree' was earlier computed into a register pair; free up that
- * register pair (but also make sure the value is presently in a register
- * pair).
- */
-
-void CodeGen::genReleaseRegPair(GenTree* tree)
-{
- if (tree->gtFlags & GTF_SPILLED)
- {
- /* The register has been spilled -- reload it */
-
- regSet.rsUnspillRegPair(tree, 0, RegSet::FREE_REG);
- return;
- }
-
- regSet.rsMarkRegFree(genRegPairMask(tree->gtRegPair));
-}
-
-/*****************************************************************************
- *
- * The value 'tree' was earlier computed into a register pair. Check whether
- * either register of that pair has been spilled (and reload it if so), and
- * if 'keepReg' is 0, free the register pair.
- */
-
-void CodeGen::genRecoverRegPair(GenTree* tree, regPairNo regPair, RegSet::KeepReg keepReg)
-{
- if (tree->gtFlags & GTF_SPILLED)
- {
- regMaskTP regMask;
-
- if (regPair == REG_PAIR_NONE)
- regMask = RBM_NONE;
- else
- regMask = genRegPairMask(regPair);
-
- /* The register pair has been spilled -- reload it */
-
- regSet.rsUnspillRegPair(tree, regMask, RegSet::KEEP_REG);
- }
-
- /* Does the caller insist on the value being in a specific place? */
-
- if (regPair != REG_PAIR_NONE && regPair != tree->gtRegPair)
- {
- /* No good -- we'll have to move the value to a new place */
-
- genMoveRegPair(tree, 0, regPair);
-
- /* Mark the pair as used if appropriate */
-
- if (keepReg == RegSet::KEEP_REG)
- regSet.rsMarkRegPairUsed(tree);
-
- return;
- }
-
- /* Free the register pair if the caller desired so */
-
- if (keepReg == RegSet::FREE_REG)
- regSet.rsMarkRegFree(genRegPairMask(tree->gtRegPair));
-}
-
-/*****************************************************************************
- *
- * Compute the given long value into the specified register pair; don't mark
- * the register pair as used.
- */
-
-// inline
-void CodeGen::genEvalIntoFreeRegPair(GenTree* tree, regPairNo regPair, regMaskTP avoidReg)
-{
- genComputeRegPair(tree, regPair, avoidReg, RegSet::KEEP_REG);
- genRecoverRegPair(tree, regPair, RegSet::FREE_REG);
-}
-
-/*****************************************************************************
- * This helper makes sure that the regpair target of an assignment is
- * available for use. This needs to be called in genCodeForTreeLng just before
- * a long assignment, but must not be called until everything has been
- * evaluated, or else we might try to spill enregistered variables.
- *
- */
-
-// inline
-void CodeGen::genMakeRegPairAvailable(regPairNo regPair)
-{
- /* Make sure the target of the store is available */
-
- regNumber regLo = genRegPairLo(regPair);
- regNumber regHi = genRegPairHi(regPair);
-
- if ((regHi != REG_STK) && (regSet.rsMaskUsed & genRegMask(regHi)))
- regSet.rsSpillReg(regHi);
-
- if ((regLo != REG_STK) && (regSet.rsMaskUsed & genRegMask(regLo)))
- regSet.rsSpillReg(regLo);
-}
-
-/*****************************************************************************/
-/*****************************************************************************
- *
- * Return true if the given tree 'addr' can be computed via an addressing mode,
- * such as "[ebx+esi*4+20]". If the expression isn't an address mode already
- * try to make it so (but we don't try 'too hard' to accomplish this).
- *
- * If we end up needing a register (or two registers) to hold some part(s) of the
- * address, we return the use register mask via '*useMaskPtr'.
- *
- * If keepReg==RegSet::KEEP_REG, the registers (viz. *useMaskPtr) will be marked as
- * in use. The caller would then be responsible for calling
- * regSet.rsMarkRegFree(*useMaskPtr).
- *
- * If keepReg==RegSet::FREE_REG, then the caller needs update the GC-tracking by
- * calling genDoneAddressable(addr, *useMaskPtr, RegSet::FREE_REG);
- */
-
-bool CodeGen::genMakeIndAddrMode(GenTree* addr,
- GenTree* oper,
- bool forLea,
- regMaskTP regMask,
- RegSet::KeepReg keepReg,
- regMaskTP* useMaskPtr,
- bool deferOK)
-{
- if (addr->gtOper == GT_ARR_ELEM)
- {
- regMaskTP regs = genMakeAddrArrElem(addr, oper, RBM_ALLINT, keepReg);
- *useMaskPtr = regs;
- return true;
- }
-
- bool rev;
- GenTree* rv1;
- GenTree* rv2;
- bool operIsArrIndex; // is oper an array index
- GenTree* scaledIndex; // If scaled addressing mode can't be used
-
- regMaskTP anyMask = RBM_ALLINT;
-
- unsigned cns;
- unsigned mul;
-
- GenTree* tmp;
- int ixv = INT_MAX; // unset value
-
- GenTree* scaledIndexVal;
-
- regMaskTP newLiveMask;
- regMaskTP rv1Mask;
- regMaskTP rv2Mask;
-
- /* Deferred address mode forming NYI for x86 */
-
- noway_assert(deferOK == false);
-
- noway_assert(oper == NULL ||
- ((oper->OperIsIndir() || oper->OperIsAtomicOp()) &&
- ((oper->gtOper == GT_CMPXCHG && oper->gtCmpXchg.gtOpLocation == addr) || oper->gtOp.gtOp1 == addr)));
- operIsArrIndex = (oper != nullptr && oper->OperGet() == GT_IND && (oper->gtFlags & GTF_IND_ARR_INDEX) != 0);
-
- if (addr->gtOper == GT_LEA)
- {
- rev = (addr->gtFlags & GTF_REVERSE_OPS) != 0;
- GenTreeAddrMode* lea = addr->AsAddrMode();
- rv1 = lea->Base();
- rv2 = lea->Index();
- mul = lea->gtScale;
- cns = lea->gtOffset;
-
- if (rv1 != NULL && rv2 == NULL && cns == 0 && rv1->InReg())
- {
- scaledIndex = NULL;
- goto YES;
- }
- }
- else
- {
- // NOTE: FOR NOW THIS ISN'T APPROPRIATELY INDENTED - THIS IS TO MAKE IT
- // EASIER TO MERGE
-
- /* Is the complete address already sitting in a register? */
-
- if ((addr->InReg()) || (addr->gtOper == GT_LCL_VAR && genMarkLclVar(addr)))
- {
- genUpdateLife(addr);
-
- rv1 = addr;
- rv2 = scaledIndex = 0;
- cns = 0;
-
- goto YES;
- }
-
- /* Is it an absolute address */
-
- if (addr->IsCnsIntOrI())
- {
- rv1 = rv2 = scaledIndex = 0;
- // along this code path cns is never used, so place a BOGUS value in it as proof
- // cns = addr->gtIntCon.gtIconVal;
- cns = UINT_MAX;
-
- goto YES;
- }
-
- /* Is there a chance of forming an address mode? */
-
- if (!genCreateAddrMode(addr, forLea ? 1 : 0, false, regMask, &rev, &rv1, &rv2, &mul, &cns))
- {
- /* This better not be an array index */
- noway_assert(!operIsArrIndex);
-
- return false;
- }
- // THIS IS THE END OF THE INAPPROPRIATELY INDENTED SECTION
- }
-
- /* For scaled array access, RV2 may not be pointing to the index of the
- array if the CPU does not support the needed scaling factor. We will
- make it point to the actual index, and scaledIndex will point to
- the scaled value */
-
- scaledIndex = NULL;
- scaledIndexVal = NULL;
-
- if (operIsArrIndex && rv2 != NULL && (rv2->gtOper == GT_MUL || rv2->gtOper == GT_LSH) &&
- rv2->gtOp.gtOp2->IsIntCnsFitsInI32())
- {
- scaledIndex = rv2;
- compiler->optGetArrayRefScaleAndIndex(scaledIndex, &scaledIndexVal DEBUGARG(true));
-
- noway_assert(scaledIndex->gtOp.gtOp2->IsIntCnsFitsInI32());
- }
-
- /* Has the address already been computed? */
-
- if (addr->InReg())
- {
- if (forLea)
- return true;
-
- rv1 = addr;
- rv2 = NULL;
- scaledIndex = NULL;
- genUpdateLife(addr);
- goto YES;
- }
-
- /*
- Here we have the following operands:
-
- rv1 ..... base address
- rv2 ..... offset value (or NULL)
- mul ..... multiplier for rv2 (or 0)
- cns ..... additional constant (or 0)
-
- The first operand must be present (and be an address) unless we're
- computing an expression via 'LEA'. The scaled operand is optional,
- but must not be a pointer if present.
- */
-
- noway_assert(rv2 == NULL || !varTypeIsGC(rv2->TypeGet()));
-
- /*-------------------------------------------------------------------------
- *
- * Make sure both rv1 and rv2 (if present) are in registers
- *
- */
-
- // Trivial case : Is either rv1 or rv2 a NULL ?
-
- if (!rv2)
- {
- /* A single operand, make sure it's in a register */
-
- if (cns != 0)
- {
- // In the case where "rv1" is already in a register, there's no reason to get into a
- // register in "regMask" yet, if there's a non-zero constant that we're going to add;
- // if there is, we can do an LEA.
- genCodeForTree(rv1, RBM_NONE);
- }
- else
- {
- genCodeForTree(rv1, regMask);
- }
- goto DONE_REGS;
- }
- else if (!rv1)
- {
- /* A single (scaled) operand, make sure it's in a register */
-
- genCodeForTree(rv2, 0);
- goto DONE_REGS;
- }
-
- /* At this point, both rv1 and rv2 are non-NULL and we have to make sure
- they are in registers */
-
- noway_assert(rv1 && rv2);
-
- /* If we have to check a constant array index, compare it against
- the array dimension (see below) but then fold the index with a
- scaling factor (if any) and additional offset (if any).
- */
-
- if (rv2->gtOper == GT_CNS_INT || (scaledIndex != NULL && scaledIndexVal->gtOper == GT_CNS_INT))
- {
- if (scaledIndex != NULL)
- {
- assert(rv2 == scaledIndex && scaledIndexVal != NULL);
- rv2 = scaledIndexVal;
- }
- /* We must have a range-checked index operation */
-
- noway_assert(operIsArrIndex);
-
- /* Get hold of the index value and see if it's a constant */
-
- if (rv2->IsIntCnsFitsInI32())
- {
- ixv = (int)rv2->gtIntCon.gtIconVal;
- // Maybe I should just set "fold" true in the call to genMakeAddressable above.
- if (scaledIndex != NULL)
- {
- int scale = 1 << ((int)scaledIndex->gtOp.gtOp2->gtIntCon.gtIconVal); // If this truncates, that's OK --
- // multiple of 2^6.
- if (mul == 0)
- {
- mul = scale;
- }
- else
- {
- mul *= scale;
- }
- }
- rv2 = scaledIndex = NULL;
-
- /* Add the scaled index into the added value */
-
- if (mul)
- cns += ixv * mul;
- else
- cns += ixv;
-
- /* Make sure 'rv1' is in a register */
-
- genCodeForTree(rv1, regMask);
-
- goto DONE_REGS;
- }
- }
-
- if (rv1->InReg())
- {
- /* op1 already in register - how about op2? */
-
- if (rv2->InReg())
- {
- /* Great - both operands are in registers already. Just update
- the liveness and we are done. */
-
- if (rev)
- {
- genUpdateLife(rv2);
- genUpdateLife(rv1);
- }
- else
- {
- genUpdateLife(rv1);
- genUpdateLife(rv2);
- }
-
- goto DONE_REGS;
- }
-
- /* rv1 is in a register, but rv2 isn't */
-
- if (!rev)
- {
- /* rv1 is already materialized in a register. Just update liveness
- to rv1 and generate code for rv2 */
-
- genUpdateLife(rv1);
- regSet.rsMarkRegUsed(rv1, oper);
- }
-
- goto GEN_RV2;
- }
- else if (rv2->InReg())
- {
- /* rv2 is in a register, but rv1 isn't */
-
- noway_assert(rv2->gtOper == GT_REG_VAR);
-
- if (rev)
- {
- /* rv2 is already materialized in a register. Update liveness
- to after rv2 and then hang on to rv2 */
-
- genUpdateLife(rv2);
- regSet.rsMarkRegUsed(rv2, oper);
- }
-
- /* Generate the for the first operand */
-
- genCodeForTree(rv1, regMask);
-
- if (rev)
- {
- // Free up rv2 in the right fashion (it might be re-marked if keepReg)
- regSet.rsMarkRegUsed(rv1, oper);
- regSet.rsLockUsedReg(genRegMask(rv1->gtRegNum));
- genReleaseReg(rv2);
- regSet.rsUnlockUsedReg(genRegMask(rv1->gtRegNum));
- genReleaseReg(rv1);
- }
- else
- {
- /* We have evaluated rv1, and now we just need to update liveness
- to rv2 which was already in a register */
-
- genUpdateLife(rv2);
- }
-
- goto DONE_REGS;
- }
-
- if (forLea && !cns)
- return false;
-
- /* Make sure we preserve the correct operand order */
-
- if (rev)
- {
- /* Generate the second operand first */
-
- // Determine what registers go live between rv2 and rv1
- newLiveMask = genNewLiveRegMask(rv2, rv1);
-
- rv2Mask = regMask & ~newLiveMask;
- rv2Mask &= ~rv1->gtRsvdRegs;
-
- if (rv2Mask == RBM_NONE)
- {
- // The regMask hint cannot be honored
- // We probably have a call that trashes the register(s) in regMask
- // so ignore the regMask hint, but try to avoid using
- // the registers in newLiveMask and the rv1->gtRsvdRegs
- //
- rv2Mask = RBM_ALLINT & ~newLiveMask;
- rv2Mask = regSet.rsMustExclude(rv2Mask, rv1->gtRsvdRegs);
- }
-
- genCodeForTree(rv2, rv2Mask);
- regMask &= ~genRegMask(rv2->gtRegNum);
-
- regSet.rsMarkRegUsed(rv2, oper);
-
- /* Generate the first operand second */
-
- genCodeForTree(rv1, regMask);
- regSet.rsMarkRegUsed(rv1, oper);
-
- /* Free up both operands in the right order (they might be
- re-marked as used below)
- */
- regSet.rsLockUsedReg(genRegMask(rv1->gtRegNum));
- genReleaseReg(rv2);
- regSet.rsUnlockUsedReg(genRegMask(rv1->gtRegNum));
- genReleaseReg(rv1);
- }
- else
- {
- /* Get the first operand into a register */
-
- // Determine what registers go live between rv1 and rv2
- newLiveMask = genNewLiveRegMask(rv1, rv2);
-
- rv1Mask = regMask & ~newLiveMask;
- rv1Mask &= ~rv2->gtRsvdRegs;
-
- if (rv1Mask == RBM_NONE)
- {
- // The regMask hint cannot be honored
- // We probably have a call that trashes the register(s) in regMask
- // so ignore the regMask hint, but try to avoid using
- // the registers in liveMask and the rv2->gtRsvdRegs
- //
- rv1Mask = RBM_ALLINT & ~newLiveMask;
- rv1Mask = regSet.rsMustExclude(rv1Mask, rv2->gtRsvdRegs);
- }
-
- genCodeForTree(rv1, rv1Mask);
- regSet.rsMarkRegUsed(rv1, oper);
-
- GEN_RV2:
-
- /* Here, we need to get rv2 in a register. We have either already
- materialized rv1 into a register, or it was already in a one */
-
- noway_assert(rv1->InReg());
- noway_assert(rev || regSet.rsIsTreeInReg(rv1->gtRegNum, rv1));
-
- /* Generate the second operand as well */
-
- regMask &= ~genRegMask(rv1->gtRegNum);
- genCodeForTree(rv2, regMask);
-
- if (rev)
- {
- /* rev==true means the evaluation order is rv2,rv1. We just
- evaluated rv2, and rv1 was already in a register. Just
- update liveness to rv1 and we are done. */
-
- genUpdateLife(rv1);
- }
- else
- {
- /* We have evaluated rv1 and rv2. Free up both operands in
- the right order (they might be re-marked as used below) */
-
- /* Even though we have not explicitly marked rv2 as used,
- rv2->gtRegNum may be used if rv2 is a multi-use or
- an enregistered variable. */
- regMaskTP rv2Used;
- regSet.rsLockReg(genRegMask(rv2->gtRegNum), &rv2Used);
-
- /* Check for special case both rv1 and rv2 are the same register */
- if (rv2Used != genRegMask(rv1->gtRegNum))
- {
- genReleaseReg(rv1);
- regSet.rsUnlockReg(genRegMask(rv2->gtRegNum), rv2Used);
- }
- else
- {
- regSet.rsUnlockReg(genRegMask(rv2->gtRegNum), rv2Used);
- genReleaseReg(rv1);
- }
- }
- }
-
-/*-------------------------------------------------------------------------
- *
- * At this point, both rv1 and rv2 (if present) are in registers
- *
- */
-
-DONE_REGS:
-
- /* We must verify that 'rv1' and 'rv2' are both sitting in registers */
-
- if (rv1 && !(rv1->InReg()))
- return false;
- if (rv2 && !(rv2->InReg()))
- return false;
-
-YES:
-
- // *(intVar1+intVar1) causes problems as we
- // call regSet.rsMarkRegUsed(op1) and regSet.rsMarkRegUsed(op2). So the calling function
- // needs to know that it has to call rsFreeReg(reg1) twice. We can't do
- // that currently as we return a single mask in useMaskPtr.
-
- if ((keepReg == RegSet::KEEP_REG) && oper && rv1 && rv2 && rv1->InReg() && rv2->InReg())
- {
- if (rv1->gtRegNum == rv2->gtRegNum)
- {
- noway_assert(!operIsArrIndex);
- return false;
- }
- }
-
- /* Check either register operand to see if it needs to be saved */
-
- if (rv1)
- {
- noway_assert(rv1->InReg());
-
- if (keepReg == RegSet::KEEP_REG)
- {
- regSet.rsMarkRegUsed(rv1, oper);
- }
- else
- {
- /* If the register holds an address, mark it */
-
- gcInfo.gcMarkRegPtrVal(rv1->gtRegNum, rv1->TypeGet());
- }
- }
-
- if (rv2)
- {
- noway_assert(rv2->InReg());
-
- if (keepReg == RegSet::KEEP_REG)
- regSet.rsMarkRegUsed(rv2, oper);
- }
-
- if (deferOK)
- {
- noway_assert(!scaledIndex);
- return true;
- }
-
- /* Compute the set of registers the address depends on */
-
- regMaskTP useMask = RBM_NONE;
-
- if (rv1)
- {
- if (rv1->gtFlags & GTF_SPILLED)
- regSet.rsUnspillReg(rv1, 0, RegSet::KEEP_REG);
-
- noway_assert(rv1->InReg());
- useMask |= genRegMask(rv1->gtRegNum);
- }
-
- if (rv2)
- {
- if (rv2->gtFlags & GTF_SPILLED)
- {
- if (rv1)
- {
- regMaskTP lregMask = genRegMask(rv1->gtRegNum);
- regMaskTP used;
-
- regSet.rsLockReg(lregMask, &used);
- regSet.rsUnspillReg(rv2, 0, RegSet::KEEP_REG);
- regSet.rsUnlockReg(lregMask, used);
- }
- else
- regSet.rsUnspillReg(rv2, 0, RegSet::KEEP_REG);
- }
- noway_assert(rv2->InReg());
- useMask |= genRegMask(rv2->gtRegNum);
- }
-
- /* Tell the caller which registers we need to hang on to */
-
- *useMaskPtr = useMask;
-
- return true;
-}
-
-/*****************************************************************************
- *
- * 'oper' is an array bounds check (a GT_ARR_BOUNDS_CHECK node).
- */
-
-void CodeGen::genRangeCheck(GenTree* oper)
-{
- noway_assert(oper->OperGet() == GT_ARR_BOUNDS_CHECK);
- GenTreeBoundsChk* bndsChk = oper->AsBoundsChk();
-
- GenTree* arrLen = bndsChk->gtArrLen;
- GenTree* arrRef = NULL;
- int lenOffset = 0;
-
- /* Is the array index a constant value? */
- GenTree* index = bndsChk->gtIndex;
- if (!index->IsCnsIntOrI())
- {
- // No, it's not a constant.
- genCodeForTree(index, RBM_ALLINT);
- regSet.rsMarkRegUsed(index);
- }
-
- // If "arrLen" is a ARR_LENGTH operation, get the array whose length that takes in a register.
- // Otherwise, if the length is not a constant, get it (the length, not the arr reference) in
- // a register.
-
- if (arrLen->OperGet() == GT_ARR_LENGTH)
- {
- GenTreeArrLen* arrLenExact = arrLen->AsArrLen();
- lenOffset = arrLenExact->ArrLenOffset();
-
-#if !CPU_LOAD_STORE_ARCH && !defined(_TARGET_64BIT_)
- // We always load the length into a register on ARM and x64.
-
- // 64-bit has to act like LOAD_STORE_ARCH because the array only holds 32-bit
- // lengths, but the index expression *can* be native int (64-bits)
- arrRef = arrLenExact->ArrRef();
- genCodeForTree(arrRef, RBM_ALLINT);
- noway_assert(arrRef->InReg());
- regSet.rsMarkRegUsed(arrRef);
- noway_assert(regSet.rsMaskUsed & genRegMask(arrRef->gtRegNum));
-#endif
- }
-#if !CPU_LOAD_STORE_ARCH && !defined(_TARGET_64BIT_)
- // This is another form in which we have an array reference and a constant length. Don't use
- // on LOAD_STORE or 64BIT.
- else if (arrLen->OperGet() == GT_IND && arrLen->gtOp.gtOp1->IsAddWithI32Const(&arrRef, &lenOffset))
- {
- genCodeForTree(arrRef, RBM_ALLINT);
- noway_assert(arrRef->InReg());
- regSet.rsMarkRegUsed(arrRef);
- noway_assert(regSet.rsMaskUsed & genRegMask(arrRef->gtRegNum));
- }
-#endif
-
- // If we didn't find one of the special forms above, generate code to evaluate the array length to a register.
- if (arrRef == NULL)
- {
- // (Unless it's a constant.)
- if (!arrLen->IsCnsIntOrI())
- {
- genCodeForTree(arrLen, RBM_ALLINT);
- regSet.rsMarkRegUsed(arrLen);
-
- noway_assert(arrLen->InReg());
- noway_assert(regSet.rsMaskUsed & genRegMask(arrLen->gtRegNum));
- }
- }
-
- if (!index->IsCnsIntOrI())
- {
- // If we need "arrRef" or "arrLen", and evaluating "index" displaced whichever of them we're using
- // from its register, get it back in a register.
- regMaskTP indRegMask = RBM_ALLINT;
- regMaskTP arrRegMask = RBM_ALLINT;
- if (!(index->gtFlags & GTF_SPILLED))
- arrRegMask = ~genRegMask(index->gtRegNum);
- if (arrRef != NULL)
- {
- genRecoverReg(arrRef, arrRegMask, RegSet::KEEP_REG);
- indRegMask &= ~genRegMask(arrRef->gtRegNum);
- }
- else if (!arrLen->IsCnsIntOrI())
- {
- genRecoverReg(arrLen, arrRegMask, RegSet::KEEP_REG);
- indRegMask &= ~genRegMask(arrLen->gtRegNum);
- }
- if (index->gtFlags & GTF_SPILLED)
- regSet.rsUnspillReg(index, indRegMask, RegSet::KEEP_REG);
-
- /* Make sure we have the values we expect */
- noway_assert(index->InReg());
- noway_assert(regSet.rsMaskUsed & genRegMask(index->gtRegNum));
-
- noway_assert(index->TypeGet() == TYP_I_IMPL ||
- (varTypeIsIntegral(index->TypeGet()) && !varTypeIsLong(index->TypeGet())));
- var_types indxType = index->TypeGet();
- if (indxType != TYP_I_IMPL)
- indxType = TYP_INT;
-
- if (arrRef != NULL)
- { // _TARGET_X86_ or X64 when we have a TYP_INT (32-bit) index expression in the index register
-
- /* Generate "cmp index, [arrRef+LenOffs]" */
- inst_RV_AT(INS_cmp, emitTypeSize(indxType), indxType, index->gtRegNum, arrRef, lenOffset);
- }
- else if (arrLen->IsCnsIntOrI())
- {
- ssize_t len = arrLen->AsIntConCommon()->IconValue();
- inst_RV_IV(INS_cmp, index->gtRegNum, len, EA_4BYTE);
- }
- else
- {
- inst_RV_RV(INS_cmp, index->gtRegNum, arrLen->gtRegNum, indxType, emitTypeSize(indxType));
- }
-
- /* Generate "jae <fail_label>" */
-
- noway_assert(oper->gtOper == GT_ARR_BOUNDS_CHECK);
- emitJumpKind jmpGEU = genJumpKindForOper(GT_GE, CK_UNSIGNED);
- genJumpToThrowHlpBlk(jmpGEU, SCK_RNGCHK_FAIL, bndsChk->gtIndRngFailBB);
- }
- else
- {
- /* Generate "cmp [rv1+LenOffs], cns" */
-
- bool indIsInt = true;
-#ifdef _TARGET_64BIT_
- int ixv = 0;
- ssize_t ixvFull = index->AsIntConCommon()->IconValue();
- if (ixvFull > INT32_MAX)
- {
- indIsInt = false;
- }
- else
- {
- ixv = (int)ixvFull;
- }
-#else
- ssize_t ixvFull = index->AsIntConCommon()->IconValue();
- int ixv = (int)ixvFull;
-#endif
- if (arrRef != NULL && indIsInt)
- { // _TARGET_X86_ or X64 when we have a TYP_INT (32-bit) index expression in the index register
- /* Generate "cmp [arrRef+LenOffs], ixv" */
- inst_AT_IV(INS_cmp, EA_4BYTE, arrRef, ixv, lenOffset);
- // Generate "jbe <fail_label>"
- emitJumpKind jmpLEU = genJumpKindForOper(GT_LE, CK_UNSIGNED);
- genJumpToThrowHlpBlk(jmpLEU, SCK_RNGCHK_FAIL, bndsChk->gtIndRngFailBB);
- }
- else if (arrLen->IsCnsIntOrI())
- {
- ssize_t lenv = arrLen->AsIntConCommon()->IconValue();
- // Both are constants; decide at compile time.
- if (!(0 <= ixvFull && ixvFull < lenv))
- {
- genJumpToThrowHlpBlk(EJ_jmp, SCK_RNGCHK_FAIL, bndsChk->gtIndRngFailBB);
- }
- }
- else if (!indIsInt)
- {
- genJumpToThrowHlpBlk(EJ_jmp, SCK_RNGCHK_FAIL, bndsChk->gtIndRngFailBB);
- }
- else
- {
- /* Generate "cmp arrLen, ixv" */
- inst_RV_IV(INS_cmp, arrLen->gtRegNum, ixv, EA_4BYTE);
- // Generate "jbe <fail_label>"
- emitJumpKind jmpLEU = genJumpKindForOper(GT_LE, CK_UNSIGNED);
- genJumpToThrowHlpBlk(jmpLEU, SCK_RNGCHK_FAIL, bndsChk->gtIndRngFailBB);
- }
- }
-
- // Free the registers that were used.
- if (!index->IsCnsIntOrI())
- {
- genReleaseReg(index);
- }
-
- if (arrRef != NULL)
- {
- genReleaseReg(arrRef);
- }
- else if (!arrLen->IsCnsIntOrI())
- {
- genReleaseReg(arrLen);
- }
-}
-
-/*****************************************************************************
- *
- * If compiling without REDUNDANT_LOAD, same as genMakeAddressable().
- * Otherwise, check if rvalue is in register. If so, mark it. Then
- * call genMakeAddressable(). Needed because genMakeAddressable is used
- * for both lvalue and rvalue, and we only can do this for rvalue.
- */
-
-// inline
-regMaskTP CodeGen::genMakeRvalueAddressable(
- GenTree* tree, regMaskTP needReg, RegSet::KeepReg keepReg, bool forLoadStore, bool smallOK)
-{
- regNumber reg;
-
-#if REDUNDANT_LOAD
-
- if (tree->gtOper == GT_LCL_VAR)
- {
- reg = findStkLclInReg(tree->gtLclVarCommon.gtLclNum);
-
- if (reg != REG_NA && (needReg == 0 || (genRegMask(reg) & needReg) != 0))
- {
- noway_assert(!isRegPairType(tree->gtType));
-
- genMarkTreeInReg(tree, reg);
- }
- }
-
-#endif
-
- return genMakeAddressable2(tree, needReg, keepReg, forLoadStore, smallOK);
-}
-
-/*****************************************************************************/
-
-bool CodeGen::genIsLocalLastUse(GenTree* tree)
-{
- const LclVarDsc* varDsc = &compiler->lvaTable[tree->gtLclVarCommon.gtLclNum];
-
- noway_assert(tree->OperGet() == GT_LCL_VAR);
- noway_assert(varDsc->lvTracked);
-
- return ((tree->gtFlags & GTF_VAR_DEATH) != 0);
-}
-
-/*****************************************************************************
- *
- * This is genMakeAddressable(GT_ARR_ELEM).
- * Makes the array-element addressible and returns the addressibility registers.
- * It also marks them as used if keepReg==RegSet::KEEP_REG.
- * tree is the dependant tree.
- *
- * Note that an array-element needs 2 registers to be addressibile, the
- * array-object and the offset. This function marks gtArrObj and gtArrInds[0]
- * with the 2 registers so that other functions (like instGetAddrMode()) know
- * where to look for the offset to use.
- */
-
-regMaskTP CodeGen::genMakeAddrArrElem(GenTree* arrElem, GenTree* tree, regMaskTP needReg, RegSet::KeepReg keepReg)
-{
- noway_assert(arrElem->gtOper == GT_ARR_ELEM);
- noway_assert(!tree || tree->gtOper == GT_IND || tree == arrElem);
-
- /* Evaluate all the operands. We don't evaluate them into registers yet
- as GT_ARR_ELEM does not reorder the evaluation of the operands, and
- hence may use a sub-optimal ordering. We try to improve this
- situation somewhat by accessing the operands in stages
- (genMakeAddressable2 + genComputeAddressable and
- genCompIntoFreeReg + genRecoverReg).
-
- Note: we compute operands into free regs to avoid multiple uses of
- the same register. Multi-use would cause problems when we free
- registers in FIFO order instead of the assumed LIFO order that
- applies to all type of tree nodes except for GT_ARR_ELEM.
- */
-
- GenTree* arrObj = arrElem->gtArrElem.gtArrObj;
- unsigned rank = arrElem->gtArrElem.gtArrRank;
- var_types elemType = arrElem->gtArrElem.gtArrElemType;
- regMaskTP addrReg = RBM_NONE;
- regMaskTP regNeed = RBM_ALLINT;
-
-#if !NOGC_WRITE_BARRIERS
- // In CodeGen::WriteBarrier we set up ARG_1 followed by ARG_0
- // since the arrObj participates in the lea/add instruction
- // that computes ARG_0 we should avoid putting it in ARG_1
- //
- if (varTypeIsGC(elemType))
- {
- regNeed &= ~RBM_ARG_1;
- }
-#endif
-
- // Strip off any comma expression.
- arrObj = genCodeForCommaTree(arrObj);
-
- // Having generated the code for the comma, we don't care about it anymore.
- arrElem->gtArrElem.gtArrObj = arrObj;
-
- // If the array ref is a stack var that's dying here we have to move it
- // into a register (regalloc already counts of this), as if it's a GC pointer
- // it can be collected from here on. This is not an issue for locals that are
- // in a register, as they get marked as used an will be tracked.
- // The bug that caused this is #100776. (untracked vars?)
- if (arrObj->OperGet() == GT_LCL_VAR && compiler->optIsTrackedLocal(arrObj) && genIsLocalLastUse(arrObj) &&
- !genMarkLclVar(arrObj))
- {
- genCodeForTree(arrObj, regNeed);
- regSet.rsMarkRegUsed(arrObj, 0);
- addrReg = genRegMask(arrObj->gtRegNum);
- }
- else
- {
- addrReg = genMakeAddressable2(arrObj, regNeed, RegSet::KEEP_REG,
- true, // forLoadStore
- false, // smallOK
- false, // deferOK
- true); // evalSideEffs
- }
-
- unsigned dim;
- for (dim = 0; dim < rank; dim++)
- genCompIntoFreeReg(arrElem->gtArrElem.gtArrInds[dim], RBM_NONE, RegSet::KEEP_REG);
-
- /* Ensure that the array-object is in a register */
-
- addrReg = genKeepAddressable(arrObj, addrReg);
- genComputeAddressable(arrObj, addrReg, RegSet::KEEP_REG, regNeed, RegSet::KEEP_REG);
-
- regNumber arrReg = arrObj->gtRegNum;
- regMaskTP arrRegMask = genRegMask(arrReg);
- regMaskTP indRegMask = RBM_ALLINT & ~arrRegMask;
- regSet.rsLockUsedReg(arrRegMask);
-
- /* Now process all the indices, do the range check, and compute
- the offset of the element */
-
- regNumber accReg = DUMMY_INIT(REG_CORRUPT); // accumulates the offset calculation
-
- for (dim = 0; dim < rank; dim++)
- {
- GenTree* index = arrElem->gtArrElem.gtArrInds[dim];
-
- /* Get the index into a free register (other than the register holding the array) */
-
- genRecoverReg(index, indRegMask, RegSet::KEEP_REG);
-
-#if CPU_LOAD_STORE_ARCH
- /* Subtract the lower bound, and do the range check */
-
- regNumber valueReg = regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(arrReg) & ~genRegMask(index->gtRegNum));
- getEmitter()->emitIns_R_AR(INS_ldr, EA_4BYTE, valueReg, arrReg,
- compiler->eeGetArrayDataOffset(elemType) + sizeof(int) * (dim + rank));
- regTracker.rsTrackRegTrash(valueReg);
- getEmitter()->emitIns_R_R(INS_sub, EA_4BYTE, index->gtRegNum, valueReg);
- regTracker.rsTrackRegTrash(index->gtRegNum);
-
- getEmitter()->emitIns_R_AR(INS_ldr, EA_4BYTE, valueReg, arrReg,
- compiler->eeGetArrayDataOffset(elemType) + sizeof(int) * dim);
- getEmitter()->emitIns_R_R(INS_cmp, EA_4BYTE, index->gtRegNum, valueReg);
-#else
- /* Subtract the lower bound, and do the range check */
- getEmitter()->emitIns_R_AR(INS_sub, EA_4BYTE, index->gtRegNum, arrReg,
- compiler->eeGetArrayDataOffset(elemType) + sizeof(int) * (dim + rank));
- regTracker.rsTrackRegTrash(index->gtRegNum);
-
- getEmitter()->emitIns_R_AR(INS_cmp, EA_4BYTE, index->gtRegNum, arrReg,
- compiler->eeGetArrayDataOffset(elemType) + sizeof(int) * dim);
-#endif
- emitJumpKind jmpGEU = genJumpKindForOper(GT_GE, CK_UNSIGNED);
- genJumpToThrowHlpBlk(jmpGEU, SCK_RNGCHK_FAIL);
-
- if (dim == 0)
- {
- /* Hang on to the register of the first index */
-
- noway_assert(accReg == DUMMY_INIT(REG_CORRUPT));
- accReg = index->gtRegNum;
- noway_assert(accReg != arrReg);
- regSet.rsLockUsedReg(genRegMask(accReg));
- }
- else
- {
- /* Evaluate accReg = accReg*dim_size + index */
-
- noway_assert(accReg != DUMMY_INIT(REG_CORRUPT));
-#if CPU_LOAD_STORE_ARCH
- getEmitter()->emitIns_R_AR(INS_ldr, EA_4BYTE, valueReg, arrReg,
- compiler->eeGetArrayDataOffset(elemType) + sizeof(int) * dim);
- regTracker.rsTrackRegTrash(valueReg);
- getEmitter()->emitIns_R_R(INS_MUL, EA_4BYTE, accReg, valueReg);
-#else
- getEmitter()->emitIns_R_AR(INS_MUL, EA_4BYTE, accReg, arrReg,
- compiler->eeGetArrayDataOffset(elemType) + sizeof(int) * dim);
-#endif
-
- inst_RV_RV(INS_add, accReg, index->gtRegNum);
- regSet.rsMarkRegFree(index->gtRegNum, index);
- regTracker.rsTrackRegTrash(accReg);
- }
- }
-
- if (!jitIsScaleIndexMul(arrElem->gtArrElem.gtArrElemSize))
- {
- regNumber sizeReg = genGetRegSetToIcon(arrElem->gtArrElem.gtArrElemSize);
-
- getEmitter()->emitIns_R_R(INS_MUL, EA_4BYTE, accReg, sizeReg);
- regTracker.rsTrackRegTrash(accReg);
- }
-
- regSet.rsUnlockUsedReg(genRegMask(arrReg));
- regSet.rsUnlockUsedReg(genRegMask(accReg));
-
- regSet.rsMarkRegFree(genRegMask(arrReg));
- regSet.rsMarkRegFree(genRegMask(accReg));
-
- if (keepReg == RegSet::KEEP_REG)
- {
- /* We mark the addressability registers on arrObj and gtArrInds[0].
- instGetAddrMode() knows to work with this. */
-
- regSet.rsMarkRegUsed(arrObj, tree);
- regSet.rsMarkRegUsed(arrElem->gtArrElem.gtArrInds[0], tree);
- }
-
- return genRegMask(arrReg) | genRegMask(accReg);
-}
-
-/*****************************************************************************
- *
- * Make sure the given tree is addressable. 'needReg' is a mask that indicates
- * the set of registers we would prefer the destination tree to be computed
- * into (RBM_NONE means no preference).
- *
- * 'tree' can subsequently be used with the inst_XX_TT() family of functions.
- *
- * If 'keepReg' is RegSet::KEEP_REG, we mark any registers the addressability depends
- * on as used, and return the mask for that register set (if no registers
- * are marked as used, RBM_NONE is returned).
- *
- * If 'smallOK' is not true and the datatype being address is a byte or short,
- * then the tree is forced into a register. This is useful when the machine
- * instruction being emitted does not have a byte or short version.
- *
- * The "deferOK" parameter indicates the mode of operation - when it's false,
- * upon returning an actual address mode must have been formed (i.e. it must
- * be possible to immediately call one of the inst_TT methods to operate on
- * the value). When "deferOK" is true, we do whatever it takes to be ready
- * to form the address mode later - for example, if an index address mode on
- * a particular CPU requires the use of a specific register, we usually don't
- * want to immediately grab that register for an address mode that will only
- * be needed later. The convention is to call genMakeAddressable() with
- * "deferOK" equal to true, do whatever work is needed to prepare the other
- * operand, call genMakeAddressable() with "deferOK" equal to false, and
- * finally call one of the inst_TT methods right after that.
- *
- * If we do any other codegen after genMakeAddressable(tree) which can
- * potentially spill the addressability registers, genKeepAddressable()
- * needs to be called before accessing the tree again.
- *
- * genDoneAddressable() needs to be called when we are done with the tree
- * to free the addressability registers.
- */
-
-regMaskTP CodeGen::genMakeAddressable(
- GenTree* tree, regMaskTP needReg, RegSet::KeepReg keepReg, bool smallOK, bool deferOK)
-{
- GenTree* addr = NULL;
- regMaskTP regMask;
-
- /* Is the value simply sitting in a register? */
-
- if (tree->InReg())
- {
- genUpdateLife(tree);
-
- goto GOT_VAL;
- }
-
- // TODO: If the value is for example a cast of float -> int, compute
- // TODO: the converted value into a stack temp, and leave it there,
- // TODO: since stack temps are always addressable. This would require
- // TODO: recording the fact that a particular tree is in a stack temp.
-
- /* byte/char/short operand -- is this acceptable to the caller? */
-
- if (varTypeIsSmall(tree->TypeGet()) && !smallOK)
- goto EVAL_TREE;
-
- // Evaluate non-last elements of comma expressions, to get to the last.
- tree = genCodeForCommaTree(tree);
-
- switch (tree->gtOper)
- {
- case GT_LCL_FLD:
-
- // We only use GT_LCL_FLD for lvDoNotEnregister vars, so we don't have
- // to worry about it being enregistered.
- noway_assert(compiler->lvaTable[tree->gtLclFld.gtLclNum].lvRegister == 0);
-
- genUpdateLife(tree);
- return 0;
-
- case GT_LCL_VAR:
-
- if (!genMarkLclVar(tree))
- {
- genUpdateLife(tree);
- return 0;
- }
-
- __fallthrough; // it turns out the variable lives in a register
-
- case GT_REG_VAR:
-
- genUpdateLife(tree);
-
- goto GOT_VAL;
-
- case GT_CLS_VAR:
-
- return 0;
-
- case GT_CNS_INT:
-#ifdef _TARGET_64BIT_
- // Non-relocs will be sign extended, so we don't have to enregister
- // constants that are equivalent to a sign-extended int.
- // Relocs can be left alone if they are RIP-relative.
- if ((genTypeSize(tree->TypeGet()) > 4) &&
- (!tree->IsIntCnsFitsInI32() ||
- (tree->IsIconHandle() &&
- (IMAGE_REL_BASED_REL32 != compiler->eeGetRelocTypeHint((void*)tree->gtIntCon.gtIconVal)))))
- {
- break;
- }
-#endif // _TARGET_64BIT_
- __fallthrough;
-
- case GT_CNS_LNG:
- case GT_CNS_DBL:
- // For MinOpts, we don't do constant folding, so we have
- // constants showing up in places we don't like.
- // force them into a register now to prevent that.
- if (compiler->opts.OptEnabled(CLFLG_CONSTANTFOLD))
- return 0;
- break;
-
- case GT_IND:
- case GT_NULLCHECK:
-
- /* Try to make the address directly addressable */
-
- if (genMakeIndAddrMode(tree->gtOp.gtOp1, tree, false, /* not for LEA */
- needReg, keepReg, &regMask, deferOK))
- {
- genUpdateLife(tree);
- return regMask;
- }
-
- /* No good, we'll have to load the address into a register */
-
- addr = tree;
- tree = tree->gtOp.gtOp1;
- break;
-
- default:
- break;
- }
-
-EVAL_TREE:
-
- /* Here we need to compute the value 'tree' into a register */
-
- genCodeForTree(tree, needReg);
-
-GOT_VAL:
-
- noway_assert(tree->InReg());
-
- if (isRegPairType(tree->gtType))
- {
- /* Are we supposed to hang on to the register? */
-
- if (keepReg == RegSet::KEEP_REG)
- regSet.rsMarkRegPairUsed(tree);
-
- regMask = genRegPairMask(tree->gtRegPair);
- }
- else
- {
- /* Are we supposed to hang on to the register? */
-
- if (keepReg == RegSet::KEEP_REG)
- regSet.rsMarkRegUsed(tree, addr);
-
- regMask = genRegMask(tree->gtRegNum);
- }
-
- return regMask;
-}
-
-/*****************************************************************************
- * Compute a tree (which was previously made addressable using
- * genMakeAddressable()) into a register.
- * needReg - mask of preferred registers.
- * keepReg - should the computed register be marked as used by the tree
- * freeOnly - target register needs to be a scratch register
- */
-
-void CodeGen::genComputeAddressable(GenTree* tree,
- regMaskTP addrReg,
- RegSet::KeepReg keptReg,
- regMaskTP needReg,
- RegSet::KeepReg keepReg,
- bool freeOnly)
-{
- noway_assert(genStillAddressable(tree));
- noway_assert(varTypeIsIntegralOrI(tree->TypeGet()));
-
- genDoneAddressable(tree, addrReg, keptReg);
-
- regNumber reg;
-
- if (tree->InReg())
- {
- reg = tree->gtRegNum;
-
- if (freeOnly && !(genRegMask(reg) & regSet.rsRegMaskFree()))
- goto MOVE_REG;
- }
- else
- {
- if (tree->OperIsConst())
- {
- /* Need to handle consts separately as we don't want to emit
- "mov reg, 0" (emitter doesn't like that). Also, genSetRegToIcon()
- handles consts better for SMALL_CODE */
-
- noway_assert(tree->IsCnsIntOrI());
- reg = genGetRegSetToIcon(tree->gtIntCon.gtIconVal, needReg, tree->gtType);
- }
- else
- {
- MOVE_REG:
- reg = regSet.rsPickReg(needReg);
-
- inst_RV_TT(INS_mov, reg, tree);
- regTracker.rsTrackRegTrash(reg);
- }
- }
-
- genMarkTreeInReg(tree, reg);
-
- if (keepReg == RegSet::KEEP_REG)
- regSet.rsMarkRegUsed(tree);
- else
- gcInfo.gcMarkRegPtrVal(tree);
-}
-
-/*****************************************************************************
- * Should be similar to genMakeAddressable() but gives more control.
- */
-
-regMaskTP CodeGen::genMakeAddressable2(GenTree* tree,
- regMaskTP needReg,
- RegSet::KeepReg keepReg,
- bool forLoadStore,
- bool smallOK,
- bool deferOK,
- bool evalSideEffs)
-
-{
- bool evalToReg = false;
-
- if (evalSideEffs && (tree->gtOper == GT_IND) && (tree->gtFlags & GTF_EXCEPT))
- evalToReg = true;
-
-#if CPU_LOAD_STORE_ARCH
- if (!forLoadStore)
- evalToReg = true;
-#endif
-
- if (evalToReg)
- {
- genCodeForTree(tree, needReg);
-
- noway_assert(tree->InReg());
-
- if (isRegPairType(tree->gtType))
- {
- /* Are we supposed to hang on to the register? */
-
- if (keepReg == RegSet::KEEP_REG)
- regSet.rsMarkRegPairUsed(tree);
-
- return genRegPairMask(tree->gtRegPair);
- }
- else
- {
- /* Are we supposed to hang on to the register? */
-
- if (keepReg == RegSet::KEEP_REG)
- regSet.rsMarkRegUsed(tree);
-
- return genRegMask(tree->gtRegNum);
- }
- }
- else
- {
- return genMakeAddressable(tree, needReg, keepReg, smallOK, deferOK);
- }
-}
-
-/*****************************************************************************
- *
- * The given tree was previously passed to genMakeAddressable(); return
- * 'true' if the operand is still addressable.
- */
-
-// inline
-bool CodeGen::genStillAddressable(GenTree* tree)
-{
- /* Has the value (or one or more of its sub-operands) been spilled? */
-
- if (tree->gtFlags & (GTF_SPILLED | GTF_SPILLED_OPER))
- return false;
-
- return true;
-}
-
-/*****************************************************************************
- *
- * Recursive helper to restore complex address modes. The 'lockPhase'
- * argument indicates whether we're in the 'lock' or 'reload' phase.
- */
-
-regMaskTP CodeGen::genRestoreAddrMode(GenTree* addr, GenTree* tree, bool lockPhase)
-{
- regMaskTP regMask = RBM_NONE;
-
- /* Have we found a spilled value? */
-
- if (tree->gtFlags & GTF_SPILLED)
- {
- /* Do nothing if we're locking, otherwise reload and lock */
-
- if (!lockPhase)
- {
- /* Unspill the register */
-
- regSet.rsUnspillReg(tree, 0, RegSet::FREE_REG);
-
- /* The value should now be sitting in a register */
-
- noway_assert(tree->InReg());
- regMask = genRegMask(tree->gtRegNum);
-
- /* Mark the register as used for the address */
-
- regSet.rsMarkRegUsed(tree, addr);
-
- /* Lock the register until we're done with the entire address */
-
- regSet.rsMaskLock |= regMask;
- }
-
- return regMask;
- }
-
- /* Is this sub-tree sitting in a register? */
-
- if (tree->InReg())
- {
- regMask = genRegMask(tree->gtRegNum);
-
- /* Lock the register if we're in the locking phase */
-
- if (lockPhase)
- regSet.rsMaskLock |= regMask;
- }
- else
- {
- /* Process any sub-operands of this node */
-
- unsigned kind = tree->OperKind();
-
- if (kind & GTK_SMPOP)
- {
- /* Unary/binary operator */
-
- if (tree->gtOp.gtOp1)
- regMask |= genRestoreAddrMode(addr, tree->gtOp.gtOp1, lockPhase);
- if (tree->gtGetOp2IfPresent())
- regMask |= genRestoreAddrMode(addr, tree->gtOp.gtOp2, lockPhase);
- }
- else if (tree->gtOper == GT_ARR_ELEM)
- {
- /* gtArrObj is the array-object and gtArrInds[0] is marked with the register
- which holds the offset-calculation */
-
- regMask |= genRestoreAddrMode(addr, tree->gtArrElem.gtArrObj, lockPhase);
- regMask |= genRestoreAddrMode(addr, tree->gtArrElem.gtArrInds[0], lockPhase);
- }
- else if (tree->gtOper == GT_CMPXCHG)
- {
- regMask |= genRestoreAddrMode(addr, tree->gtCmpXchg.gtOpLocation, lockPhase);
- }
- else
- {
- /* Must be a leaf/constant node */
-
- noway_assert(kind & (GTK_LEAF | GTK_CONST));
- }
- }
-
- return regMask;
-}
-
-/*****************************************************************************
- *
- * The given tree was previously passed to genMakeAddressable, but since then
- * some of its registers are known to have been spilled; do whatever it takes
- * to make the operand addressable again (typically by reloading any spilled
- * registers).
- */
-
-regMaskTP CodeGen::genRestAddressable(GenTree* tree, regMaskTP addrReg, regMaskTP lockMask)
-{
- noway_assert((regSet.rsMaskLock & lockMask) == lockMask);
-
- /* Is this a 'simple' register spill? */
-
- if (tree->gtFlags & GTF_SPILLED)
- {
- /* The mask must match the original register/regpair */
-
- if (isRegPairType(tree->gtType))
- {
- noway_assert(addrReg == genRegPairMask(tree->gtRegPair));
-
- regSet.rsUnspillRegPair(tree, /* restore it anywhere */ RBM_NONE, RegSet::KEEP_REG);
-
- addrReg = genRegPairMask(tree->gtRegPair);
- }
- else
- {
- noway_assert(addrReg == genRegMask(tree->gtRegNum));
-
- regSet.rsUnspillReg(tree, /* restore it anywhere */ RBM_NONE, RegSet::KEEP_REG);
-
- addrReg = genRegMask(tree->gtRegNum);
- }
-
- noway_assert((regSet.rsMaskLock & lockMask) == lockMask);
- regSet.rsMaskLock -= lockMask;
-
- return addrReg;
- }
-
- /* We have a complex address mode with some of its sub-operands spilled */
-
- noway_assert((tree->InReg()) == 0);
- noway_assert((tree->gtFlags & GTF_SPILLED_OPER) != 0);
-
- /*
- We'll proceed in several phases:
-
- 1. Lock any registers that are part of the address mode and
- have not been spilled. This prevents these registers from
- getting spilled in step 2.
-
- 2. Reload any registers that have been spilled; lock each
- one right after it is reloaded.
-
- 3. Unlock all the registers.
- */
-
- addrReg = genRestoreAddrMode(tree, tree, true);
- addrReg |= genRestoreAddrMode(tree, tree, false);
-
- /* Unlock all registers that the address mode uses */
-
- lockMask |= addrReg;
-
- noway_assert((regSet.rsMaskLock & lockMask) == lockMask);
- regSet.rsMaskLock -= lockMask;
-
- return addrReg;
-}
-
-/*****************************************************************************
- *
- * The given tree was previously passed to genMakeAddressable, but since then
- * some of its registers might have been spilled ('addrReg' is the set of
- * registers used by the address). This function makes sure the operand is
- * still addressable (while avoiding any of the registers in 'avoidMask'),
- * and returns the (possibly modified) set of registers that are used by
- * the address (these will be marked as used on exit).
- */
-
-regMaskTP CodeGen::genKeepAddressable(GenTree* tree, regMaskTP addrReg, regMaskTP avoidMask)
-{
- /* Is the operand still addressable? */
-
- tree = tree->gtEffectiveVal(/*commaOnly*/ true); // Strip off commas for this purpose.
-
- if (!genStillAddressable(tree))
- {
- if (avoidMask)
- {
- // Temporarily lock 'avoidMask' while we restore addressability
- // genRestAddressable will unlock the 'avoidMask' for us
- // avoidMask must already be marked as a used reg in regSet.rsMaskUsed
- // In regSet.rsRegMaskFree() we require that all locked register be marked as used
- //
- regSet.rsLockUsedReg(avoidMask);
- }
-
- addrReg = genRestAddressable(tree, addrReg, avoidMask);
-
- noway_assert((regSet.rsMaskLock & avoidMask) == 0);
- }
-
- return addrReg;
-}
-
-/*****************************************************************************
- *
- * After we're finished with the given operand (which was previously marked
- * by calling genMakeAddressable), this function must be called to free any
- * registers that may have been used by the address.
- * keptReg indicates if the addressability registers were marked as used
- * by genMakeAddressable().
- */
-
-void CodeGen::genDoneAddressable(GenTree* tree, regMaskTP addrReg, RegSet::KeepReg keptReg)
-{
- if (keptReg == RegSet::FREE_REG)
- {
- // We exclude regSet.rsMaskUsed since the registers may be multi-used.
- // ie. There may be a pending use in a higher-up tree.
-
- addrReg &= ~regSet.rsMaskUsed;
-
- /* addrReg was not marked as used. So just reset its GC info */
- if (addrReg)
- {
- gcInfo.gcMarkRegSetNpt(addrReg);
- }
- }
- else
- {
- /* addrReg was marked as used. So we need to free it up (which
- will also reset its GC info) */
-
- regSet.rsMarkRegFree(addrReg);
- }
-}
-
-/*****************************************************************************/
-/*****************************************************************************
- *
- * Make sure the given floating point value is addressable, and return a tree
- * that will yield the value as an addressing mode (this tree may differ from
- * the one passed in, BTW). If the only way to make the value addressable is
- * to evaluate into the FP stack, we do this and return zero.
- */
-
-GenTree* CodeGen::genMakeAddrOrFPstk(GenTree* tree, regMaskTP* regMaskPtr, bool roundResult)
-{
- *regMaskPtr = 0;
-
- switch (tree->gtOper)
- {
- case GT_LCL_VAR:
- case GT_LCL_FLD:
- case GT_CLS_VAR:
- return tree;
-
- case GT_CNS_DBL:
- if (tree->gtType == TYP_FLOAT)
- {
- float f = forceCastToFloat(tree->gtDblCon.gtDconVal);
- return genMakeConst(&f, TYP_FLOAT, tree, false);
- }
- return genMakeConst(&tree->gtDblCon.gtDconVal, tree->gtType, tree, true);
-
- case GT_IND:
- case GT_NULLCHECK:
-
- /* Try to make the address directly addressable */
-
- if (genMakeIndAddrMode(tree->gtOp.gtOp1, tree, false, /* not for LEA */
- 0, RegSet::FREE_REG, regMaskPtr, false))
- {
- genUpdateLife(tree);
- return tree;
- }
-
- break;
-
- default:
- break;
- }
-#if FEATURE_STACK_FP_X87
- /* We have no choice but to compute the value 'tree' onto the FP stack */
-
- genCodeForTreeFlt(tree);
-#endif
- return 0;
-}
-
-/*****************************************************************************/
-/*****************************************************************************
- *
- * Display a string literal value (debug only).
- */
-
-#ifdef DEBUG
-#endif
-
-/*****************************************************************************
- *
- * Generate code to check that the GS cookie wasn't thrashed by a buffer
- * overrun. If pushReg is true, preserve all registers around code sequence.
- * Otherwise, ECX maybe modified.
- */
-void CodeGen::genEmitGSCookieCheck(bool pushReg)
-{
- // Make sure that EAX didn't die in the return expression
- if (!pushReg && (compiler->info.compRetType == TYP_REF))
- gcInfo.gcRegGCrefSetCur |= RBM_INTRET;
-
- // Add cookie check code for unsafe buffers
- BasicBlock* gsCheckBlk;
- regMaskTP byrefPushedRegs = RBM_NONE;
- regMaskTP norefPushedRegs = RBM_NONE;
- regMaskTP pushedRegs = RBM_NONE;
-
- noway_assert(compiler->gsGlobalSecurityCookieAddr || compiler->gsGlobalSecurityCookieVal);
-
-#if CPU_LOAD_STORE_ARCH
- // Lock all ABI argument registers before generating the check. All other registers should be dead, so this
- // shouldn't over-constrain us.
- const regMaskTP unlockedArgRegs = RBM_ARG_REGS & ~regSet.rsMaskLock;
- regMaskTP usedArgRegs;
- regSet.rsLockReg(unlockedArgRegs, &usedArgRegs);
-#endif
-
- if (compiler->gsGlobalSecurityCookieAddr == NULL)
- {
- // JIT case
- CLANG_FORMAT_COMMENT_ANCHOR;
-
-#if CPU_LOAD_STORE_ARCH
- regNumber reg = regSet.rsGrabReg(RBM_ALLINT);
- getEmitter()->emitIns_R_S(ins_Load(TYP_INT), EA_4BYTE, reg, compiler->lvaGSSecurityCookie, 0);
- regTracker.rsTrackRegTrash(reg);
-
- if (arm_Valid_Imm_For_Alu(compiler->gsGlobalSecurityCookieVal) ||
- arm_Valid_Imm_For_Alu(~compiler->gsGlobalSecurityCookieVal))
- {
- getEmitter()->emitIns_R_I(INS_cmp, EA_4BYTE, reg, compiler->gsGlobalSecurityCookieVal);
- }
- else
- {
- // Load CookieVal into a register
- regNumber immReg = regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(reg));
- instGen_Set_Reg_To_Imm(EA_4BYTE, immReg, compiler->gsGlobalSecurityCookieVal);
- getEmitter()->emitIns_R_R(INS_cmp, EA_4BYTE, reg, immReg);
- }
-#else
- getEmitter()->emitIns_S_I(INS_cmp, EA_PTRSIZE, compiler->lvaGSSecurityCookie, 0,
- (int)compiler->gsGlobalSecurityCookieVal);
-#endif
- }
- else
- {
- regNumber regGSCheck;
- regMaskTP regMaskGSCheck;
-#if CPU_LOAD_STORE_ARCH
- regGSCheck = regSet.rsGrabReg(RBM_ALLINT);
- regMaskGSCheck = genRegMask(regGSCheck);
-#else
- // Don't pick the 'this' register
- if (compiler->lvaKeepAliveAndReportThis() && compiler->lvaTable[compiler->info.compThisArg].lvRegister &&
- (compiler->lvaTable[compiler->info.compThisArg].lvRegNum == REG_ECX))
- {
- regGSCheck = REG_EDX;
- regMaskGSCheck = RBM_EDX;
- }
- else
- {
- regGSCheck = REG_ECX;
- regMaskGSCheck = RBM_ECX;
- }
-
- // NGen case
- if (pushReg && (regMaskGSCheck & (regSet.rsMaskUsed | regSet.rsMaskVars | regSet.rsMaskLock)))
- {
- pushedRegs = genPushRegs(regMaskGSCheck, &byrefPushedRegs, &norefPushedRegs);
- }
- else
- {
- noway_assert((regMaskGSCheck & (regSet.rsMaskUsed | regSet.rsMaskVars | regSet.rsMaskLock)) == 0);
- }
-#endif
-#if defined(_TARGET_ARM_)
- instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, regGSCheck, (ssize_t)compiler->gsGlobalSecurityCookieAddr);
- getEmitter()->emitIns_R_R_I(INS_ldr, EA_PTRSIZE, regGSCheck, regGSCheck, 0);
-#else
- getEmitter()->emitIns_R_C(ins_Load(TYP_I_IMPL), EA_PTR_DSP_RELOC, regGSCheck, FLD_GLOBAL_DS,
- (ssize_t)compiler->gsGlobalSecurityCookieAddr);
-#endif // !_TARGET_ARM_
- regTracker.rsTrashRegSet(regMaskGSCheck);
-#ifdef _TARGET_ARM_
- regNumber regTmp = regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(regGSCheck));
- getEmitter()->emitIns_R_S(INS_ldr, EA_PTRSIZE, regTmp, compiler->lvaGSSecurityCookie, 0);
- regTracker.rsTrackRegTrash(regTmp);
- getEmitter()->emitIns_R_R(INS_cmp, EA_PTRSIZE, regTmp, regGSCheck);
-#else
- getEmitter()->emitIns_S_R(INS_cmp, EA_PTRSIZE, regGSCheck, compiler->lvaGSSecurityCookie, 0);
-#endif
- }
-
- gsCheckBlk = genCreateTempLabel();
- emitJumpKind jmpEqual = genJumpKindForOper(GT_EQ, CK_SIGNED);
- inst_JMP(jmpEqual, gsCheckBlk);
- genEmitHelperCall(CORINFO_HELP_FAIL_FAST, 0, EA_UNKNOWN);
- genDefineTempLabel(gsCheckBlk);
-
- genPopRegs(pushedRegs, byrefPushedRegs, norefPushedRegs);
-
-#if CPU_LOAD_STORE_ARCH
- // Unlock all ABI argument registers.
- regSet.rsUnlockReg(unlockedArgRegs, usedArgRegs);
-#endif
-}
-
-/*****************************************************************************
- *
- * Generate any side effects within the given expression tree.
- */
-
-void CodeGen::genEvalSideEffects(GenTree* tree)
-{
- genTreeOps oper;
- unsigned kind;
-
-AGAIN:
-
- /* Does this sub-tree contain any side-effects? */
- if (tree->gtFlags & GTF_SIDE_EFFECT)
- {
-#if FEATURE_STACK_FP_X87
- /* Remember the current FP stack level */
- int iTemps = genNumberTemps();
-#endif
- if (tree->OperIsIndir())
- {
- regMaskTP addrReg = genMakeAddressable(tree, RBM_ALLINT, RegSet::KEEP_REG, true, false);
-
- if (tree->InReg())
- {
- gcInfo.gcMarkRegPtrVal(tree);
- genDoneAddressable(tree, addrReg, RegSet::KEEP_REG);
- }
- // GTF_IND_RNGCHK trees have already de-referenced the pointer, and so
- // do not need an additional null-check
- /* Do this only if the GTF_EXCEPT or GTF_IND_VOLATILE flag is set on the indir */
- else if ((tree->gtFlags & GTF_IND_ARR_INDEX) == 0 && ((tree->gtFlags & GTF_EXCEPT) | GTF_IND_VOLATILE))
- {
- /* Compare against any register to do null-check */
- CLANG_FORMAT_COMMENT_ANCHOR;
-
-#if defined(_TARGET_XARCH_)
- inst_TT_RV(INS_cmp, tree, REG_TMP_0, 0, EA_1BYTE);
- genDoneAddressable(tree, addrReg, RegSet::KEEP_REG);
-#elif CPU_LOAD_STORE_ARCH
- if (varTypeIsFloating(tree->TypeGet()))
- {
- genComputeAddressableFloat(tree, addrReg, RBM_NONE, RegSet::KEEP_REG, RBM_ALLFLOAT,
- RegSet::FREE_REG);
- }
- else
- {
- genComputeAddressable(tree, addrReg, RegSet::KEEP_REG, RBM_NONE, RegSet::FREE_REG);
- }
-#ifdef _TARGET_ARM_
- if (tree->gtFlags & GTF_IND_VOLATILE)
- {
- // Emit a memory barrier instruction after the load
- instGen_MemoryBarrier();
- }
-#endif
-#else
- NYI("TARGET");
-#endif
- }
- else
- {
- genDoneAddressable(tree, addrReg, RegSet::KEEP_REG);
- }
- }
- else
- {
- /* Generate the expression and throw it away */
- genCodeForTree(tree, RBM_ALL(tree->TypeGet()));
- if (tree->InReg())
- {
- gcInfo.gcMarkRegPtrVal(tree);
- }
- }
-#if FEATURE_STACK_FP_X87
- /* If the tree computed a value on the FP stack, pop the stack */
- if (genNumberTemps() > iTemps)
- {
- noway_assert(genNumberTemps() == iTemps + 1);
- genDiscardStackFP(tree);
- }
-#endif
- return;
- }
-
- noway_assert(tree->gtOper != GT_ASG);
-
- /* Walk the tree, just to mark any dead values appropriately */
-
- oper = tree->OperGet();
- kind = tree->OperKind();
-
- /* Is this a constant or leaf node? */
-
- if (kind & (GTK_CONST | GTK_LEAF))
- {
-#if FEATURE_STACK_FP_X87
- if (tree->IsRegVar() && isFloatRegType(tree->gtType) && tree->IsRegVarDeath())
- {
- genRegVarDeathStackFP(tree);
- FlatFPX87_Unload(&compCurFPState, tree->gtRegNum);
- }
-#endif
- genUpdateLife(tree);
- gcInfo.gcMarkRegPtrVal(tree);
- return;
- }
-
- /* Must be a 'simple' unary/binary operator */
-
- noway_assert(kind & GTK_SMPOP);
-
- if (tree->gtGetOp2IfPresent())
- {
- genEvalSideEffects(tree->gtOp.gtOp1);
-
- tree = tree->gtOp.gtOp2;
- goto AGAIN;
- }
- else
- {
- tree = tree->gtOp.gtOp1;
- if (tree)
- goto AGAIN;
- }
-}
-
-/*****************************************************************************
- *
- * A persistent pointer value is being overwritten, record it for the GC.
- *
- * tgt : the destination being written to
- * assignVal : the value being assigned (the source). It must currently be in a register.
- * tgtAddrReg : the set of registers being used by "tgt"
- *
- * Returns : the mask of the scratch register that was used.
- * RBM_NONE if a write-barrier is not needed.
- */
-
-regMaskTP CodeGen::WriteBarrier(GenTree* tgt, GenTree* assignVal, regMaskTP tgtAddrReg)
-{
- noway_assert(assignVal->InReg());
-
- GCInfo::WriteBarrierForm wbf = gcInfo.gcIsWriteBarrierCandidate(tgt, assignVal);
- if (wbf == GCInfo::WBF_NoBarrier)
- return RBM_NONE;
-
- regMaskTP resultRegMask = RBM_NONE;
-
- regNumber reg = assignVal->gtRegNum;
-
-#if defined(_TARGET_X86_) && NOGC_WRITE_BARRIERS
-#ifdef DEBUG
- if (wbf != GCInfo::WBF_NoBarrier_CheckNotHeapInDebug) // This one is always a call to a C++ method.
- {
-#endif
- const static int regToHelper[2][8] = {
- // If the target is known to be in managed memory
- {
- CORINFO_HELP_ASSIGN_REF_EAX, CORINFO_HELP_ASSIGN_REF_ECX, -1, CORINFO_HELP_ASSIGN_REF_EBX, -1,
- CORINFO_HELP_ASSIGN_REF_EBP, CORINFO_HELP_ASSIGN_REF_ESI, CORINFO_HELP_ASSIGN_REF_EDI,
- },
-
- // Don't know if the target is in managed memory
- {
- CORINFO_HELP_CHECKED_ASSIGN_REF_EAX, CORINFO_HELP_CHECKED_ASSIGN_REF_ECX, -1,
- CORINFO_HELP_CHECKED_ASSIGN_REF_EBX, -1, CORINFO_HELP_CHECKED_ASSIGN_REF_EBP,
- CORINFO_HELP_CHECKED_ASSIGN_REF_ESI, CORINFO_HELP_CHECKED_ASSIGN_REF_EDI,
- },
- };
-
- noway_assert(regToHelper[0][REG_EAX] == CORINFO_HELP_ASSIGN_REF_EAX);
- noway_assert(regToHelper[0][REG_ECX] == CORINFO_HELP_ASSIGN_REF_ECX);
- noway_assert(regToHelper[0][REG_EBX] == CORINFO_HELP_ASSIGN_REF_EBX);
- noway_assert(regToHelper[0][REG_ESP] == -1);
- noway_assert(regToHelper[0][REG_EBP] == CORINFO_HELP_ASSIGN_REF_EBP);
- noway_assert(regToHelper[0][REG_ESI] == CORINFO_HELP_ASSIGN_REF_ESI);
- noway_assert(regToHelper[0][REG_EDI] == CORINFO_HELP_ASSIGN_REF_EDI);
-
- noway_assert(regToHelper[1][REG_EAX] == CORINFO_HELP_CHECKED_ASSIGN_REF_EAX);
- noway_assert(regToHelper[1][REG_ECX] == CORINFO_HELP_CHECKED_ASSIGN_REF_ECX);
- noway_assert(regToHelper[1][REG_EBX] == CORINFO_HELP_CHECKED_ASSIGN_REF_EBX);
- noway_assert(regToHelper[1][REG_ESP] == -1);
- noway_assert(regToHelper[1][REG_EBP] == CORINFO_HELP_CHECKED_ASSIGN_REF_EBP);
- noway_assert(regToHelper[1][REG_ESI] == CORINFO_HELP_CHECKED_ASSIGN_REF_ESI);
- noway_assert(regToHelper[1][REG_EDI] == CORINFO_HELP_CHECKED_ASSIGN_REF_EDI);
-
- noway_assert((reg != REG_ESP) && (reg != REG_WRITE_BARRIER));
-
- /*
- Generate the following code:
-
- lea edx, tgt
- call write_barrier_helper_reg
-
- First grab the RBM_WRITE_BARRIER register for the target address.
- */
-
- regNumber rg1;
- bool trashOp1;
-
- if ((tgtAddrReg & RBM_WRITE_BARRIER) == 0)
- {
- rg1 = regSet.rsGrabReg(RBM_WRITE_BARRIER);
-
- regSet.rsMaskUsed |= RBM_WRITE_BARRIER;
- regSet.rsMaskLock |= RBM_WRITE_BARRIER;
-
- trashOp1 = false;
- }
- else
- {
- rg1 = REG_WRITE_BARRIER;
-
- trashOp1 = true;
- }
-
- noway_assert(rg1 == REG_WRITE_BARRIER);
-
- /* Generate "lea EDX, [addr-mode]" */
-
- noway_assert(tgt->gtType == TYP_REF);
- tgt->gtType = TYP_BYREF;
- inst_RV_TT(INS_lea, rg1, tgt, 0, EA_BYREF);
-
- /* Free up anything that was tied up by the LHS */
- genDoneAddressable(tgt, tgtAddrReg, RegSet::KEEP_REG);
-
- // In case "tgt" was a comma:
- tgt = tgt->gtEffectiveVal();
-
- regTracker.rsTrackRegTrash(rg1);
- gcInfo.gcMarkRegSetNpt(genRegMask(rg1));
- gcInfo.gcMarkRegPtrVal(rg1, TYP_BYREF);
-
- /* Call the proper vm helper */
-
- // enforced by gcIsWriteBarrierCandidate
- noway_assert(tgt->gtOper == GT_IND || tgt->gtOper == GT_CLS_VAR);
-
- unsigned tgtAnywhere = 0;
- if ((tgt->gtOper == GT_IND) &&
- ((tgt->gtFlags & GTF_IND_TGTANYWHERE) || (tgt->gtOp.gtOp1->TypeGet() == TYP_I_IMPL)))
- {
- tgtAnywhere = 1;
- }
-
- int helper = regToHelper[tgtAnywhere][reg];
- resultRegMask = genRegMask(reg);
-
- gcInfo.gcMarkRegSetNpt(RBM_WRITE_BARRIER); // byref EDX is killed in the call
-
- genEmitHelperCall(helper,
- 0, // argSize
- EA_PTRSIZE); // retSize
-
- if (!trashOp1)
- {
- regSet.rsMaskUsed &= ~RBM_WRITE_BARRIER;
- regSet.rsMaskLock &= ~RBM_WRITE_BARRIER;
- }
-
- return resultRegMask;
-
-#ifdef DEBUG
- }
- else
-#endif
-#endif // defined(_TARGET_X86_) && NOGC_WRITE_BARRIERS
-
-#if defined(DEBUG) || !(defined(_TARGET_X86_) && NOGC_WRITE_BARRIERS)
- {
- /*
- Generate the following code (or its equivalent on the given target):
-
- mov arg1, srcReg
- lea arg0, tgt
- call write_barrier_helper
-
- First, setup REG_ARG_1 with the GC ref that we are storing via the Write Barrier
- */
-
- if (reg != REG_ARG_1)
- {
- // We may need to spill whatever is in the ARG_1 register
- //
- if ((regSet.rsMaskUsed & RBM_ARG_1) != 0)
- {
- regSet.rsSpillReg(REG_ARG_1);
- }
-
- inst_RV_RV(INS_mov, REG_ARG_1, reg, TYP_REF);
- }
- resultRegMask = RBM_ARG_1;
-
- regTracker.rsTrackRegTrash(REG_ARG_1);
- gcInfo.gcMarkRegSetNpt(REG_ARG_1);
- gcInfo.gcMarkRegSetGCref(RBM_ARG_1); // gcref in ARG_1
-
- bool free_arg1 = false;
- if ((regSet.rsMaskUsed & RBM_ARG_1) == 0)
- {
- regSet.rsMaskUsed |= RBM_ARG_1;
- free_arg1 = true;
- }
-
- // Then we setup REG_ARG_0 with the target address to store into via the Write Barrier
-
- /* Generate "lea R0, [addr-mode]" */
-
- noway_assert(tgt->gtType == TYP_REF);
- tgt->gtType = TYP_BYREF;
-
- tgtAddrReg = genKeepAddressable(tgt, tgtAddrReg);
-
- // We may need to spill whatever is in the ARG_0 register
- //
- if (((tgtAddrReg & RBM_ARG_0) == 0) && // tgtAddrReg does not contain REG_ARG_0
- ((regSet.rsMaskUsed & RBM_ARG_0) != 0) && // and regSet.rsMaskUsed contains REG_ARG_0
- (reg != REG_ARG_0)) // unless REG_ARG_0 contains the REF value being written, which we're finished with.
- {
- regSet.rsSpillReg(REG_ARG_0);
- }
-
- inst_RV_TT(INS_lea, REG_ARG_0, tgt, 0, EA_BYREF);
-
- /* Free up anything that was tied up by the LHS */
- genDoneAddressable(tgt, tgtAddrReg, RegSet::KEEP_REG);
-
- regTracker.rsTrackRegTrash(REG_ARG_0);
- gcInfo.gcMarkRegSetNpt(REG_ARG_0);
- gcInfo.gcMarkRegSetByref(RBM_ARG_0); // byref in ARG_0
-
-#ifdef _TARGET_ARM_
-#if NOGC_WRITE_BARRIERS
- // Finally, we may be required to spill whatever is in the further argument registers
- // trashed by the call. The write barrier trashes some further registers --
- // either the standard volatile var set, or, if we're using assembly barriers, a more specialized set.
-
- regMaskTP volatileRegsTrashed = RBM_CALLEE_TRASH_NOGC;
-#else
- regMaskTP volatileRegsTrashed = RBM_CALLEE_TRASH;
-#endif
- // Spill any other registers trashed by the write barrier call and currently in use.
- regMaskTP mustSpill = (volatileRegsTrashed & regSet.rsMaskUsed & ~(RBM_ARG_0 | RBM_ARG_1));
- if (mustSpill)
- regSet.rsSpillRegs(mustSpill);
-#endif // _TARGET_ARM_
-
- bool free_arg0 = false;
- if ((regSet.rsMaskUsed & RBM_ARG_0) == 0)
- {
- regSet.rsMaskUsed |= RBM_ARG_0;
- free_arg0 = true;
- }
-
- // genEmitHelperCall might need to grab a register
- // so don't let it spill one of the arguments
- //
- regMaskTP reallyUsedRegs = RBM_NONE;
- regSet.rsLockReg(RBM_ARG_0 | RBM_ARG_1, &reallyUsedRegs);
-
- genGCWriteBarrier(tgt, wbf);
-
- regSet.rsUnlockReg(RBM_ARG_0 | RBM_ARG_1, reallyUsedRegs);
- gcInfo.gcMarkRegSetNpt(RBM_ARG_0 | RBM_ARG_1); // byref ARG_0 and reg ARG_1 are killed by the call
-
- if (free_arg0)
- {
- regSet.rsMaskUsed &= ~RBM_ARG_0;
- }
- if (free_arg1)
- {
- regSet.rsMaskUsed &= ~RBM_ARG_1;
- }
-
- return resultRegMask;
- }
-#endif // defined(DEBUG) || !(defined(_TARGET_X86_) && NOGC_WRITE_BARRIERS)
-}
-
-#ifdef _TARGET_X86_
-/*****************************************************************************
- *
- * Generate the appropriate conditional jump(s) right after the low 32 bits
- * of two long values have been compared.
- */
-
-void CodeGen::genJccLongHi(genTreeOps cmp, BasicBlock* jumpTrue, BasicBlock* jumpFalse, bool isUnsigned)
-{
- if (cmp != GT_NE)
- {
- jumpFalse->bbFlags |= BBF_JMP_TARGET | BBF_HAS_LABEL;
- }
-
- switch (cmp)
- {
- case GT_EQ:
- inst_JMP(EJ_jne, jumpFalse);
- break;
-
- case GT_NE:
- inst_JMP(EJ_jne, jumpTrue);
- break;
-
- case GT_LT:
- case GT_LE:
- if (isUnsigned)
- {
- inst_JMP(EJ_ja, jumpFalse);
- inst_JMP(EJ_jb, jumpTrue);
- }
- else
- {
- inst_JMP(EJ_jg, jumpFalse);
- inst_JMP(EJ_jl, jumpTrue);
- }
- break;
-
- case GT_GE:
- case GT_GT:
- if (isUnsigned)
- {
- inst_JMP(EJ_jb, jumpFalse);
- inst_JMP(EJ_ja, jumpTrue);
- }
- else
- {
- inst_JMP(EJ_jl, jumpFalse);
- inst_JMP(EJ_jg, jumpTrue);
- }
- break;
-
- default:
- noway_assert(!"expected a comparison operator");
- }
-}
-
-/*****************************************************************************
- *
- * Generate the appropriate conditional jump(s) right after the high 32 bits
- * of two long values have been compared.
- */
-
-void CodeGen::genJccLongLo(genTreeOps cmp, BasicBlock* jumpTrue, BasicBlock* jumpFalse)
-{
- switch (cmp)
- {
- case GT_EQ:
- inst_JMP(EJ_je, jumpTrue);
- break;
-
- case GT_NE:
- inst_JMP(EJ_jne, jumpTrue);
- break;
-
- case GT_LT:
- inst_JMP(EJ_jb, jumpTrue);
- break;
-
- case GT_LE:
- inst_JMP(EJ_jbe, jumpTrue);
- break;
-
- case GT_GE:
- inst_JMP(EJ_jae, jumpTrue);
- break;
-
- case GT_GT:
- inst_JMP(EJ_ja, jumpTrue);
- break;
-
- default:
- noway_assert(!"expected comparison");
- }
-}
-#elif defined(_TARGET_ARM_)
-/*****************************************************************************
-*
-* Generate the appropriate conditional jump(s) right after the low 32 bits
-* of two long values have been compared.
-*/
-
-void CodeGen::genJccLongHi(genTreeOps cmp, BasicBlock* jumpTrue, BasicBlock* jumpFalse, bool isUnsigned)
-{
- if (cmp != GT_NE)
- {
- jumpFalse->bbFlags |= BBF_JMP_TARGET | BBF_HAS_LABEL;
- }
-
- switch (cmp)
- {
- case GT_EQ:
- inst_JMP(EJ_ne, jumpFalse);
- break;
-
- case GT_NE:
- inst_JMP(EJ_ne, jumpTrue);
- break;
-
- case GT_LT:
- case GT_LE:
- if (isUnsigned)
- {
- inst_JMP(EJ_hi, jumpFalse);
- inst_JMP(EJ_lo, jumpTrue);
- }
- else
- {
- inst_JMP(EJ_gt, jumpFalse);
- inst_JMP(EJ_lt, jumpTrue);
- }
- break;
-
- case GT_GE:
- case GT_GT:
- if (isUnsigned)
- {
- inst_JMP(EJ_lo, jumpFalse);
- inst_JMP(EJ_hi, jumpTrue);
- }
- else
- {
- inst_JMP(EJ_lt, jumpFalse);
- inst_JMP(EJ_gt, jumpTrue);
- }
- break;
-
- default:
- noway_assert(!"expected a comparison operator");
- }
-}
-
-/*****************************************************************************
-*
-* Generate the appropriate conditional jump(s) right after the high 32 bits
-* of two long values have been compared.
-*/
-
-void CodeGen::genJccLongLo(genTreeOps cmp, BasicBlock* jumpTrue, BasicBlock* jumpFalse)
-{
- switch (cmp)
- {
- case GT_EQ:
- inst_JMP(EJ_eq, jumpTrue);
- break;
-
- case GT_NE:
- inst_JMP(EJ_ne, jumpTrue);
- break;
-
- case GT_LT:
- inst_JMP(EJ_lo, jumpTrue);
- break;
-
- case GT_LE:
- inst_JMP(EJ_ls, jumpTrue);
- break;
-
- case GT_GE:
- inst_JMP(EJ_hs, jumpTrue);
- break;
-
- case GT_GT:
- inst_JMP(EJ_hi, jumpTrue);
- break;
-
- default:
- noway_assert(!"expected comparison");
- }
-}
-#endif
-/*****************************************************************************
- *
- * Called by genCondJump() for TYP_LONG.
- */
-
-void CodeGen::genCondJumpLng(GenTree* cond, BasicBlock* jumpTrue, BasicBlock* jumpFalse, bool bFPTransition)
-{
- noway_assert(jumpTrue && jumpFalse);
- noway_assert((cond->gtFlags & GTF_REVERSE_OPS) == false); // Done in genCondJump()
- noway_assert(cond->gtOp.gtOp1->gtType == TYP_LONG);
-
- GenTree* op1 = cond->gtOp.gtOp1;
- GenTree* op2 = cond->gtOp.gtOp2;
- genTreeOps cmp = cond->OperGet();
-
- regMaskTP addrReg;
-
- /* Are we comparing against a constant? */
-
- if (op2->gtOper == GT_CNS_LNG)
- {
- __int64 lval = op2->gtLngCon.gtLconVal;
- regNumber rTmp;
-
- // We're "done" evaluating op2; let's strip any commas off op1 before we
- // evaluate it.
- op1 = genCodeForCommaTree(op1);
-
- /* We can generate better code for some special cases */
- instruction ins = INS_invalid;
- bool useIncToSetFlags = false;
- bool specialCaseCmp = false;
-
- if (cmp == GT_EQ)
- {
- if (lval == 0)
- {
- /* op1 == 0 */
- ins = INS_OR;
- useIncToSetFlags = false;
- specialCaseCmp = true;
- }
- else if (lval == -1)
- {
- /* op1 == -1 */
- ins = INS_AND;
- useIncToSetFlags = true;
- specialCaseCmp = true;
- }
- }
- else if (cmp == GT_NE)
- {
- if (lval == 0)
- {
- /* op1 != 0 */
- ins = INS_OR;
- useIncToSetFlags = false;
- specialCaseCmp = true;
- }
- else if (lval == -1)
- {
- /* op1 != -1 */
- ins = INS_AND;
- useIncToSetFlags = true;
- specialCaseCmp = true;
- }
- }
-
- if (specialCaseCmp)
- {
- /* Make the comparand addressable */
-
- addrReg = genMakeRvalueAddressable(op1, 0, RegSet::KEEP_REG, false, true);
-
- regMaskTP tmpMask = regSet.rsRegMaskCanGrab();
- insFlags flags = useIncToSetFlags ? INS_FLAGS_DONT_CARE : INS_FLAGS_SET;
-
- if (op1->InReg())
- {
- regPairNo regPair = op1->gtRegPair;
- regNumber rLo = genRegPairLo(regPair);
- regNumber rHi = genRegPairHi(regPair);
- if (tmpMask & genRegMask(rLo))
- {
- rTmp = rLo;
- }
- else if (tmpMask & genRegMask(rHi))
- {
- rTmp = rHi;
- rHi = rLo;
- }
- else
- {
- rTmp = regSet.rsGrabReg(tmpMask);
- inst_RV_RV(INS_mov, rTmp, rLo, TYP_INT);
- }
-
- /* The register is now trashed */
- regTracker.rsTrackRegTrash(rTmp);
-
- if (rHi != REG_STK)
- {
- /* Set the flags using INS_AND | INS_OR */
- inst_RV_RV(ins, rTmp, rHi, TYP_INT, EA_4BYTE, flags);
- }
- else
- {
- /* Set the flags using INS_AND | INS_OR */
- inst_RV_TT(ins, rTmp, op1, 4, EA_4BYTE, flags);
- }
- }
- else // op1 is not in a register.
- {
- rTmp = regSet.rsGrabReg(tmpMask);
-
- /* Load the low 32-bits of op1 */
- inst_RV_TT(ins_Load(TYP_INT), rTmp, op1, 0);
-
- /* The register is now trashed */
- regTracker.rsTrackRegTrash(rTmp);
-
- /* Set the flags using INS_AND | INS_OR */
- inst_RV_TT(ins, rTmp, op1, 4, EA_4BYTE, flags);
- }
-
- /* Free up the addrReg(s) if any */
- genDoneAddressable(op1, addrReg, RegSet::KEEP_REG);
-
- /* compares against -1, also requires an an inc instruction */
- if (useIncToSetFlags)
- {
- /* Make sure the inc will set the flags */
- assert(cond->gtSetFlags());
- genIncRegBy(rTmp, 1, cond, TYP_INT);
- }
-
-#if FEATURE_STACK_FP_X87
- // We may need a transition block
- if (bFPTransition)
- {
- jumpTrue = genTransitionBlockStackFP(&compCurFPState, compiler->compCurBB, jumpTrue);
- }
-#endif
- emitJumpKind jmpKind = genJumpKindForOper(cmp, CK_SIGNED);
- inst_JMP(jmpKind, jumpTrue);
- }
- else // specialCaseCmp == false
- {
- /* Make the comparand addressable */
- addrReg = genMakeRvalueAddressable(op1, 0, RegSet::FREE_REG, false, true);
-
- /* Compare the high part first */
-
- int ival = (int)(lval >> 32);
-
- /* Comparing a register against 0 is easier */
-
- if (!ival && (op1->InReg()) && (rTmp = genRegPairHi(op1->gtRegPair)) != REG_STK)
- {
- /* Generate 'test rTmp, rTmp' */
- instGen_Compare_Reg_To_Zero(emitTypeSize(op1->TypeGet()), rTmp); // set flags
- }
- else
- {
- if (!(op1->InReg()) && (op1->gtOper == GT_CNS_LNG))
- {
- /* Special case: comparison of two constants */
- // Needed as gtFoldExpr() doesn't fold longs
-
- noway_assert(addrReg == 0);
- int op1_hiword = (int)(op1->gtLngCon.gtLconVal >> 32);
-
- /* Get the constant operand into a register */
- rTmp = genGetRegSetToIcon(op1_hiword);
-
- /* Generate 'cmp rTmp, ival' */
-
- inst_RV_IV(INS_cmp, rTmp, ival, EA_4BYTE);
- }
- else
- {
- /* Generate 'cmp op1, ival' */
-
- inst_TT_IV(INS_cmp, op1, ival, 4);
- }
- }
-
-#if FEATURE_STACK_FP_X87
- // We may need a transition block
- if (bFPTransition)
- {
- jumpTrue = genTransitionBlockStackFP(&compCurFPState, compiler->compCurBB, jumpTrue);
- }
-#endif
- /* Generate the appropriate jumps */
-
- if (cond->gtFlags & GTF_UNSIGNED)
- genJccLongHi(cmp, jumpTrue, jumpFalse, true);
- else
- genJccLongHi(cmp, jumpTrue, jumpFalse);
-
- /* Compare the low part second */
-
- ival = (int)lval;
-
- /* Comparing a register against 0 is easier */
-
- if (!ival && (op1->InReg()) && (rTmp = genRegPairLo(op1->gtRegPair)) != REG_STK)
- {
- /* Generate 'test rTmp, rTmp' */
- instGen_Compare_Reg_To_Zero(emitTypeSize(op1->TypeGet()), rTmp); // set flags
- }
- else
- {
- if (!(op1->InReg()) && (op1->gtOper == GT_CNS_LNG))
- {
- /* Special case: comparison of two constants */
- // Needed as gtFoldExpr() doesn't fold longs
-
- noway_assert(addrReg == 0);
- int op1_loword = (int)op1->gtLngCon.gtLconVal;
-
- /* get the constant operand into a register */
- rTmp = genGetRegSetToIcon(op1_loword);
-
- /* Generate 'cmp rTmp, ival' */
-
- inst_RV_IV(INS_cmp, rTmp, ival, EA_4BYTE);
- }
- else
- {
- /* Generate 'cmp op1, ival' */
-
- inst_TT_IV(INS_cmp, op1, ival, 0);
- }
- }
-
- /* Generate the appropriate jumps */
- genJccLongLo(cmp, jumpTrue, jumpFalse);
-
- genDoneAddressable(op1, addrReg, RegSet::FREE_REG);
- }
- }
- else // (op2->gtOper != GT_CNS_LNG)
- {
-
- /* The operands would be reversed by physically swapping them */
-
- noway_assert((cond->gtFlags & GTF_REVERSE_OPS) == 0);
-
- /* Generate the first operand into a register pair */
-
- genComputeRegPair(op1, REG_PAIR_NONE, op2->gtRsvdRegs, RegSet::KEEP_REG, false);
- noway_assert(op1->InReg());
-
-#if CPU_LOAD_STORE_ARCH
- /* Generate the second operand into a register pair */
- // Fix 388442 ARM JitStress WP7
- genComputeRegPair(op2, REG_PAIR_NONE, genRegPairMask(op1->gtRegPair), RegSet::KEEP_REG, false);
- noway_assert(op2->InReg());
- regSet.rsLockUsedReg(genRegPairMask(op2->gtRegPair));
-#else
- /* Make the second operand addressable */
-
- addrReg = genMakeRvalueAddressable(op2, RBM_ALLINT & ~genRegPairMask(op1->gtRegPair), RegSet::KEEP_REG, false);
-#endif
- /* Make sure the first operand hasn't been spilled */
-
- genRecoverRegPair(op1, REG_PAIR_NONE, RegSet::KEEP_REG);
- noway_assert(op1->InReg());
-
- regPairNo regPair = op1->gtRegPair;
-
-#if !CPU_LOAD_STORE_ARCH
- /* Make sure 'op2' is still addressable while avoiding 'op1' (regPair) */
-
- addrReg = genKeepAddressable(op2, addrReg, genRegPairMask(regPair));
-#endif
-
-#if FEATURE_STACK_FP_X87
- // We may need a transition block
- if (bFPTransition)
- {
- jumpTrue = genTransitionBlockStackFP(&compCurFPState, compiler->compCurBB, jumpTrue);
- }
-#endif
-
- /* Perform the comparison - high parts */
-
- inst_RV_TT(INS_cmp, genRegPairHi(regPair), op2, 4);
-
- if (cond->gtFlags & GTF_UNSIGNED)
- genJccLongHi(cmp, jumpTrue, jumpFalse, true);
- else
- genJccLongHi(cmp, jumpTrue, jumpFalse);
-
- /* Compare the low parts */
-
- inst_RV_TT(INS_cmp, genRegPairLo(regPair), op2, 0);
- genJccLongLo(cmp, jumpTrue, jumpFalse);
-
- /* Free up anything that was tied up by either operand */
- CLANG_FORMAT_COMMENT_ANCHOR;
-
-#if CPU_LOAD_STORE_ARCH
-
- // Fix 388442 ARM JitStress WP7
- regSet.rsUnlockUsedReg(genRegPairMask(op2->gtRegPair));
- genReleaseRegPair(op2);
-#else
- genDoneAddressable(op2, addrReg, RegSet::KEEP_REG);
-#endif
- genReleaseRegPair(op1);
- }
-}
-
-/*****************************************************************************
- * gen_fcomp_FN, gen_fcomp_FS_TT, gen_fcompp_FS
- * Called by genCondJumpFlt() to generate the fcomp instruction appropriate
- * to the architecture we're running on.
- *
- * P5:
- * gen_fcomp_FN: fcomp ST(0), stk
- * gen_fcomp_FS_TT: fcomp ST(0), addr
- * gen_fcompp_FS: fcompp
- * These are followed by fnstsw, sahf to get the flags in EFLAGS.
- *
- * P6:
- * gen_fcomp_FN: fcomip ST(0), stk
- * gen_fcomp_FS_TT: fld addr, fcomip ST(0), ST(1), fstp ST(0)
- * (and reverse the branch condition since addr comes first)
- * gen_fcompp_FS: fcomip, fstp
- * These instructions will correctly set the EFLAGS register.
- *
- * Return value: These functions return true if the instruction has
- * already placed its result in the EFLAGS register.
- */
-
-bool CodeGen::genUse_fcomip()
-{
- return compiler->opts.compUseFCOMI;
-}
-
-/*****************************************************************************
- *
- * Sets the flag for the TYP_INT/TYP_REF comparison.
- * We try to use the flags if they have already been set by a prior
- * instruction.
- * eg. i++; if(i<0) {} Here, the "i++;" will have set the sign flag. We don't
- * need to compare again with zero. Just use a "INS_js"
- *
- * Returns the flags the following jump/set instruction should use.
- */
-
-emitJumpKind CodeGen::genCondSetFlags(GenTree* cond)
-{
- noway_assert(cond->OperIsCompare());
- noway_assert(varTypeIsI(genActualType(cond->gtOp.gtOp1->gtType)));
-
- GenTree* op1 = cond->gtOp.gtOp1;
- GenTree* op2 = cond->gtOp.gtOp2;
- genTreeOps cmp = cond->OperGet();
-
- if (cond->gtFlags & GTF_REVERSE_OPS)
- {
- /* Don't forget to modify the condition as well */
-
- cond->gtOp.gtOp1 = op2;
- cond->gtOp.gtOp2 = op1;
- cond->SetOper(GenTree::SwapRelop(cmp));
- cond->gtFlags &= ~GTF_REVERSE_OPS;
-
- /* Get hold of the new values */
-
- cmp = cond->OperGet();
- op1 = cond->gtOp.gtOp1;
- op2 = cond->gtOp.gtOp2;
- }
-
- // Note that op1's type may get bashed. So save it early
-
- var_types op1Type = op1->TypeGet();
- bool unsignedCmp = (cond->gtFlags & GTF_UNSIGNED) != 0;
- emitAttr size = EA_UNKNOWN;
-
- regMaskTP regNeed;
- regMaskTP addrReg1 = RBM_NONE;
- regMaskTP addrReg2 = RBM_NONE;
- emitJumpKind jumpKind = EJ_COUNT; // Initialize with an invalid value
-
- bool byteCmp;
- bool shortCmp;
-
- regMaskTP newLiveMask;
- regNumber op1Reg;
-
- /* Are we comparing against a constant? */
-
- if (op2->IsCnsIntOrI())
- {
- ssize_t ival = op2->gtIntConCommon.IconValue();
-
- /* unsigned less than comparisons with 1 ('< 1' )
- should be transformed into '== 0' to potentially
- suppress a tst instruction.
- */
- if ((ival == 1) && (cmp == GT_LT) && unsignedCmp)
- {
- op2->gtIntCon.gtIconVal = ival = 0;
- cond->gtOper = cmp = GT_EQ;
- }
-
- /* Comparisons against 0 can be easier */
-
- if (ival == 0)
- {
- // if we can safely change the comparison to unsigned we do so
- if (!unsignedCmp && varTypeIsSmall(op1->TypeGet()) && varTypeIsUnsigned(op1->TypeGet()))
- {
- unsignedCmp = true;
- }
-
- /* unsigned comparisons with 0 should be transformed into
- '==0' or '!= 0' to potentially suppress a tst instruction. */
-
- if (unsignedCmp)
- {
- if (cmp == GT_GT)
- cond->gtOper = cmp = GT_NE;
- else if (cmp == GT_LE)
- cond->gtOper = cmp = GT_EQ;
- }
-
- /* Is this a simple zero/non-zero test? */
-
- if (cmp == GT_EQ || cmp == GT_NE)
- {
- /* Is the operand an "AND" operation? */
-
- if (op1->gtOper == GT_AND)
- {
- GenTree* an1 = op1->gtOp.gtOp1;
- GenTree* an2 = op1->gtOp.gtOp2;
-
- /* Check for the case "expr & icon" */
-
- if (an2->IsIntCnsFitsInI32())
- {
- int iVal = (int)an2->gtIntCon.gtIconVal;
-
- /* make sure that constant is not out of an1's range */
-
- switch (an1->gtType)
- {
- case TYP_BOOL:
- case TYP_BYTE:
- if (iVal & 0xffffff00)
- goto NO_TEST_FOR_AND;
- break;
- case TYP_USHORT:
- case TYP_SHORT:
- if (iVal & 0xffff0000)
- goto NO_TEST_FOR_AND;
- break;
- default:
- break;
- }
-
- if (an1->IsCnsIntOrI())
- {
- // Special case - Both operands of AND are consts
- genComputeReg(an1, 0, RegSet::EXACT_REG, RegSet::KEEP_REG);
- addrReg1 = genRegMask(an1->gtRegNum);
- }
- else
- {
- addrReg1 = genMakeAddressable(an1, RBM_NONE, RegSet::KEEP_REG, true);
- }
-#if CPU_LOAD_STORE_ARCH
- if ((an1->InReg()) == 0)
- {
- genComputeAddressable(an1, addrReg1, RegSet::KEEP_REG, RBM_NONE, RegSet::KEEP_REG);
- if (arm_Valid_Imm_For_Alu(iVal))
- {
- inst_RV_IV(INS_TEST, an1->gtRegNum, iVal, emitActualTypeSize(an1->gtType));
- }
- else
- {
- regNumber regTmp = regSet.rsPickFreeReg();
- instGen_Set_Reg_To_Imm(EmitSize(an2), regTmp, iVal);
- inst_RV_RV(INS_TEST, an1->gtRegNum, regTmp);
- }
- genReleaseReg(an1);
- addrReg1 = RBM_NONE;
- }
- else
-#endif
- {
-#ifdef _TARGET_XARCH_
- // Check to see if we can use a smaller immediate.
- if ((an1->InReg()) && ((iVal & 0x0000FFFF) == iVal))
- {
- var_types testType =
- (var_types)(((iVal & 0x000000FF) == iVal) ? TYP_UBYTE : TYP_USHORT);
-#if CPU_HAS_BYTE_REGS
- // if we don't have byte-able register, switch to the 2-byte form
- if ((testType == TYP_UBYTE) && !(genRegMask(an1->gtRegNum) & RBM_BYTE_REGS))
- {
- testType = TYP_USHORT;
- }
-#endif // CPU_HAS_BYTE_REGS
-
- inst_TT_IV(INS_TEST, an1, iVal, testType);
- }
- else
-#endif // _TARGET_XARCH_
- {
- inst_TT_IV(INS_TEST, an1, iVal);
- }
- }
-
- goto DONE;
-
- NO_TEST_FOR_AND:;
- }
-
- // TODO: Check for other cases that can generate 'test',
- // TODO: also check for a 64-bit integer zero test which
- // TODO: could generate 'or lo, hi' followed by jz/jnz.
- }
- }
-
- // See what Jcc instruction we would use if we can take advantage of
- // the knowledge of EFLAGs.
-
- if (unsignedCmp)
- {
- /*
- Unsigned comparison to 0. Using this table:
-
- ----------------------------------------------------
- | Comparison | Flags Checked | Instruction Used |
- ----------------------------------------------------
- | == 0 | ZF = 1 | je |
- ----------------------------------------------------
- | != 0 | ZF = 0 | jne |
- ----------------------------------------------------
- | < 0 | always FALSE | N/A |
- ----------------------------------------------------
- | <= 0 | ZF = 1 | je |
- ----------------------------------------------------
- | >= 0 | always TRUE | N/A |
- ----------------------------------------------------
- | > 0 | ZF = 0 | jne |
- ----------------------------------------------------
- */
- switch (cmp)
- {
-#ifdef _TARGET_ARM_
- case GT_EQ:
- jumpKind = EJ_eq;
- break;
- case GT_NE:
- jumpKind = EJ_ne;
- break;
- case GT_LT:
- jumpKind = EJ_NONE;
- break;
- case GT_LE:
- jumpKind = EJ_eq;
- break;
- case GT_GE:
- jumpKind = EJ_NONE;
- break;
- case GT_GT:
- jumpKind = EJ_ne;
- break;
-#elif defined(_TARGET_X86_)
- case GT_EQ:
- jumpKind = EJ_je;
- break;
- case GT_NE:
- jumpKind = EJ_jne;
- break;
- case GT_LT:
- jumpKind = EJ_NONE;
- break;
- case GT_LE:
- jumpKind = EJ_je;
- break;
- case GT_GE:
- jumpKind = EJ_NONE;
- break;
- case GT_GT:
- jumpKind = EJ_jne;
- break;
-#endif // TARGET
- default:
- noway_assert(!"Unexpected comparison OpCode");
- break;
- }
- }
- else
- {
- /*
- Signed comparison to 0. Using this table:
-
- -----------------------------------------------------
- | Comparison | Flags Checked | Instruction Used |
- -----------------------------------------------------
- | == 0 | ZF = 1 | je |
- -----------------------------------------------------
- | != 0 | ZF = 0 | jne |
- -----------------------------------------------------
- | < 0 | SF = 1 | js |
- -----------------------------------------------------
- | <= 0 | N/A | N/A |
- -----------------------------------------------------
- | >= 0 | SF = 0 | jns |
- -----------------------------------------------------
- | > 0 | N/A | N/A |
- -----------------------------------------------------
- */
-
- switch (cmp)
- {
-#ifdef _TARGET_ARM_
- case GT_EQ:
- jumpKind = EJ_eq;
- break;
- case GT_NE:
- jumpKind = EJ_ne;
- break;
- case GT_LT:
- jumpKind = EJ_mi;
- break;
- case GT_LE:
- jumpKind = EJ_NONE;
- break;
- case GT_GE:
- jumpKind = EJ_pl;
- break;
- case GT_GT:
- jumpKind = EJ_NONE;
- break;
-#elif defined(_TARGET_X86_)
- case GT_EQ:
- jumpKind = EJ_je;
- break;
- case GT_NE:
- jumpKind = EJ_jne;
- break;
- case GT_LT:
- jumpKind = EJ_js;
- break;
- case GT_LE:
- jumpKind = EJ_NONE;
- break;
- case GT_GE:
- jumpKind = EJ_jns;
- break;
- case GT_GT:
- jumpKind = EJ_NONE;
- break;
-#endif // TARGET
- default:
- noway_assert(!"Unexpected comparison OpCode");
- break;
- }
- assert(jumpKind == genJumpKindForOper(cmp, CK_LOGICAL));
- }
- assert(jumpKind != EJ_COUNT); // Ensure that it was assigned a valid value above
-
- /* Is the value a simple local variable? */
-
- if (op1->gtOper == GT_LCL_VAR)
- {
- /* Is the flags register set to the value? */
-
- if (genFlagsAreVar(op1->gtLclVarCommon.gtLclNum))
- {
- if (jumpKind != EJ_NONE)
- {
- addrReg1 = RBM_NONE;
- genUpdateLife(op1);
- goto DONE_FLAGS;
- }
- }
- }
-
- /* Make the comparand addressable */
- addrReg1 = genMakeRvalueAddressable(op1, RBM_NONE, RegSet::KEEP_REG, false, true);
-
- /* Are the condition flags set based on the value? */
-
- unsigned flags = (op1->gtFlags & GTF_ZSF_SET);
-
- if (op1->InReg())
- {
- if (genFlagsAreReg(op1->gtRegNum))
- {
- flags |= GTF_ZSF_SET;
- }
- }
-
- if (flags)
- {
- if (jumpKind != EJ_NONE)
- {
- goto DONE_FLAGS;
- }
- }
-
- /* Is the value in a register? */
-
- if (op1->InReg())
- {
- regNumber reg = op1->gtRegNum;
-
- /* With a 'test' we can do any signed test or any test for equality */
-
- if (!(cond->gtFlags & GTF_UNSIGNED) || cmp == GT_EQ || cmp == GT_NE)
- {
- emitAttr compareSize = emitTypeSize(op1->TypeGet());
-
- // If we have an GT_REG_VAR then the register will be properly sign/zero extended
- // But only up to 4 bytes
- if ((op1->gtOper == GT_REG_VAR) && (compareSize < EA_4BYTE))
- {
- compareSize = EA_4BYTE;
- }
-
-#if CPU_HAS_BYTE_REGS
- // Make sure if we require a byte compare that we have a byte-able register
- if ((compareSize != EA_1BYTE) || ((genRegMask(op1->gtRegNum) & RBM_BYTE_REGS) != 0))
-#endif // CPU_HAS_BYTE_REGS
- {
- /* Generate 'test reg, reg' */
- instGen_Compare_Reg_To_Zero(compareSize, reg);
- goto DONE;
- }
- }
- }
- }
-
- else // if (ival != 0)
- {
- bool smallOk = true;
-
- /* make sure that constant is not out of op1's range
- if it is, we need to perform an int with int comparison
- and therefore, we set smallOk to false, so op1 gets loaded
- into a register
- */
-
- /* If op1 is TYP_SHORT, and is followed by an unsigned
- * comparison, we can use smallOk. But we don't know which
- * flags will be needed. This probably doesn't happen often.
- */
- var_types gtType = op1->TypeGet();
-
- switch (gtType)
- {
- case TYP_BYTE:
- if (ival != (signed char)ival)
- smallOk = false;
- break;
- case TYP_BOOL:
- case TYP_UBYTE:
- if (ival != (unsigned char)ival)
- smallOk = false;
- break;
-
- case TYP_SHORT:
- if (ival != (signed short)ival)
- smallOk = false;
- break;
- case TYP_USHORT:
- if (ival != (unsigned short)ival)
- smallOk = false;
- break;
-
-#ifdef _TARGET_64BIT_
- case TYP_INT:
- if (!FitsIn<INT32>(ival))
- smallOk = false;
- break;
- case TYP_UINT:
- if (!FitsIn<UINT32>(ival))
- smallOk = false;
- break;
-#endif // _TARGET_64BIT_
-
- default:
- break;
- }
-
- if (smallOk && // constant is in op1's range
- !unsignedCmp && // signed comparison
- varTypeIsSmall(gtType) && // smalltype var
- varTypeIsUnsigned(gtType)) // unsigned type
- {
- unsignedCmp = true;
- }
-
- /* Make the comparand addressable */
- addrReg1 = genMakeRvalueAddressable(op1, RBM_NONE, RegSet::KEEP_REG, false, smallOk);
- }
-
- /* Special case: comparison of two constants */
-
- // Needed if Importer doesn't call gtFoldExpr()
-
- if (!(op1->InReg()) && (op1->IsCnsIntOrI()))
- {
- // noway_assert(compiler->opts.MinOpts() || compiler->opts.compDbgCode);
-
- /* Workaround: get the constant operand into a register */
- genComputeReg(op1, RBM_NONE, RegSet::ANY_REG, RegSet::KEEP_REG);
-
- noway_assert(addrReg1 == RBM_NONE);
- noway_assert(op1->InReg());
-
- addrReg1 = genRegMask(op1->gtRegNum);
- }
-
- /* Compare the operand against the constant */
-
- if (op2->IsIconHandle())
- {
- inst_TT_IV(INS_cmp, op1, ival, 0, EA_HANDLE_CNS_RELOC);
- }
- else
- {
- inst_TT_IV(INS_cmp, op1, ival);
- }
- goto DONE;
- }
-
- //---------------------------------------------------------------------
- //
- // We reach here if op2 was not a GT_CNS_INT
- //
-
- byteCmp = false;
- shortCmp = false;
-
- if (op1Type == op2->gtType)
- {
- shortCmp = varTypeIsShort(op1Type);
- byteCmp = varTypeIsByte(op1Type);
- }
-
- noway_assert(op1->gtOper != GT_CNS_INT);
-
- if (op2->gtOper == GT_LCL_VAR)
- genMarkLclVar(op2);
-
- assert(((addrReg1 | addrReg2) & regSet.rsMaskUsed) == (addrReg1 | addrReg2));
- assert(((addrReg1 & addrReg2) & regSet.rsMaskMult) == (addrReg1 & addrReg2));
-
- /* Are we comparing against a register? */
-
- if (op2->InReg())
- {
- /* Make the comparands addressable and mark as used */
-
- assert(addrReg1 == RBM_NONE);
- addrReg1 = genMakeAddressable2(op1, RBM_NONE, RegSet::KEEP_REG, false, true);
-
- /* Is the size of the comparison byte/char/short ? */
-
- if (varTypeIsSmall(op1->TypeGet()))
- {
- /* Is op2 sitting in an appropriate register? */
-
- if (varTypeIsByte(op1->TypeGet()) && !isByteReg(op2->gtRegNum))
- goto NO_SMALL_CMP;
-
- /* Is op2 of the right type for a small comparison */
-
- if (op2->gtOper == GT_REG_VAR)
- {
- if (op1->gtType != compiler->lvaGetRealType(op2->gtRegVar.gtLclNum))
- goto NO_SMALL_CMP;
- }
- else
- {
- if (op1->gtType != op2->gtType)
- goto NO_SMALL_CMP;
- }
-
- if (varTypeIsUnsigned(op1->TypeGet()))
- unsignedCmp = true;
- }
-
- assert(addrReg2 == RBM_NONE);
-
- genComputeReg(op2, RBM_NONE, RegSet::ANY_REG, RegSet::KEEP_REG);
- addrReg2 = genRegMask(op2->gtRegNum);
- addrReg1 = genKeepAddressable(op1, addrReg1, addrReg2);
- assert(((addrReg1 | addrReg2) & regSet.rsMaskUsed) == (addrReg1 | addrReg2));
- assert(((addrReg1 & addrReg2) & regSet.rsMaskMult) == (addrReg1 & addrReg2));
-
- /* Compare against the register */
-
- inst_TT_RV(INS_cmp, op1, op2->gtRegNum);
-
- goto DONE;
-
- NO_SMALL_CMP:
-
- // op1 has been made addressable and is marked as in use
- // op2 is un-generated
- assert(addrReg2 == 0);
-
- if ((op1->InReg()) == 0)
- {
- regNumber reg1 = regSet.rsPickReg();
-
- noway_assert(varTypeIsSmall(op1->TypeGet()));
- instruction ins = ins_Move_Extend(op1->TypeGet(), (op1->InReg()) != 0);
-
- // regSet.rsPickReg can cause one of the trees within this address mode to get spilled
- // so we need to make sure it is still valid. Note that at this point, reg1 is
- // *not* marked as in use, and it is possible for it to be used in the address
- // mode expression, but that is OK, because we are done with expression after
- // this. We only need reg1.
- addrReg1 = genKeepAddressable(op1, addrReg1);
- inst_RV_TT(ins, reg1, op1);
- regTracker.rsTrackRegTrash(reg1);
-
- genDoneAddressable(op1, addrReg1, RegSet::KEEP_REG);
- addrReg1 = 0;
-
- genMarkTreeInReg(op1, reg1);
-
- regSet.rsMarkRegUsed(op1);
- addrReg1 = genRegMask(op1->gtRegNum);
- }
-
- assert(((addrReg1 | addrReg2) & regSet.rsMaskUsed) == (addrReg1 | addrReg2));
- assert(((addrReg1 & addrReg2) & regSet.rsMaskMult) == (addrReg1 & addrReg2));
-
- goto DONE_OP1;
- }
-
- // We come here if op2 is not enregistered or not in a "good" register.
-
- assert(addrReg1 == 0);
-
- // Determine what registers go live between op1 and op2
- newLiveMask = genNewLiveRegMask(op1, op2);
-
- // Setup regNeed with the set of register that we suggest for op1 to be in
- //
- regNeed = RBM_ALLINT;
-
- // avoid selecting registers that get newly born in op2
- regNeed = regSet.rsNarrowHint(regNeed, ~newLiveMask);
-
- // avoid selecting op2 reserved regs
- regNeed = regSet.rsNarrowHint(regNeed, ~op2->gtRsvdRegs);
-
-#if CPU_HAS_BYTE_REGS
- // if necessary setup regNeed to select just the byte-able registers
- if (byteCmp)
- regNeed = regSet.rsNarrowHint(RBM_BYTE_REGS, regNeed);
-#endif // CPU_HAS_BYTE_REGS
-
- // Compute the first comparand into some register, regNeed here is simply a hint because RegSet::ANY_REG is used.
- //
- genComputeReg(op1, regNeed, RegSet::ANY_REG, RegSet::FREE_REG);
- noway_assert(op1->InReg());
-
- op1Reg = op1->gtRegNum;
-
- // Setup regNeed with the set of register that we require for op1 to be in
- //
- regNeed = RBM_ALLINT;
-
-#if CPU_HAS_BYTE_REGS
- // if necessary setup regNeed to select just the byte-able registers
- if (byteCmp)
- regNeed &= RBM_BYTE_REGS;
-#endif // CPU_HAS_BYTE_REGS
-
- // avoid selecting registers that get newly born in op2, as using them will force a spill temp to be used.
- regNeed = regSet.rsMustExclude(regNeed, newLiveMask);
-
- // avoid selecting op2 reserved regs, as using them will force a spill temp to be used.
- regNeed = regSet.rsMustExclude(regNeed, op2->gtRsvdRegs);
-
- // Did we end up in an acceptable register?
- // and do we have an acceptable free register available to grab?
- //
- if (((genRegMask(op1Reg) & regNeed) == 0) && ((regSet.rsRegMaskFree() & regNeed) != 0))
- {
- // Grab an acceptable register
- regNumber newReg = regSet.rsGrabReg(regNeed);
-
- noway_assert(op1Reg != newReg);
-
- /* Update the value in the target register */
-
- regTracker.rsTrackRegCopy(newReg, op1Reg);
-
- inst_RV_RV(ins_Copy(op1->TypeGet()), newReg, op1Reg, op1->TypeGet());
-
- /* The value has been transferred to 'reg' */
-
- if ((genRegMask(op1Reg) & regSet.rsMaskUsed) == 0)
- gcInfo.gcMarkRegSetNpt(genRegMask(op1Reg));
-
- gcInfo.gcMarkRegPtrVal(newReg, op1->TypeGet());
-
- /* The value is now in an appropriate register */
-
- op1->gtRegNum = newReg;
- }
- noway_assert(op1->InReg());
- op1Reg = op1->gtRegNum;
-
- genUpdateLife(op1);
-
- /* Mark the register as 'used' */
- regSet.rsMarkRegUsed(op1);
-
- addrReg1 = genRegMask(op1Reg);
-
- assert(((addrReg1 | addrReg2) & regSet.rsMaskUsed) == (addrReg1 | addrReg2));
- assert(((addrReg1 & addrReg2) & regSet.rsMaskMult) == (addrReg1 & addrReg2));
-
-DONE_OP1:
-
- assert(((addrReg1 | addrReg2) & regSet.rsMaskUsed) == (addrReg1 | addrReg2));
- assert(((addrReg1 & addrReg2) & regSet.rsMaskMult) == (addrReg1 & addrReg2));
- noway_assert(op1->InReg());
-
- // Setup regNeed with either RBM_ALLINT or the RBM_BYTE_REGS subset
- // when byteCmp is true we will perform a byte sized cmp instruction
- // and that instruction requires that any registers used are byte-able ones.
- //
- regNeed = RBM_ALLINT;
-
-#if CPU_HAS_BYTE_REGS
- // if necessary setup regNeed to select just the byte-able registers
- if (byteCmp)
- regNeed &= RBM_BYTE_REGS;
-#endif // CPU_HAS_BYTE_REGS
-
- /* Make the comparand addressable */
- assert(addrReg2 == 0);
- addrReg2 = genMakeRvalueAddressable(op2, regNeed, RegSet::KEEP_REG, false, (byteCmp | shortCmp));
-
- /* Make sure the first operand is still in a register; if
- it's been spilled, we have to make sure it's reloaded
- into a byte-addressable register if needed.
- Pass keepReg=RegSet::KEEP_REG. Otherwise get pointer lifetimes wrong.
- */
-
- assert(addrReg1 != 0);
- genRecoverReg(op1, regNeed, RegSet::KEEP_REG);
-
- noway_assert(op1->InReg());
- noway_assert(!byteCmp || isByteReg(op1->gtRegNum));
-
- addrReg1 = genRegMask(op1->gtRegNum);
- regSet.rsLockUsedReg(addrReg1);
-
- /* Make sure that op2 is addressable. If we are going to do a
- byte-comparison, we need it to be in a byte register. */
-
- if (byteCmp && (op2->InReg()))
- {
- genRecoverReg(op2, regNeed, RegSet::KEEP_REG);
- addrReg2 = genRegMask(op2->gtRegNum);
- }
- else
- {
- addrReg2 = genKeepAddressable(op2, addrReg2);
- }
-
- regSet.rsUnlockUsedReg(addrReg1);
-
- assert(((addrReg1 | addrReg2) & regSet.rsMaskUsed) == (addrReg1 | addrReg2));
- assert(((addrReg1 & addrReg2) & regSet.rsMaskMult) == (addrReg1 & addrReg2));
-
- if (byteCmp || shortCmp)
- {
- size = emitTypeSize(op2->TypeGet());
- if (varTypeIsUnsigned(op1Type))
- unsignedCmp = true;
- }
- else
- {
- size = emitActualTypeSize(op2->TypeGet());
- }
-
- /* Perform the comparison */
- inst_RV_TT(INS_cmp, op1->gtRegNum, op2, 0, size);
-
-DONE:
-
- jumpKind = genJumpKindForOper(cmp, unsignedCmp ? CK_UNSIGNED : CK_SIGNED);
-
-DONE_FLAGS: // We have determined what jumpKind to use
-
- genUpdateLife(cond);
-
- /* The condition value is dead at the jump that follows */
-
- assert(((addrReg1 | addrReg2) & regSet.rsMaskUsed) == (addrReg1 | addrReg2));
- assert(((addrReg1 & addrReg2) & regSet.rsMaskMult) == (addrReg1 & addrReg2));
- genDoneAddressable(op1, addrReg1, RegSet::KEEP_REG);
- genDoneAddressable(op2, addrReg2, RegSet::KEEP_REG);
-
- noway_assert(jumpKind != EJ_COUNT); // Ensure that it was assigned a valid value
-
- return jumpKind;
-}
-
-/*****************************************************************************/
-/*****************************************************************************/
-/*****************************************************************************
- *
- * Generate code to jump to the jump target of the current basic block if
- * the given relational operator yields 'true'.
- */
-
-void CodeGen::genCondJump(GenTree* cond, BasicBlock* destTrue, BasicBlock* destFalse, bool bStackFPFixup)
-{
- BasicBlock* jumpTrue;
- BasicBlock* jumpFalse;
-
- GenTree* op1 = cond->gtOp.gtOp1;
- GenTree* op2 = cond->gtOp.gtOp2;
- genTreeOps cmp = cond->OperGet();
-
- if (destTrue)
- {
- jumpTrue = destTrue;
- jumpFalse = destFalse;
- }
- else
- {
- noway_assert(compiler->compCurBB->bbJumpKind == BBJ_COND);
-
- jumpTrue = compiler->compCurBB->bbJumpDest;
- jumpFalse = compiler->compCurBB->bbNext;
- }
-
- noway_assert(cond->OperIsCompare());
-
- /* Make sure the more expensive operand is 'op1' */
- noway_assert((cond->gtFlags & GTF_REVERSE_OPS) == 0);
-
- if (cond->gtFlags & GTF_REVERSE_OPS) // TODO: note that this is now dead code, since the above is a noway_assert()
- {
- /* Don't forget to modify the condition as well */
-
- cond->gtOp.gtOp1 = op2;
- cond->gtOp.gtOp2 = op1;
- cond->SetOper(GenTree::SwapRelop(cmp));
- cond->gtFlags &= ~GTF_REVERSE_OPS;
-
- /* Get hold of the new values */
-
- cmp = cond->OperGet();
- op1 = cond->gtOp.gtOp1;
- op2 = cond->gtOp.gtOp2;
- }
-
- /* What is the type of the operand? */
-
- switch (genActualType(op1->gtType))
- {
- case TYP_INT:
- case TYP_REF:
- case TYP_BYREF:
- emitJumpKind jumpKind;
-
- // Check if we can use the currently set flags. Else set them
-
- jumpKind = genCondSetFlags(cond);
-
-#if FEATURE_STACK_FP_X87
- if (bStackFPFixup)
- {
- genCondJmpInsStackFP(jumpKind, jumpTrue, jumpFalse);
- }
- else
-#endif
- {
- /* Generate the conditional jump */
- inst_JMP(jumpKind, jumpTrue);
- }
-
- return;
-
- case TYP_LONG:
-#if FEATURE_STACK_FP_X87
- if (bStackFPFixup)
- {
- genCondJumpLngStackFP(cond, jumpTrue, jumpFalse);
- }
- else
-#endif
- {
- genCondJumpLng(cond, jumpTrue, jumpFalse);
- }
- return;
-
- case TYP_FLOAT:
- case TYP_DOUBLE:
-#if FEATURE_STACK_FP_X87
- genCondJumpFltStackFP(cond, jumpTrue, jumpFalse, bStackFPFixup);
-#else
- genCondJumpFloat(cond, jumpTrue, jumpFalse);
-#endif
- return;
-
- default:
-#ifdef DEBUG
- compiler->gtDispTree(cond);
-#endif
- unreached(); // unexpected/unsupported 'jtrue' operands type
- }
-}
-
-/*****************************************************************************
- * Spill registers to check callers can handle it.
- */
-
-#ifdef DEBUG
-
-void CodeGen::genStressRegs(GenTree* tree)
-{
- if (regSet.rsStressRegs() < 2)
- return;
-
- /* Spill as many registers as possible. Callers should be prepared
- to handle this case.
- But don't spill trees with no size (TYP_STRUCT comes to mind) */
-
- {
- regMaskTP spillRegs = regSet.rsRegMaskCanGrab() & regSet.rsMaskUsed;
- regNumber regNum;
- regMaskTP regBit;
-
- for (regNum = REG_FIRST, regBit = 1; regNum < REG_COUNT; regNum = REG_NEXT(regNum), regBit <<= 1)
- {
- if ((spillRegs & regBit) && (regSet.rsUsedTree[regNum] != NULL) &&
- (genTypeSize(regSet.rsUsedTree[regNum]->TypeGet()) > 0))
- {
- regSet.rsSpillReg(regNum);
-
- spillRegs &= regSet.rsMaskUsed;
-
- if (!spillRegs)
- break;
- }
- }
- }
-
- regMaskTP trashRegs = regSet.rsRegMaskFree();
-
- if (trashRegs == RBM_NONE)
- return;
-
- /* It is sometimes reasonable to expect that calling genCodeForTree()
- on certain trees won't spill anything */
-
- if ((compiler->compCurStmt == compiler->compCurBB->bbTreeList) && (compiler->compCurBB->bbCatchTyp) &&
- handlerGetsXcptnObj(compiler->compCurBB->bbCatchTyp))
- {
- trashRegs &= ~(RBM_EXCEPTION_OBJECT);
- }
-
- // If genCodeForTree() effectively gets called a second time on the same tree
-
- if (tree->InReg())
- {
- noway_assert(varTypeIsIntegralOrI(tree->TypeGet()));
- trashRegs &= ~genRegMask(tree->gtRegNum);
- }
-
- if (tree->gtType == TYP_INT && tree->OperIsSimple())
- {
- GenTree* op1 = tree->gtOp.gtOp1;
- GenTree* op2 = tree->gtOp.gtOp2;
- if (op1 && (op1->InReg()))
- trashRegs &= ~genRegMask(op1->gtRegNum);
- if (op2 && (op2->InReg()))
- trashRegs &= ~genRegMask(op2->gtRegNum);
- }
-
- if (compiler->compCurBB == compiler->genReturnBB)
- {
- if (compiler->info.compCallUnmanaged)
- {
- LclVarDsc* varDsc = &compiler->lvaTable[compiler->info.compLvFrameListRoot];
- if (varDsc->lvRegister)
- trashRegs &= ~genRegMask(varDsc->lvRegNum);
- }
- }
-
- /* Now trash the registers. We use regSet.rsModifiedRegsMask, else we will have
- to save/restore the register. We try to be as unintrusive
- as possible */
-
- noway_assert((REG_INT_LAST - REG_INT_FIRST) == 7);
- // This is obviously false for ARM, but this function is never called.
- for (regNumber reg = REG_INT_FIRST; reg <= REG_INT_LAST; reg = REG_NEXT(reg))
- {
- regMaskTP regMask = genRegMask(reg);
-
- if (regSet.rsRegsModified(regMask & trashRegs))
- genSetRegToIcon(reg, 0);
- }
-}
-
-#endif // DEBUG
-
-/*****************************************************************************
- *
- * Generate code for a GTK_CONST tree
- */
-
-void CodeGen::genCodeForTreeConst(GenTree* tree, regMaskTP destReg, regMaskTP bestReg)
-{
- noway_assert(tree->IsCnsIntOrI());
- GenTreeIntConCommon* con = tree->AsIntConCommon();
- ssize_t ival = con->IconValue();
- bool needReloc = con->ImmedValNeedsReloc(compiler);
- regMaskTP needReg = destReg;
- regNumber reg;
-
-#if REDUNDANT_LOAD
-
- /* If we are targeting destReg and ival is zero */
- /* we would rather xor needReg than copy another register */
-
- if (!needReloc)
- {
- bool reuseConstantInReg = false;
-
- if (destReg == RBM_NONE)
- reuseConstantInReg = true;
-
-#ifdef _TARGET_ARM_
- // If we can set a register to a constant with a small encoding, then do that.
- // Assume we'll get a low register if needReg has low registers as options.
- if (!reuseConstantInReg &&
- !arm_Valid_Imm_For_Small_Mov((needReg & RBM_LOW_REGS) ? REG_R0 : REG_R8, ival, INS_FLAGS_DONT_CARE))
- {
- reuseConstantInReg = true;
- }
-#else
- if (!reuseConstantInReg && ival != 0)
- reuseConstantInReg = true;
-#endif
-
- if (reuseConstantInReg)
- {
- /* Is the constant already in register? If so, use this register */
-
- reg = regTracker.rsIconIsInReg(ival);
- if (reg != REG_NA)
- goto REG_LOADED;
- }
- }
-
-#endif // REDUNDANT_LOAD
-
- reg = regSet.rsPickReg(needReg, bestReg);
-
- /* If the constant is a handle, we need a reloc to be applied to it */
-
- if (needReloc)
- {
- instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, reg, ival);
- regTracker.rsTrackRegTrash(reg);
- }
- else
- {
- genSetRegToIcon(reg, ival, tree->TypeGet());
- }
-
-REG_LOADED:
-
-#ifdef DEBUG
- /* Special case: GT_CNS_INT - Restore the current live set if it was changed */
-
- if (!genTempLiveChg)
- {
- VarSetOps::Assign(compiler, compiler->compCurLife, genTempOldLife);
- genTempLiveChg = true;
- }
-#endif
-
- gcInfo.gcMarkRegPtrVal(reg, tree->TypeGet()); // In case the handle is a GC object (for eg, frozen strings)
- genCodeForTree_DONE(tree, reg);
-}
-
-/*****************************************************************************
- *
- * Generate code for a GTK_LEAF tree
- */
-
-void CodeGen::genCodeForTreeLeaf(GenTree* tree, regMaskTP destReg, regMaskTP bestReg)
-{
- genTreeOps oper = tree->OperGet();
- regNumber reg = DUMMY_INIT(REG_CORRUPT);
- regMaskTP regs = regSet.rsMaskUsed;
- regMaskTP needReg = destReg;
- size_t size;
-
- noway_assert(tree->OperKind() & GTK_LEAF);
-
- switch (oper)
- {
- case GT_REG_VAR:
- NO_WAY("GT_REG_VAR should have been caught above");
- break;
-
- case GT_LCL_VAR:
-
- /* Does the variable live in a register? */
-
- if (genMarkLclVar(tree))
- {
- genCodeForTree_REG_VAR1(tree);
- return;
- }
-
-#if REDUNDANT_LOAD
-
- /* Is the local variable already in register? */
-
- reg = findStkLclInReg(tree->gtLclVarCommon.gtLclNum);
-
- if (reg != REG_NA)
- {
- /* Use the register the variable happens to be in */
- regMaskTP regMask = genRegMask(reg);
-
- // If the register that it was in isn't one of the needRegs
- // then try to move it into a needReg register
-
- if (((regMask & needReg) == 0) && (regSet.rsRegMaskCanGrab() & needReg))
- {
- regNumber rg2 = reg;
- reg = regSet.rsPickReg(needReg, bestReg);
- if (reg != rg2)
- {
- regMask = genRegMask(reg);
- inst_RV_RV(INS_mov, reg, rg2, tree->TypeGet());
- }
- }
-
- gcInfo.gcMarkRegPtrVal(reg, tree->TypeGet());
- regTracker.rsTrackRegLclVar(reg, tree->gtLclVarCommon.gtLclNum);
- break;
- }
-
-#endif
- goto MEM_LEAF;
-
- case GT_LCL_FLD:
-
- // We only use GT_LCL_FLD for lvDoNotEnregister vars, so we don't have
- // to worry about it being enregistered.
- noway_assert(compiler->lvaTable[tree->gtLclFld.gtLclNum].lvRegister == 0);
- goto MEM_LEAF;
-
- case GT_CLS_VAR:
-
- MEM_LEAF:
-
- /* Pick a register for the value */
-
- reg = regSet.rsPickReg(needReg, bestReg);
-
- /* Load the variable into the register */
-
- size = genTypeSize(tree->gtType);
-
- if (size < EA_4BYTE)
- {
- instruction ins = ins_Move_Extend(tree->TypeGet(), tree->InReg());
- inst_RV_TT(ins, reg, tree, 0);
-
- /* We've now "promoted" the tree-node to TYP_INT */
-
- tree->gtType = TYP_INT;
- }
- else
- {
- inst_RV_TT(INS_mov, reg, tree, 0);
- }
-
- regTracker.rsTrackRegTrash(reg);
-
- gcInfo.gcMarkRegPtrVal(reg, tree->TypeGet());
-
- switch (oper)
- {
- case GT_CLS_VAR:
- regTracker.rsTrackRegClsVar(reg, tree);
- break;
- case GT_LCL_VAR:
- regTracker.rsTrackRegLclVar(reg, tree->gtLclVarCommon.gtLclNum);
- break;
- case GT_LCL_FLD:
- break;
- default:
- noway_assert(!"Unexpected oper");
- }
-
-#ifdef _TARGET_ARM_
- if (tree->gtFlags & GTF_IND_VOLATILE)
- {
- // Emit a memory barrier instruction after the load
- instGen_MemoryBarrier();
- }
-#endif
-
- break;
-
- case GT_NO_OP:
- instGen(INS_nop);
- reg = REG_STK;
- break;
-
-#if !FEATURE_EH_FUNCLETS
- case GT_END_LFIN:
-
- /* Have to clear the shadowSP of the nesting level which
- encloses the finally */
-
- unsigned finallyNesting;
- finallyNesting = (unsigned)tree->gtVal.gtVal1;
- noway_assert(tree->gtVal.gtVal1 <
- compiler->compHndBBtabCount); // assert we didn't truncate with the cast above.
- noway_assert(finallyNesting < compiler->compHndBBtabCount);
-
- // The last slot is reserved for ICodeManager::FixContext(ppEndRegion)
- unsigned filterEndOffsetSlotOffs;
- PREFIX_ASSUME(compiler->lvaLclSize(compiler->lvaShadowSPslotsVar) >
- TARGET_POINTER_SIZE); // below doesn't underflow.
- filterEndOffsetSlotOffs =
- (unsigned)(compiler->lvaLclSize(compiler->lvaShadowSPslotsVar) - TARGET_POINTER_SIZE);
-
- unsigned curNestingSlotOffs;
- curNestingSlotOffs = filterEndOffsetSlotOffs - ((finallyNesting + 1) * TARGET_POINTER_SIZE);
- instGen_Store_Imm_Into_Lcl(TYP_I_IMPL, EA_PTRSIZE, 0, compiler->lvaShadowSPslotsVar, curNestingSlotOffs);
- reg = REG_STK;
- break;
-#endif // !FEATURE_EH_FUNCLETS
-
- case GT_CATCH_ARG:
-
- noway_assert(compiler->compCurBB->bbCatchTyp && handlerGetsXcptnObj(compiler->compCurBB->bbCatchTyp));
-
- /* Catch arguments get passed in a register. genCodeForBBlist()
- would have marked it as holding a GC object, but not used. */
-
- noway_assert(gcInfo.gcRegGCrefSetCur & RBM_EXCEPTION_OBJECT);
- reg = REG_EXCEPTION_OBJECT;
- break;
-
- case GT_JMP:
- genCodeForTreeLeaf_GT_JMP(tree);
- return;
-
- case GT_MEMORYBARRIER:
- // Emit the memory barrier instruction
- instGen_MemoryBarrier();
- reg = REG_STK;
- break;
-
- default:
-#ifdef DEBUG
- compiler->gtDispTree(tree);
-#endif
- noway_assert(!"unexpected leaf");
- }
-
- noway_assert(reg != DUMMY_INIT(REG_CORRUPT));
- genCodeForTree_DONE(tree, reg);
-}
-
-GenTree* CodeGen::genCodeForCommaTree(GenTree* tree)
-{
- while (tree->OperGet() == GT_COMMA)
- {
- GenTree* op1 = tree->gtOp.gtOp1;
- genEvalSideEffects(op1);
- gcInfo.gcMarkRegPtrVal(op1);
-
- tree = tree->gtOp.gtOp2;
- }
- return tree;
-}
-
-/*****************************************************************************
- *
- * Generate code for the a leaf node of type GT_JMP
- */
-
-void CodeGen::genCodeForTreeLeaf_GT_JMP(GenTree* tree)
-{
- noway_assert(compiler->compCurBB->bbFlags & BBF_HAS_JMP);
-
-#ifdef PROFILING_SUPPORTED
- if (compiler->compIsProfilerHookNeeded())
- {
- /* fire the event at the call site */
- unsigned saveStackLvl2 = genStackLevel;
-
- compiler->info.compProfilerCallback = true;
-
-#ifdef _TARGET_X86_
- //
- // Push the profilerHandle
- //
- regMaskTP byrefPushedRegs;
- regMaskTP norefPushedRegs;
- regMaskTP pushedArgRegs =
- genPushRegs(RBM_ARG_REGS & (regSet.rsMaskUsed | regSet.rsMaskVars | regSet.rsMaskLock), &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_
- // For GT_JMP nodes we have added r0 as a used register, when under arm profiler, to evaluate GT_JMP node.
- // To emit tailcall callback we need r0 to pass profiler handle. Any free register could be used as call target.
- regNumber argReg = regSet.rsGrabReg(RBM_PROFILER_JMP_USED);
- noway_assert(argReg == REG_PROFILER_JMP_ARG);
- regSet.rsLockReg(RBM_PROFILER_JMP_USED);
-
- if (compiler->compProfilerMethHndIndirected)
- {
- getEmitter()->emitIns_R_AI(INS_ldr, EA_PTR_DSP_RELOC, argReg, (ssize_t)compiler->compProfilerMethHnd);
- regTracker.rsTrackRegTrash(argReg);
- }
- else
- {
- instGen_Set_Reg_To_Imm(EA_4BYTE, argReg, (ssize_t)compiler->compProfilerMethHnd);
- }
-
- genEmitHelperCall(CORINFO_HELP_PROF_FCN_TAILCALL,
- 0, // argSize
- EA_UNKNOWN); // retSize
-
- regSet.rsUnlockReg(RBM_PROFILER_JMP_USED);
-#else
- NYI("Pushing the profilerHandle & caller's sp for the profiler callout and locking 'arguments'");
-#endif //_TARGET_X86_
-
- /* Restore the stack level */
- SetStackLevel(saveStackLvl2);
- }
-#endif // PROFILING_SUPPORTED
-
- /* This code is cloned from the regular processing of GT_RETURN values. We have to remember to
- * call genPInvokeMethodEpilog anywhere that we have a method return. We should really
- * generate trees for the PInvoke prolog and epilog so we can remove these special cases.
- */
-
- if (compiler->info.compCallUnmanaged)
- {
- genPInvokeMethodEpilog();
- }
-
- // Make sure register arguments are in their initial registers
- // and stack arguments are put back as well.
- //
- // This does not deal with circular dependencies of register
- // arguments, which is safe because RegAlloc prevents that by
- // not enregistering any RegArgs when a JMP opcode is used.
-
- if (compiler->info.compArgsCount == 0)
- {
- return;
- }
-
- unsigned varNum;
- LclVarDsc* varDsc;
-
- // First move any enregistered stack arguments back to the stack
- for (varNum = 0, varDsc = compiler->lvaTable; varNum < compiler->info.compArgsCount; varNum++, varDsc++)
- {
- noway_assert(varDsc->lvIsParam);
- if (varDsc->lvIsRegArg || !varDsc->lvRegister)
- continue;
-
- /* Argument was passed on the stack, but ended up in a register
- * Store it back to the stack */
- CLANG_FORMAT_COMMENT_ANCHOR;
-
-#ifndef _TARGET_64BIT_
- if (varDsc->TypeGet() == TYP_LONG)
- {
- /* long - at least the low half must be enregistered */
-
- getEmitter()->emitIns_S_R(ins_Store(TYP_INT), EA_4BYTE, varDsc->lvRegNum, varNum, 0);
-
- /* Is the upper half also enregistered? */
-
- if (varDsc->lvOtherReg != REG_STK)
- {
- getEmitter()->emitIns_S_R(ins_Store(TYP_INT), EA_4BYTE, varDsc->lvOtherReg, varNum, sizeof(int));
- }
- }
- else
-#endif // _TARGET_64BIT_
- {
- getEmitter()->emitIns_S_R(ins_Store(varDsc->TypeGet()), emitTypeSize(varDsc->TypeGet()), varDsc->lvRegNum,
- varNum, 0);
- }
- }
-
-#ifdef _TARGET_ARM_
- regMaskTP fixedArgsMask = RBM_NONE;
-#endif
-
- // Next move any un-enregistered register arguments back to their register
- for (varNum = 0, varDsc = compiler->lvaTable; varNum < compiler->info.compArgsCount; varNum++, varDsc++)
- {
- /* Is this variable a register arg? */
-
- if (!varDsc->lvIsRegArg)
- continue;
-
- /* Register argument */
-
- noway_assert(isRegParamType(genActualType(varDsc->TypeGet())));
- noway_assert(!varDsc->lvRegister);
-
- /* Reload it from the stack */
- CLANG_FORMAT_COMMENT_ANCHOR;
-
-#ifndef _TARGET_64BIT_
- if (varDsc->TypeGet() == TYP_LONG)
- {
- /* long - at least the low half must be enregistered */
-
- getEmitter()->emitIns_R_S(ins_Load(TYP_INT), EA_4BYTE, varDsc->lvArgReg, varNum, 0);
- regTracker.rsTrackRegTrash(varDsc->lvArgReg);
-
- /* Also assume the upper half also enregistered */
-
- getEmitter()->emitIns_R_S(ins_Load(TYP_INT), EA_4BYTE, genRegArgNext(varDsc->lvArgReg), varNum,
- sizeof(int));
- regTracker.rsTrackRegTrash(genRegArgNext(varDsc->lvArgReg));
-
-#ifdef _TARGET_ARM_
- fixedArgsMask |= genRegMask(varDsc->lvArgReg);
- fixedArgsMask |= genRegMask(genRegArgNext(varDsc->lvArgReg));
-#endif
- }
- else
-#endif // _TARGET_64BIT_
-#ifdef _TARGET_ARM_
- if (varDsc->lvIsHfaRegArg())
- {
- const var_types elemType = varDsc->GetHfaType();
- const instruction loadOp = ins_Load(elemType);
- const emitAttr size = emitTypeSize(elemType);
- regNumber argReg = varDsc->lvArgReg;
- const unsigned maxSize = min(varDsc->lvSize(), (LAST_FP_ARGREG + 1 - argReg) * REGSIZE_BYTES);
-
- for (unsigned ofs = 0; ofs < maxSize; ofs += (unsigned)size)
- {
- getEmitter()->emitIns_R_S(loadOp, size, argReg, varNum, ofs);
- assert(genIsValidFloatReg(argReg)); // we don't use register tracking for FP
- argReg = regNextOfType(argReg, elemType);
- }
- }
- else if (varDsc->TypeGet() == TYP_STRUCT)
- {
- const var_types elemType = TYP_INT; // we pad everything out to at least 4 bytes
- const instruction loadOp = ins_Load(elemType);
- const emitAttr size = emitTypeSize(elemType);
- regNumber argReg = varDsc->lvArgReg;
- const unsigned maxSize = min(varDsc->lvSize(), (REG_ARG_LAST + 1 - argReg) * REGSIZE_BYTES);
-
- for (unsigned ofs = 0; ofs < maxSize; ofs += (unsigned)size)
- {
- getEmitter()->emitIns_R_S(loadOp, size, argReg, varNum, ofs);
- regTracker.rsTrackRegTrash(argReg);
-
- fixedArgsMask |= genRegMask(argReg);
-
- argReg = genRegArgNext(argReg);
- }
- }
- else
-#endif //_TARGET_ARM_
- {
- var_types loadType = varDsc->TypeGet();
- regNumber argReg = varDsc->lvArgReg; // incoming arg register
- bool twoParts = false;
-
- if (compiler->info.compIsVarArgs && isFloatRegType(loadType))
- {
-#ifndef _TARGET_64BIT_
- if (loadType == TYP_DOUBLE)
- twoParts = true;
-#endif
- loadType = TYP_I_IMPL;
- assert(isValidIntArgReg(argReg));
- }
-
- getEmitter()->emitIns_R_S(ins_Load(loadType), emitTypeSize(loadType), argReg, varNum, 0);
- regTracker.rsTrackRegTrash(argReg);
-
-#ifdef _TARGET_ARM_
- fixedArgsMask |= genRegMask(argReg);
-#endif
- if (twoParts)
- {
- argReg = genRegArgNext(argReg);
- assert(isValidIntArgReg(argReg));
-
- getEmitter()->emitIns_R_S(ins_Load(loadType), emitTypeSize(loadType), argReg, varNum, REGSIZE_BYTES);
- regTracker.rsTrackRegTrash(argReg);
-
-#ifdef _TARGET_ARM_
- fixedArgsMask |= genRegMask(argReg);
-#endif
- }
- }
- }
-
-#ifdef _TARGET_ARM_
- // Check if we have any non-fixed args possibly in the arg registers.
- if (compiler->info.compIsVarArgs && (fixedArgsMask & RBM_ARG_REGS) != RBM_ARG_REGS)
- {
- noway_assert(compiler->lvaTable[compiler->lvaVarargsHandleArg].lvOnFrame);
-
- regNumber regDeclArgs = REG_ARG_FIRST;
-
- // Skip the 'this' pointer.
- if (!compiler->info.compIsStatic)
- {
- regDeclArgs = REG_NEXT(regDeclArgs);
- }
-
- // Skip the 'generic context.'
- if (compiler->info.compMethodInfo->args.callConv & CORINFO_CALLCONV_PARAMTYPE)
- {
- regDeclArgs = REG_NEXT(regDeclArgs);
- }
-
- // Skip any 'return buffer arg.'
- if (compiler->info.compRetBuffArg != BAD_VAR_NUM)
- {
- regDeclArgs = REG_NEXT(regDeclArgs);
- }
-
- // Skip the 'vararg cookie.'
- regDeclArgs = REG_NEXT(regDeclArgs);
-
- // Also add offset for the vararg cookie.
- int offset = REGSIZE_BYTES;
-
- // Load all the variable arguments in registers back to their registers.
- for (regNumber reg = regDeclArgs; reg <= REG_ARG_LAST; reg = REG_NEXT(reg))
- {
- if (!(fixedArgsMask & genRegMask(reg)))
- {
- getEmitter()->emitIns_R_S(ins_Load(TYP_INT), EA_4BYTE, reg, compiler->lvaVarargsHandleArg, offset);
- regTracker.rsTrackRegTrash(reg);
- }
- offset += REGSIZE_BYTES;
- }
- }
-#endif // _TARGET_ARM_
-}
-
-/*****************************************************************************
- *
- * Check if a variable is assigned to in a tree. The variable number is
- * passed in pCallBackData. If the variable is assigned to, return
- * Compiler::WALK_ABORT. Otherwise return Compiler::WALK_CONTINUE.
- */
-Compiler::fgWalkResult CodeGen::fgIsVarAssignedTo(GenTree** pTree, Compiler::fgWalkData* data)
-{
- GenTree* tree = *pTree;
- if ((tree->OperIsAssignment()) && (tree->gtOp.gtOp1->OperGet() == GT_LCL_VAR) &&
- (tree->gtOp.gtOp1->gtLclVarCommon.gtLclNum == (unsigned)(size_t)data->pCallbackData))
- {
- return Compiler::WALK_ABORT;
- }
-
- return Compiler::WALK_CONTINUE;
-}
-
-regNumber CodeGen::genIsEnregisteredIntVariable(GenTree* tree)
-{
- unsigned varNum;
- LclVarDsc* varDsc;
-
- if (tree->gtOper == GT_LCL_VAR)
- {
- /* Does the variable live in a register? */
-
- varNum = tree->gtLclVarCommon.gtLclNum;
- noway_assert(varNum < compiler->lvaCount);
- varDsc = compiler->lvaTable + varNum;
-
- if (!varDsc->IsFloatRegType() && varDsc->lvRegister)
- {
- return varDsc->lvRegNum;
- }
- }
-
- return REG_NA;
-}
-
-// inline
-void CodeGen::unspillLiveness(genLivenessSet* ls)
-{
- // Only try to unspill the registers that are missing from the currentLiveRegs
- //
- regMaskTP cannotSpillMask = ls->maskVars | ls->gcRefRegs | ls->byRefRegs;
- regMaskTP currentLiveRegs = regSet.rsMaskVars | gcInfo.gcRegGCrefSetCur | gcInfo.gcRegByrefSetCur;
- cannotSpillMask &= ~currentLiveRegs;
-
- // Typically this will always be true and we will return
- //
- if (cannotSpillMask == 0)
- return;
-
- for (regNumber reg = REG_INT_FIRST; reg <= REG_INT_LAST; reg = REG_NEXT(reg))
- {
- // Is this a register that we cannot leave in the spilled state?
- //
- if ((cannotSpillMask & genRegMask(reg)) == 0)
- continue;
-
- RegSet::SpillDsc* spill = regSet.rsSpillDesc[reg];
-
- // Was it spilled, if not then skip it.
- //
- if (!spill)
- continue;
-
- noway_assert(spill->spillTree->gtFlags & GTF_SPILLED);
-
- regSet.rsUnspillReg(spill->spillTree, genRegMask(reg), RegSet::KEEP_REG);
- }
-}
-
-/*****************************************************************************
- *
- * Generate code for a qmark colon
- */
-
-void CodeGen::genCodeForQmark(GenTree* tree, regMaskTP destReg, regMaskTP bestReg)
-{
- GenTree* op1 = tree->gtOp.gtOp1;
- GenTree* op2 = tree->gtOp.gtOp2;
- regNumber reg;
- regMaskTP regs = regSet.rsMaskUsed;
- regMaskTP needReg = destReg;
-
- noway_assert(compiler->compQmarkUsed);
- noway_assert(tree->gtOper == GT_QMARK);
- noway_assert(op1->OperIsCompare());
- noway_assert(op2->gtOper == GT_COLON);
-
- GenTree* thenNode = op2->AsColon()->ThenNode();
- GenTree* elseNode = op2->AsColon()->ElseNode();
-
- /* If elseNode is a Nop node you must reverse the
- thenNode and elseNode prior to reaching here!
- (If both 'else' and 'then' are Nops, whole qmark will have been optimized away.) */
-
- noway_assert(!elseNode->IsNothingNode());
-
- /* Try to implement the qmark colon using a CMOV. If we can't for
- whatever reason, this will return false and we will implement
- it using regular branching constructs. */
-
- if (genCodeForQmarkWithCMOV(tree, destReg, bestReg))
- return;
-
- /*
- This is a ?: operator; generate code like this:
-
- condition_compare
- jmp_if_true lab_true
-
- lab_false:
- op1 (false = 'else' part)
- jmp lab_done
-
- lab_true:
- op2 (true = 'then' part)
-
- lab_done:
-
-
- NOTE: If no 'then' part we do not generate the 'jmp lab_done'
- or the 'lab_done' label
- */
-
- BasicBlock* lab_true;
- BasicBlock* lab_false;
- BasicBlock* lab_done;
-
- genLivenessSet entryLiveness;
- genLivenessSet exitLiveness;
-
- lab_true = genCreateTempLabel();
- lab_false = genCreateTempLabel();
-
-#if FEATURE_STACK_FP_X87
- /* Spill any register that hold partial values so that the exit liveness
- from sides is the same */
- CLANG_FORMAT_COMMENT_ANCHOR;
-
-#ifdef DEBUG
- regMaskTP spillMask = regSet.rsMaskUsedFloat | regSet.rsMaskLockedFloat | regSet.rsMaskRegVarFloat;
-
- // spillMask should be the whole FP stack
- noway_assert(compCurFPState.m_uStackSize == genCountBits(spillMask));
-#endif
-
- SpillTempsStackFP(regSet.rsMaskUsedFloat);
- noway_assert(regSet.rsMaskUsedFloat == 0);
-#endif
-
- /* Before we generate code for qmark, we spill all the currently used registers
- that conflict with the registers used in the qmark tree. This is to avoid
- introducing spills that only occur on either the 'then' or 'else' side of
- the tree, but not both identically. We need to be careful with enregistered
- variables that are used; see below.
- */
-
- if (regSet.rsMaskUsed)
- {
- /* If regSet.rsMaskUsed overlaps with regSet.rsMaskVars (multi-use of the enregistered
- variable), then it may not get spilled. However, the variable may
- then go dead within thenNode/elseNode, at which point regSet.rsMaskUsed
- may get spilled from one side and not the other. So unmark regSet.rsMaskVars
- before spilling regSet.rsMaskUsed */
-
- regMaskTP rsAdditionalCandidates = regSet.rsMaskUsed & regSet.rsMaskVars;
- regMaskTP rsAdditional = RBM_NONE;
-
- // For each multi-use of an enregistered variable, we need to determine if
- // it can get spilled inside the qmark colon. This can only happen if
- // its life ends somewhere in the qmark colon. We have the following
- // cases:
- // 1) Variable is dead at the end of the colon -- needs to be spilled
- // 2) Variable is alive at the end of the colon -- needs to be spilled
- // iff it is assigned to in the colon. In order to determine that, we
- // examine the GTF_ASG flag to see if any assignments were made in the
- // colon. If there are any, we need to do a tree walk to see if this
- // variable is the target of an assignment. This treewalk should not
- // happen frequently.
- if (rsAdditionalCandidates)
- {
-#ifdef DEBUG
- if (compiler->verbose)
- {
- Compiler::printTreeID(tree);
- printf(": Qmark-Colon additional spilling candidates are ");
- dspRegMask(rsAdditionalCandidates);
- printf("\n");
- }
-#endif
-
- // If any candidates are not alive at the GT_QMARK node, then they
- // need to be spilled
-
- const VARSET_TP& rsLiveNow(compiler->compCurLife);
- VARSET_TP rsLiveAfter(compiler->fgUpdateLiveSet(compiler->compCurLife, compiler->compCurLifeTree, tree));
-
- VARSET_TP regVarLiveNow(VarSetOps::Intersection(compiler, compiler->raRegVarsMask, rsLiveNow));
-
- VarSetOps::Iter iter(compiler, regVarLiveNow);
- unsigned varIndex = 0;
- while (iter.NextElem(&varIndex))
- {
- // Find the variable in compiler->lvaTable
- unsigned varNum = compiler->lvaTrackedToVarNum[varIndex];
- LclVarDsc* varDsc = compiler->lvaTable + varNum;
-
-#if !FEATURE_FP_REGALLOC
- if (varDsc->IsFloatRegType())
- continue;
-#endif
-
- noway_assert(varDsc->lvRegister);
-
- regMaskTP regBit;
-
- if (varTypeIsFloating(varDsc->TypeGet()))
- {
- regBit = genRegMaskFloat(varDsc->lvRegNum, varDsc->TypeGet());
- }
- else
- {
- regBit = genRegMask(varDsc->lvRegNum);
-
- // For longs we may need to spill both regs
- if (isRegPairType(varDsc->lvType) && varDsc->lvOtherReg != REG_STK)
- regBit |= genRegMask(varDsc->lvOtherReg);
- }
-
- // Is it one of our reg-use vars? If not, we don't need to spill it.
- regBit &= rsAdditionalCandidates;
- if (!regBit)
- continue;
-
- // Is the variable live at the end of the colon?
- if (VarSetOps::IsMember(compiler, rsLiveAfter, varIndex))
- {
- // Variable is alive at the end of the colon. Was it assigned
- // to inside the colon?
-
- if (!(op2->gtFlags & GTF_ASG))
- continue;
-
- if (compiler->fgWalkTreePre(&op2, CodeGen::fgIsVarAssignedTo, (void*)(size_t)varNum) ==
- Compiler::WALK_ABORT)
- {
- // Variable was assigned to, so we need to spill it.
-
- rsAdditional |= regBit;
-#ifdef DEBUG
- if (compiler->verbose)
- {
- Compiler::printTreeID(tree);
- printf(": Qmark-Colon candidate ");
- dspRegMask(regBit);
- printf("\n");
- printf(" is assigned to inside colon and will be spilled\n");
- }
-#endif
- }
- }
- else
- {
- // Variable is not alive at the end of the colon. We need to spill it.
-
- rsAdditional |= regBit;
-#ifdef DEBUG
- if (compiler->verbose)
- {
- Compiler::printTreeID(tree);
- printf(": Qmark-Colon candidate ");
- dspRegMask(regBit);
- printf("\n");
- printf(" is alive at end of colon and will be spilled\n");
- }
-#endif
- }
- }
-
-#ifdef DEBUG
- if (compiler->verbose)
- {
- Compiler::printTreeID(tree);
- printf(": Qmark-Colon approved additional spilling candidates are ");
- dspRegMask(rsAdditional);
- printf("\n");
- }
-#endif
- }
-
- noway_assert((rsAdditionalCandidates | rsAdditional) == rsAdditionalCandidates);
-
- // We only need to spill registers that are modified by the qmark tree, as specified in tree->gtUsedRegs.
- // If we ever need to use and spill a register while generating code that is not in tree->gtUsedRegs,
- // we will have unbalanced spills and generate bad code.
- regMaskTP rsSpill =
- ((regSet.rsMaskUsed & ~(regSet.rsMaskVars | regSet.rsMaskResvd)) | rsAdditional) & tree->gtUsedRegs;
-
-#ifdef DEBUG
- // Under register stress, regSet.rsPickReg() ignores the recommended registers and always picks
- // 'bad' registers, causing spills. So, just force all used registers to get spilled
- // in the stress case, to avoid the problem we're trying to resolve here. Thus, any spills
- // that occur within the qmark condition, 'then' case, or 'else' case, will have to be
- // unspilled while generating that same tree.
-
- if (regSet.rsStressRegs() >= 1)
- {
- rsSpill |= regSet.rsMaskUsed & ~(regSet.rsMaskVars | regSet.rsMaskLock | regSet.rsMaskResvd);
- }
-#endif // DEBUG
-
- if (rsSpill)
- {
- // 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.
- regMaskTP gcRegSavedByref = gcInfo.gcRegByrefSetCur & rsAdditional;
- regMaskTP gcRegSavedGCRef = gcInfo.gcRegGCrefSetCur & rsAdditional;
-
- // regSet.rsSpillRegs() will assert if we try to spill any enregistered variables.
- // So, pretend there aren't any, and spill them anyway. This will only occur
- // if rsAdditional is non-empty.
- regMaskTP rsTemp = regSet.rsMaskVars;
- regSet.ClearMaskVars();
-
- regSet.rsSpillRegs(rsSpill);
-
- // Restore gc tracking masks.
- gcInfo.gcRegByrefSetCur |= gcRegSavedByref;
- gcInfo.gcRegGCrefSetCur |= gcRegSavedGCRef;
-
- // Set regSet.rsMaskVars back to normal
- regSet.rsMaskVars = rsTemp;
- }
- }
-
- // Generate the conditional jump but without doing any StackFP fixups.
- genCondJump(op1, lab_true, lab_false, false);
-
- /* Save the current liveness, register status, and GC pointers */
- /* This is the liveness information upon entry */
- /* to both the then and else parts of the qmark */
-
- saveLiveness(&entryLiveness);
-
- /* Clear the liveness of any local variables that are dead upon */
- /* entry to the else part. */
-
- /* Subtract the liveSet upon entry of the then part (op1->gtNext) */
- /* from the "colon or op2" liveSet */
- genDyingVars(compiler->compCurLife, tree->gtQmark.gtElseLiveSet);
-
- /* genCondJump() closes the current emitter block */
-
- genDefineTempLabel(lab_false);
-
-#if FEATURE_STACK_FP_X87
- // Store fpstate
-
- QmarkStateStackFP tempFPState;
- bool bHasFPUState = !compCurFPState.IsEmpty();
- genQMarkBeforeElseStackFP(&tempFPState, tree->gtQmark.gtElseLiveSet, op1->gtNext);
-#endif
-
- /* Does the operator yield a value? */
-
- if (tree->gtType == TYP_VOID)
- {
- /* Generate the code for the else part of the qmark */
-
- genCodeForTree(elseNode, needReg, bestReg);
-
- /* The type is VOID, so we shouldn't have computed a value */
-
- noway_assert(!(elseNode->InReg()));
-
- /* Save the current liveness, register status, and GC pointers */
- /* This is the liveness information upon exit of the then part of the qmark */
-
- saveLiveness(&exitLiveness);
-
- /* Is there a 'then' part? */
-
- if (thenNode->IsNothingNode())
- {
-#if FEATURE_STACK_FP_X87
- if (bHasFPUState)
- {
- // We had FP state on entry just after the condition, so potentially, the else
- // node may have to do transition work.
- lab_done = genCreateTempLabel();
-
- /* Generate jmp lab_done */
-
- inst_JMP(EJ_jmp, lab_done);
-
- /* No 'then' - just generate the 'lab_true' label */
-
- genDefineTempLabel(lab_true);
-
- // We need to do this after defining the lab_false label
- genQMarkAfterElseBlockStackFP(&tempFPState, compiler->compCurLife, op2->gtNext);
- genQMarkAfterThenBlockStackFP(&tempFPState);
- genDefineTempLabel(lab_done);
- }
- else
-#endif // FEATURE_STACK_FP_X87
- {
- /* No 'then' - just generate the 'lab_true' label */
- genDefineTempLabel(lab_true);
- }
- }
- else
- {
- lab_done = genCreateTempLabel();
-
- /* Generate jmp lab_done */
-
- inst_JMP(EJ_jmp, lab_done);
-
- /* Restore the liveness that we had upon entry of the then part of the qmark */
-
- restoreLiveness(&entryLiveness);
-
- /* Clear the liveness of any local variables that are dead upon */
- /* entry to the then part. */
- genDyingVars(compiler->compCurLife, tree->gtQmark.gtThenLiveSet);
-
- /* Generate lab_true: */
-
- genDefineTempLabel(lab_true);
-#if FEATURE_STACK_FP_X87
- // We need to do this after defining the lab_false label
- genQMarkAfterElseBlockStackFP(&tempFPState, compiler->compCurLife, op2->gtNext);
-#endif
- /* Enter the then part - trash all registers */
-
- regTracker.rsTrackRegClr();
-
- /* Generate the code for the then part of the qmark */
-
- genCodeForTree(thenNode, needReg, bestReg);
-
- /* The type is VOID, so we shouldn't have computed a value */
-
- noway_assert(!(thenNode->InReg()));
-
- unspillLiveness(&exitLiveness);
-
- /* Verify that the exit liveness information is the same for the two parts of the qmark */
-
- checkLiveness(&exitLiveness);
-#if FEATURE_STACK_FP_X87
- genQMarkAfterThenBlockStackFP(&tempFPState);
-#endif
- /* Define the "result" label */
-
- genDefineTempLabel(lab_done);
- }
-
- /* Join of the two branches - trash all registers */
-
- regTracker.rsTrackRegClr();
-
- /* We're just about done */
-
- genUpdateLife(tree);
- }
- else
- {
- /* Generate code for a qmark that generates a value */
-
- /* Generate the code for the else part of the qmark */
-
- noway_assert(elseNode->IsNothingNode() == false);
-
- /* Compute the elseNode into any free register */
- genComputeReg(elseNode, needReg, RegSet::ANY_REG, RegSet::FREE_REG, true);
- noway_assert(elseNode->InReg());
- noway_assert(elseNode->gtRegNum != REG_NA);
-
- /* Record the chosen register */
- reg = elseNode->gtRegNum;
- regs = genRegMask(reg);
-
- /* Save the current liveness, register status, and GC pointers */
- /* This is the liveness information upon exit of the else part of the qmark */
-
- saveLiveness(&exitLiveness);
-
- /* Generate jmp lab_done */
- lab_done = genCreateTempLabel();
-
-#ifdef DEBUG
- // We will use this to assert we don't emit instructions if we decide not to
- // do the jmp
- unsigned emittedInstructions = getEmitter()->emitInsCount;
- bool bSkippedJump = false;
-#endif
- // We would like to know here if the else node is really going to generate
- // code, as if it isn't, we're generating here a jump to the next instruction.
- // What you would really like is to be able to go back and remove the jump, but
- // we have no way of doing that right now.
-
- if (
-#if FEATURE_STACK_FP_X87
- !bHasFPUState && // If there is no FPU state, we won't need an x87 transition
-#endif
- genIsEnregisteredIntVariable(thenNode) == reg)
- {
-#ifdef DEBUG
- // For the moment, fix this easy case (enregistered else node), which
- // is the one that happens all the time.
-
- bSkippedJump = true;
-#endif
- }
- else
- {
- inst_JMP(EJ_jmp, lab_done);
- }
-
- /* Restore the liveness that we had upon entry of the else part of the qmark */
-
- restoreLiveness(&entryLiveness);
-
- /* Clear the liveness of any local variables that are dead upon */
- /* entry to the then part. */
- genDyingVars(compiler->compCurLife, tree->gtQmark.gtThenLiveSet);
-
- /* Generate lab_true: */
- genDefineTempLabel(lab_true);
-#if FEATURE_STACK_FP_X87
- // Store FP state
-
- // We need to do this after defining the lab_true label
- genQMarkAfterElseBlockStackFP(&tempFPState, compiler->compCurLife, op2->gtNext);
-#endif
- /* Enter the then part - trash all registers */
-
- regTracker.rsTrackRegClr();
-
- /* Generate the code for the then part of the qmark */
-
- noway_assert(thenNode->IsNothingNode() == false);
-
- /* This must place a value into the chosen register */
- genComputeReg(thenNode, regs, RegSet::EXACT_REG, RegSet::FREE_REG, true);
-
- noway_assert(thenNode->InReg());
- noway_assert(thenNode->gtRegNum == reg);
-
- unspillLiveness(&exitLiveness);
-
- /* Verify that the exit liveness information is the same for the two parts of the qmark */
- checkLiveness(&exitLiveness);
-#if FEATURE_STACK_FP_X87
- genQMarkAfterThenBlockStackFP(&tempFPState);
-#endif
-
-#ifdef DEBUG
- noway_assert(bSkippedJump == false || getEmitter()->emitInsCount == emittedInstructions);
-#endif
-
- /* Define the "result" label */
- genDefineTempLabel(lab_done);
-
- /* Join of the two branches - trash all registers */
-
- regTracker.rsTrackRegClr();
-
- /* Check whether this subtree has freed up any variables */
-
- genUpdateLife(tree);
-
- genMarkTreeInReg(tree, reg);
- }
-}
-
-/*****************************************************************************
- *
- * Generate code for a qmark colon using the CMOV instruction. It's OK
- * to return false when we can't easily implement it using a cmov (leading
- * genCodeForQmark to implement it using branches).
- */
-
-bool CodeGen::genCodeForQmarkWithCMOV(GenTree* tree, regMaskTP destReg, regMaskTP bestReg)
-{
-#ifdef _TARGET_XARCH_
- GenTree* cond = tree->gtOp.gtOp1;
- GenTree* colon = tree->gtOp.gtOp2;
- // Warning: this naming of the local vars is backwards!
- GenTree* thenNode = colon->gtOp.gtOp1;
- GenTree* elseNode = colon->gtOp.gtOp2;
- GenTree* alwaysNode;
- GenTree* predicateNode;
- regNumber reg;
- regMaskTP needReg = destReg;
-
- noway_assert(tree->gtOper == GT_QMARK);
- noway_assert(cond->OperIsCompare());
- noway_assert(colon->gtOper == GT_COLON);
-
-#ifdef DEBUG
- if (JitConfig.JitNoCMOV())
- {
- return false;
- }
-#endif
-
- /* Can only implement CMOV on processors that support it */
-
- if (!compiler->opts.compUseCMOV)
- {
- return false;
- }
-
- /* thenNode better be a local or a constant */
-
- if ((thenNode->OperGet() != GT_CNS_INT) && (thenNode->OperGet() != GT_LCL_VAR))
- {
- return false;
- }
-
- /* elseNode better be a local or a constant or nothing */
-
- if ((elseNode->OperGet() != GT_CNS_INT) && (elseNode->OperGet() != GT_LCL_VAR))
- {
- return false;
- }
-
- /* can't handle two constants here */
-
- if ((thenNode->OperGet() == GT_CNS_INT) && (elseNode->OperGet() == GT_CNS_INT))
- {
- return false;
- }
-
- /* let's not handle comparisons of non-integer types */
-
- if (!varTypeIsI(cond->gtOp.gtOp1->gtType))
- {
- return false;
- }
-
- /* Choose nodes for predicateNode and alwaysNode. Swap cond if necessary.
- The biggest constraint is that cmov doesn't take an integer argument.
- */
-
- bool reverseCond = false;
- if (elseNode->OperGet() == GT_CNS_INT)
- {
- // else node is a constant
-
- alwaysNode = elseNode;
- predicateNode = thenNode;
- reverseCond = true;
- }
- else
- {
- alwaysNode = thenNode;
- predicateNode = elseNode;
- }
-
- // If the live set in alwaysNode is not the same as in tree, then
- // the variable in predicate node dies here. This is a dangerous
- // case that we don't handle (genComputeReg could overwrite
- // the value of the variable in the predicate node).
-
- // This assert is just paranoid (we've already asserted it above)
- assert(predicateNode->OperGet() == GT_LCL_VAR);
- if ((predicateNode->gtFlags & GTF_VAR_DEATH) != 0)
- {
- return false;
- }
-
- // Pass this point we are comitting to use CMOV.
-
- if (reverseCond)
- {
- compiler->gtReverseCond(cond);
- }
-
- emitJumpKind jumpKind = genCondSetFlags(cond);
-
- // Compute the always node into any free register. If it's a constant,
- // we need to generate the mov instruction here (otherwise genComputeReg might
- // modify the flags, as in xor reg,reg).
-
- if (alwaysNode->OperGet() == GT_CNS_INT)
- {
- reg = regSet.rsPickReg(needReg, bestReg);
- inst_RV_IV(INS_mov, reg, alwaysNode->gtIntCon.gtIconVal, emitActualTypeSize(alwaysNode->TypeGet()));
- gcInfo.gcMarkRegPtrVal(reg, alwaysNode->TypeGet());
- regTracker.rsTrackRegTrash(reg);
- }
- else
- {
- genComputeReg(alwaysNode, needReg, RegSet::ANY_REG, RegSet::FREE_REG, true);
- noway_assert(alwaysNode->InReg());
- noway_assert(alwaysNode->gtRegNum != REG_NA);
-
- // Record the chosen register
-
- reg = alwaysNode->gtRegNum;
- }
-
- regNumber regPredicate = REG_NA;
-
- // Is predicateNode an enregistered variable?
-
- if (genMarkLclVar(predicateNode))
- {
- // Variable lives in a register
-
- regPredicate = predicateNode->gtRegNum;
- }
-#if REDUNDANT_LOAD
- else
- {
- // Checks if the variable happens to be in any of the registers
-
- regPredicate = findStkLclInReg(predicateNode->gtLclVarCommon.gtLclNum);
- }
-#endif
-
- const static instruction EJtoCMOV[] = {INS_nop, INS_nop, INS_cmovo, INS_cmovno, INS_cmovb, INS_cmovae,
- INS_cmove, INS_cmovne, INS_cmovbe, INS_cmova, INS_cmovs, INS_cmovns,
- INS_cmovpe, INS_cmovpo, INS_cmovl, INS_cmovge, INS_cmovle, INS_cmovg};
-
- noway_assert((unsigned)jumpKind < _countof(EJtoCMOV));
- instruction cmov_ins = EJtoCMOV[jumpKind];
-
- noway_assert(insIsCMOV(cmov_ins));
-
- if (regPredicate != REG_NA)
- {
- // regPredicate is in a register
-
- inst_RV_RV(cmov_ins, reg, regPredicate, predicateNode->TypeGet());
- }
- else
- {
- // regPredicate is in memory
-
- inst_RV_TT(cmov_ins, reg, predicateNode, NULL);
- }
- gcInfo.gcMarkRegPtrVal(reg, predicateNode->TypeGet());
- regTracker.rsTrackRegTrash(reg);
-
- genUpdateLife(alwaysNode);
- genUpdateLife(predicateNode);
- genCodeForTree_DONE_LIFE(tree, reg);
- return true;
-#else
- return false;
-#endif
-}
-
-#ifdef _TARGET_XARCH_
-void CodeGen::genCodeForMultEAX(GenTree* tree)
-{
- GenTree* op1 = tree->gtOp.gtOp1;
- GenTree* op2 = tree->gtGetOp2();
- bool ovfl = tree->gtOverflow();
- regNumber reg = DUMMY_INIT(REG_CORRUPT);
- regMaskTP addrReg;
-
- noway_assert(tree->OperGet() == GT_MUL);
-
- /* We'll evaluate 'op1' first */
-
- regMaskTP op1Mask = regSet.rsMustExclude(RBM_EAX, op2->gtRsvdRegs);
-
- /* Generate the op1 into op1Mask and hold on to it. freeOnly=true */
-
- genComputeReg(op1, op1Mask, RegSet::ANY_REG, RegSet::KEEP_REG, true);
- noway_assert(op1->InReg());
-
- // If op2 is a constant we need to load the constant into a register
- if (op2->OperKind() & GTK_CONST)
- {
- genCodeForTree(op2, RBM_EDX); // since EDX is going to be spilled anyway
- noway_assert(op2->InReg());
- regSet.rsMarkRegUsed(op2);
- addrReg = genRegMask(op2->gtRegNum);
- }
- else
- {
- /* Make the second operand addressable */
- // Try to avoid EAX.
- addrReg = genMakeRvalueAddressable(op2, RBM_ALLINT & ~RBM_EAX, RegSet::KEEP_REG, false);
- }
-
- /* Make sure the first operand is still in a register */
- // op1 *must* go into EAX.
- genRecoverReg(op1, RBM_EAX, RegSet::KEEP_REG);
- noway_assert(op1->InReg());
-
- reg = op1->gtRegNum;
-
- // For 8 bit operations, we need to pick byte addressable registers
-
- if (ovfl && varTypeIsByte(tree->TypeGet()) && !(genRegMask(reg) & RBM_BYTE_REGS))
- {
- regNumber byteReg = regSet.rsGrabReg(RBM_BYTE_REGS);
-
- inst_RV_RV(INS_mov, byteReg, reg);
-
- regTracker.rsTrackRegTrash(byteReg);
- regSet.rsMarkRegFree(genRegMask(reg));
-
- reg = byteReg;
- op1->gtRegNum = reg;
- regSet.rsMarkRegUsed(op1);
- }
-
- /* Make sure the operand is still addressable */
- addrReg = genKeepAddressable(op2, addrReg, genRegMask(reg));
-
- /* Free up the operand, if it's a regvar */
-
- genUpdateLife(op2);
-
- /* The register is about to be trashed */
-
- regTracker.rsTrackRegTrash(reg);
-
- // For overflow instructions, tree->TypeGet() is the accurate type,
- // and gives us the size for the operands.
-
- emitAttr opSize = emitTypeSize(tree->TypeGet());
-
- /* Compute the new value */
-
- noway_assert(op1->gtRegNum == REG_EAX);
-
- // Make sure Edx is free (unless used by op2 itself)
- bool op2Released = false;
-
- if ((addrReg & RBM_EDX) == 0)
- {
- // op2 does not use Edx, so make sure noone else does either
- regSet.rsGrabReg(RBM_EDX);
- }
- else if (regSet.rsMaskMult & RBM_EDX)
- {
- /* Edx is used by op2 and some other trees.
- Spill the other trees besides op2. */
-
- regSet.rsGrabReg(RBM_EDX);
- op2Released = true;
-
- /* keepReg==RegSet::FREE_REG so that the other multi-used trees
- don't get marked as unspilled as well. */
- regSet.rsUnspillReg(op2, RBM_EDX, RegSet::FREE_REG);
- }
-
- instruction ins;
-
- if (tree->gtFlags & GTF_UNSIGNED)
- ins = INS_mulEAX;
- else
- ins = INS_imulEAX;
-
- inst_TT(ins, op2, 0, 0, opSize);
-
- /* Both EAX and EDX are now trashed */
-
- regTracker.rsTrackRegTrash(REG_EAX);
- regTracker.rsTrackRegTrash(REG_EDX);
-
- /* Free up anything that was tied up by the operand */
-
- if (!op2Released)
- genDoneAddressable(op2, addrReg, RegSet::KEEP_REG);
-
- /* The result will be where the first operand is sitting */
-
- /* We must use RegSet::KEEP_REG since op1 can have a GC pointer here */
- genRecoverReg(op1, 0, RegSet::KEEP_REG);
-
- reg = op1->gtRegNum;
- noway_assert(reg == REG_EAX);
-
- genReleaseReg(op1);
-
- /* Do we need an overflow check */
-
- if (ovfl)
- genCheckOverflow(tree);
-
- genCodeForTree_DONE(tree, reg);
-}
-#endif // _TARGET_XARCH_
-
-#ifdef _TARGET_ARM_
-void CodeGen::genCodeForMult64(GenTree* tree, regMaskTP destReg, regMaskTP bestReg)
-{
- GenTree* op1 = tree->gtOp.gtOp1;
- GenTree* op2 = tree->gtGetOp2();
-
- noway_assert(tree->OperGet() == GT_MUL);
-
- /* Generate the first operand into some register */
-
- genComputeReg(op1, RBM_ALLINT, RegSet::ANY_REG, RegSet::KEEP_REG);
- noway_assert(op1->InReg());
-
- /* Generate the second operand into some register */
-
- genComputeReg(op2, RBM_ALLINT, RegSet::ANY_REG, RegSet::KEEP_REG);
- noway_assert(op2->InReg());
-
- /* Make sure the first operand is still in a register */
- genRecoverReg(op1, 0, RegSet::KEEP_REG);
- noway_assert(op1->InReg());
-
- /* Free up the operands */
- genUpdateLife(tree);
-
- genReleaseReg(op1);
- genReleaseReg(op2);
-
- regNumber regLo = regSet.rsPickReg(destReg, bestReg);
- regNumber regHi;
-
- regSet.rsLockReg(genRegMask(regLo));
- regHi = regSet.rsPickReg(destReg & ~genRegMask(regLo));
- regSet.rsUnlockReg(genRegMask(regLo));
-
- instruction ins;
- if (tree->gtFlags & GTF_UNSIGNED)
- ins = INS_umull;
- else
- ins = INS_smull;
-
- getEmitter()->emitIns_R_R_R_R(ins, EA_4BYTE, regLo, regHi, op1->gtRegNum, op2->gtRegNum);
- regTracker.rsTrackRegTrash(regHi);
- regTracker.rsTrackRegTrash(regLo);
-
- /* Do we need an overflow check */
-
- if (tree->gtOverflow())
- {
- // Keep regLo [and regHi] locked while generating code for the gtOverflow() case
- //
- regSet.rsLockReg(genRegMask(regLo));
-
- if (tree->gtFlags & GTF_MUL_64RSLT)
- regSet.rsLockReg(genRegMask(regHi));
-
- regNumber regTmpHi = regHi;
- if ((tree->gtFlags & GTF_UNSIGNED) == 0)
- {
- getEmitter()->emitIns_R_I(INS_cmp, EA_4BYTE, regLo, 0x80000000);
- regTmpHi = regSet.rsPickReg(RBM_ALLINT);
- getEmitter()->emitIns_R_R_I(INS_adc, EA_4BYTE, regTmpHi, regHi, 0);
- regTracker.rsTrackRegTrash(regTmpHi);
- }
- getEmitter()->emitIns_R_I(INS_cmp, EA_4BYTE, regTmpHi, 0);
-
- // Jump to the block which will throw the expection
- emitJumpKind jmpNotEqual = genJumpKindForOper(GT_NE, CK_SIGNED);
- genJumpToThrowHlpBlk(jmpNotEqual, SCK_OVERFLOW);
-
- // Unlock regLo [and regHi] after generating code for the gtOverflow() case
- //
- regSet.rsUnlockReg(genRegMask(regLo));
-
- if (tree->gtFlags & GTF_MUL_64RSLT)
- regSet.rsUnlockReg(genRegMask(regHi));
- }
-
- genUpdateLife(tree);
-
- if (tree->gtFlags & GTF_MUL_64RSLT)
- genMarkTreeInRegPair(tree, gen2regs2pair(regLo, regHi));
- else
- genMarkTreeInReg(tree, regLo);
-}
-#endif // _TARGET_ARM_
-
-/*****************************************************************************
- *
- * Generate code for a simple binary arithmetic or logical operator.
- * Handles GT_AND, GT_OR, GT_XOR, GT_ADD, GT_SUB, GT_MUL.
- */
-
-void CodeGen::genCodeForTreeSmpBinArithLogOp(GenTree* tree, regMaskTP destReg, regMaskTP bestReg)
-{
- instruction ins;
- genTreeOps oper = tree->OperGet();
- const var_types treeType = tree->TypeGet();
- GenTree* op1 = tree->gtOp.gtOp1;
- GenTree* op2 = tree->gtGetOp2();
- insFlags flags = tree->gtSetFlags() ? INS_FLAGS_SET : INS_FLAGS_DONT_CARE;
- regNumber reg = DUMMY_INIT(REG_CORRUPT);
- regMaskTP needReg = destReg;
-
- /* Figure out what instruction to generate */
-
- bool isArith;
- switch (oper)
- {
- case GT_AND:
- ins = INS_AND;
- isArith = false;
- break;
- case GT_OR:
- ins = INS_OR;
- isArith = false;
- break;
- case GT_XOR:
- ins = INS_XOR;
- isArith = false;
- break;
- case GT_ADD:
- ins = INS_add;
- isArith = true;
- break;
- case GT_SUB:
- ins = INS_sub;
- isArith = true;
- break;
- case GT_MUL:
- ins = INS_MUL;
- isArith = true;
- break;
- default:
- unreached();
- }
-
-#ifdef _TARGET_XARCH_
- /* Special case: try to use the 3 operand form "imul reg, op1, icon" */
-
- if ((oper == GT_MUL) &&
- op2->IsIntCnsFitsInI32() && // op2 is a constant that fits in a sign-extended 32-bit immediate
- !op1->IsCnsIntOrI() && // op1 is not a constant
- (tree->gtFlags & GTF_MUL_64RSLT) == 0 && // tree not marked with MUL_64RSLT
- !varTypeIsByte(treeType) && // No encoding for say "imul al,al,imm"
- !tree->gtOverflow()) // 3 operand imul doesn't set flags
- {
- /* Make the first operand addressable */
-
- regMaskTP addrReg = genMakeRvalueAddressable(op1, needReg & ~op2->gtRsvdRegs, RegSet::FREE_REG, false);
-
- /* Grab a register for the target */
-
- reg = regSet.rsPickReg(needReg, bestReg);
-
-#if LEA_AVAILABLE
- /* Compute the value into the target: reg=op1*op2_icon */
- if (op2->gtIntCon.gtIconVal == 3 || op2->gtIntCon.gtIconVal == 5 || op2->gtIntCon.gtIconVal == 9)
- {
- regNumber regSrc;
- if (op1->InReg())
- {
- regSrc = op1->gtRegNum;
- }
- else
- {
- inst_RV_TT(INS_mov, reg, op1, 0, emitActualTypeSize(op1->TypeGet()));
- regSrc = reg;
- }
- getEmitter()->emitIns_R_ARX(INS_lea, emitActualTypeSize(treeType), reg, regSrc, regSrc,
- (op2->gtIntCon.gtIconVal & -2), 0);
- }
- else
-#endif // LEA_AVAILABLE
- {
- /* Compute the value into the target: reg=op1*op2_icon */
- inst_RV_TT_IV(INS_MUL, reg, op1, (int)op2->gtIntCon.gtIconVal);
- }
-
- /* The register has been trashed now */
-
- regTracker.rsTrackRegTrash(reg);
-
- /* The address is no longer live */
-
- genDoneAddressable(op1, addrReg, RegSet::FREE_REG);
-
- genCodeForTree_DONE(tree, reg);
- return;
- }
-#endif // _TARGET_XARCH_
-
- bool ovfl = false;
-
- if (isArith)
- {
- // We only reach here for GT_ADD, GT_SUB and GT_MUL.
- assert((oper == GT_ADD) || (oper == GT_SUB) || (oper == GT_MUL));
-
- ovfl = tree->gtOverflow();
-
- /* We record the accurate (small) types in trees only we need to
- * check for overflow. Otherwise we record genActualType()
- */
-
- noway_assert(ovfl || (treeType == genActualType(treeType)));
-
-#if LEA_AVAILABLE
-
- /* Can we use an 'lea' to compute the result?
- Can't use 'lea' for overflow as it doesn't set flags
- Can't use 'lea' unless we have at least two free registers */
- {
- bool bEnoughRegs = genRegCountForLiveIntEnregVars(tree) + // Live intreg variables
- genCountBits(regSet.rsMaskLock) + // Locked registers
- 2 // We will need two regisers
- <= genCountBits(RBM_ALLINT & ~(doubleAlignOrFramePointerUsed() ? RBM_FPBASE : 0));
-
- regMaskTP regs = RBM_NONE; // OUT argument
- if (!ovfl && bEnoughRegs && genMakeIndAddrMode(tree, NULL, true, needReg, RegSet::FREE_REG, &regs, false))
- {
- emitAttr size;
-
- /* Is the value now computed in some register? */
-
- if (tree->InReg())
- {
- genCodeForTree_REG_VAR1(tree);
- return;
- }
-
- /* If we can reuse op1/2's register directly, and 'tree' is
- a simple expression (ie. not in scaled index form),
- might as well just use "add" instead of "lea" */
-
- // However, if we're in a context where we want to evaluate "tree" into a specific
- // register different from the reg we'd use in this optimization, then it doesn't
- // make sense to do the "add", since we'd also have to do a "mov."
- if (op1->InReg())
- {
- reg = op1->gtRegNum;
-
- if ((genRegMask(reg) & regSet.rsRegMaskFree()) && (genRegMask(reg) & needReg))
- {
- if (op2->InReg())
- {
- /* Simply add op2 to the register */
-
- inst_RV_TT(INS_add, reg, op2, 0, emitTypeSize(treeType), flags);
-
- if (tree->gtSetFlags())
- genFlagsEqualToReg(tree, reg);
-
- goto DONE_LEA_ADD;
- }
- else if (op2->OperGet() == GT_CNS_INT)
- {
- /* Simply add op2 to the register */
-
- genIncRegBy(reg, op2->gtIntCon.gtIconVal, tree, treeType);
-
- goto DONE_LEA_ADD;
- }
- }
- }
-
- if (op2->InReg())
- {
- reg = op2->gtRegNum;
-
- if ((genRegMask(reg) & regSet.rsRegMaskFree()) && (genRegMask(reg) & needReg))
- {
- if (op1->InReg())
- {
- /* Simply add op1 to the register */
-
- inst_RV_TT(INS_add, reg, op1, 0, emitTypeSize(treeType), flags);
-
- if (tree->gtSetFlags())
- genFlagsEqualToReg(tree, reg);
-
- goto DONE_LEA_ADD;
- }
- }
- }
-
- // The expression either requires a scaled-index form, or the
- // op1 or op2's register can't be targeted, this can be
- // caused when op1 or op2 are enregistered variables.
-
- reg = regSet.rsPickReg(needReg, bestReg);
- size = emitActualTypeSize(treeType);
-
- /* Generate "lea reg, [addr-mode]" */
-
- inst_RV_AT(INS_lea, size, treeType, reg, tree, 0, flags);
-
-#ifndef _TARGET_XARCH_
- // Don't call genFlagsEqualToReg on x86/x64
- // as it does not set the flags
- if (tree->gtSetFlags())
- genFlagsEqualToReg(tree, reg);
-#endif
-
- DONE_LEA_ADD:
- /* The register has been trashed now */
- regTracker.rsTrackRegTrash(reg);
-
- genDoneAddressable(tree, regs, RegSet::FREE_REG);
-
- /* The following could be an 'inner' pointer!!! */
-
- noway_assert(treeType == TYP_BYREF || !varTypeIsGC(treeType));
-
- if (treeType == TYP_BYREF)
- {
- genUpdateLife(tree);
-
- gcInfo.gcMarkRegSetNpt(genRegMask(reg)); // in case "reg" was a TYP_GCREF before
- gcInfo.gcMarkRegPtrVal(reg, TYP_BYREF);
- }
-
- genCodeForTree_DONE(tree, reg);
- return;
- }
- }
-
-#endif // LEA_AVAILABLE
-
- noway_assert((varTypeIsGC(treeType) == false) || (treeType == TYP_BYREF && (ins == INS_add || ins == INS_sub)));
- }
-
- /* The following makes an assumption about gtSetEvalOrder(this) */
-
- noway_assert((tree->gtFlags & GTF_REVERSE_OPS) == 0);
-
- /* Compute a useful register mask */
- needReg = regSet.rsMustExclude(needReg, op2->gtRsvdRegs);
- needReg = regSet.rsNarrowHint(needReg, regSet.rsRegMaskFree());
-
- // Determine what registers go live between op1 and op2
- // Don't bother checking if op1 is already in a register.
- // This is not just for efficiency; if it's already in a
- // register then it may already be considered "evaluated"
- // for the purposes of liveness, in which genNewLiveRegMask
- // will assert
- if (!op1->InReg())
- {
- regMaskTP newLiveMask = genNewLiveRegMask(op1, op2);
- if (newLiveMask)
- {
- needReg = regSet.rsNarrowHint(needReg, ~newLiveMask);
- }
- }
-
-#if CPU_HAS_BYTE_REGS
- /* 8-bit operations can only be done in the byte-regs */
- if (varTypeIsByte(treeType))
- needReg = regSet.rsNarrowHint(RBM_BYTE_REGS, needReg);
-#endif // CPU_HAS_BYTE_REGS
-
- // Try selecting one of the 'bestRegs'
- needReg = regSet.rsNarrowHint(needReg, bestReg);
-
- /* Special case: small_val & small_mask */
-
- if (varTypeIsSmall(op1->TypeGet()) && op2->IsCnsIntOrI() && oper == GT_AND)
- {
- size_t and_val = op2->gtIntCon.gtIconVal;
- size_t andMask;
- var_types typ = op1->TypeGet();
-
- switch (typ)
- {
- case TYP_BOOL:
- case TYP_BYTE:
- case TYP_UBYTE:
- andMask = 0x000000FF;
- break;
- case TYP_SHORT:
- case TYP_USHORT:
- andMask = 0x0000FFFF;
- break;
- default:
- noway_assert(!"unexpected type");
- return;
- }
-
- // Is the 'and_val' completely contained within the bits found in 'andMask'
- if ((and_val & ~andMask) == 0)
- {
- // We must use unsigned instructions when loading op1
- if (varTypeIsByte(typ))
- {
- op1->gtType = TYP_UBYTE;
- }
- else // varTypeIsShort(typ)
- {
- assert(varTypeIsShort(typ));
- op1->gtType = TYP_USHORT;
- }
-
- /* Generate the first operand into a scratch register */
-
- op1 = genCodeForCommaTree(op1);
- genComputeReg(op1, needReg, RegSet::ANY_REG, RegSet::FREE_REG, true);
-
- noway_assert(op1->InReg());
-
- regNumber op1Reg = op1->gtRegNum;
-
- // Did we end up in an acceptable register?
- // and do we have an acceptable free register available to grab?
- //
- if (((genRegMask(op1Reg) & needReg) == 0) && ((regSet.rsRegMaskFree() & needReg) != 0))
- {
- // See if we can pick a register from bestReg
- bestReg &= needReg;
-
- // Grab an acceptable register
- regNumber newReg;
- if ((bestReg & regSet.rsRegMaskFree()) != 0)
- newReg = regSet.rsGrabReg(bestReg);
- else
- newReg = regSet.rsGrabReg(needReg);
-
- noway_assert(op1Reg != newReg);
-
- /* Update the value in the target register */
-
- regTracker.rsTrackRegCopy(newReg, op1Reg);
-
- inst_RV_RV(ins_Copy(op1->TypeGet()), newReg, op1Reg, op1->TypeGet());
-
- /* The value has been transferred to 'reg' */
-
- if ((genRegMask(op1Reg) & regSet.rsMaskUsed) == 0)
- gcInfo.gcMarkRegSetNpt(genRegMask(op1Reg));
-
- gcInfo.gcMarkRegPtrVal(newReg, op1->TypeGet());
-
- /* The value is now in an appropriate register */
-
- op1->gtRegNum = newReg;
- }
- noway_assert(op1->InReg());
- genUpdateLife(op1);
-
- /* Mark the register as 'used' */
- regSet.rsMarkRegUsed(op1);
- reg = op1->gtRegNum;
-
- if (and_val != andMask) // Does the "and" mask only cover some of the bits?
- {
- /* "and" the value */
-
- inst_RV_IV(INS_AND, reg, and_val, EA_4BYTE, flags);
- }
-
-#ifdef DEBUG
- /* Update the live set of register variables */
- if (compiler->opts.varNames)
- genUpdateLife(tree);
-#endif
-
- /* Now we can update the register pointer information */
-
- genReleaseReg(op1);
- gcInfo.gcMarkRegPtrVal(reg, treeType);
-
- genCodeForTree_DONE_LIFE(tree, reg);
- return;
- }
- }
-
-#ifdef _TARGET_XARCH_
-
- // Do we have to use the special "imul" instruction
- // which has eax as the implicit operand ?
- //
- bool multEAX = false;
-
- if (oper == GT_MUL)
- {
- if (tree->gtFlags & GTF_MUL_64RSLT)
- {
- /* Only multiplying with EAX will leave the 64-bit
- * result in EDX:EAX */
-
- multEAX = true;
- }
- else if (ovfl)
- {
- if (tree->gtFlags & GTF_UNSIGNED)
- {
- /* "mul reg/mem" always has EAX as default operand */
-
- multEAX = true;
- }
- else if (varTypeIsSmall(treeType))
- {
- /* Only the "imul with EAX" encoding has the 'w' bit
- * to specify the size of the operands */
-
- multEAX = true;
- }
- }
- }
-
- if (multEAX)
- {
- noway_assert(oper == GT_MUL);
-
- return genCodeForMultEAX(tree);
- }
-#endif // _TARGET_XARCH_
-
-#ifdef _TARGET_ARM_
-
- // Do we have to use the special 32x32 => 64 bit multiply
- //
- bool mult64 = false;
-
- if (oper == GT_MUL)
- {
- if (tree->gtFlags & GTF_MUL_64RSLT)
- {
- mult64 = true;
- }
- else if (ovfl)
- {
- // We always must use the 32x32 => 64 bit multiply
- // to detect overflow
- mult64 = true;
- }
- }
-
- if (mult64)
- {
- noway_assert(oper == GT_MUL);
-
- return genCodeForMult64(tree, destReg, bestReg);
- }
-#endif // _TARGET_ARM_
-
- /* Generate the first operand into a scratch register */
-
- op1 = genCodeForCommaTree(op1);
- genComputeReg(op1, needReg, RegSet::ANY_REG, RegSet::FREE_REG, true);
-
- noway_assert(op1->InReg());
-
- regNumber op1Reg = op1->gtRegNum;
-
- // Setup needReg with the set of register that we require for op1 to be in
- //
- needReg = RBM_ALLINT;
-
- /* Compute a useful register mask */
- needReg = regSet.rsMustExclude(needReg, op2->gtRsvdRegs);
- needReg = regSet.rsNarrowHint(needReg, regSet.rsRegMaskFree());
-
-#if CPU_HAS_BYTE_REGS
- /* 8-bit operations can only be done in the byte-regs */
- if (varTypeIsByte(treeType))
- needReg = regSet.rsNarrowHint(RBM_BYTE_REGS, needReg);
-#endif // CPU_HAS_BYTE_REGS
-
- // Did we end up in an acceptable register?
- // and do we have an acceptable free register available to grab?
- //
- if (((genRegMask(op1Reg) & needReg) == 0) && ((regSet.rsRegMaskFree() & needReg) != 0))
- {
- // See if we can pick a register from bestReg
- bestReg &= needReg;
-
- // Grab an acceptable register
- regNumber newReg;
- if ((bestReg & regSet.rsRegMaskFree()) != 0)
- newReg = regSet.rsGrabReg(bestReg);
- else
- newReg = regSet.rsGrabReg(needReg);
-
- noway_assert(op1Reg != newReg);
-
- /* Update the value in the target register */
-
- regTracker.rsTrackRegCopy(newReg, op1Reg);
-
- inst_RV_RV(ins_Copy(op1->TypeGet()), newReg, op1Reg, op1->TypeGet());
-
- /* The value has been transferred to 'reg' */
-
- if ((genRegMask(op1Reg) & regSet.rsMaskUsed) == 0)
- gcInfo.gcMarkRegSetNpt(genRegMask(op1Reg));
-
- gcInfo.gcMarkRegPtrVal(newReg, op1->TypeGet());
-
- /* The value is now in an appropriate register */
-
- op1->gtRegNum = newReg;
- }
- noway_assert(op1->InReg());
- op1Reg = op1->gtRegNum;
-
- genUpdateLife(op1);
-
- /* Mark the register as 'used' */
- regSet.rsMarkRegUsed(op1);
-
- bool isSmallConst = false;
-
-#ifdef _TARGET_ARM_
- if ((op2->gtOper == GT_CNS_INT) && arm_Valid_Imm_For_Instr(ins, op2->gtIntCon.gtIconVal, INS_FLAGS_DONT_CARE))
- {
- isSmallConst = true;
- }
-#endif
- /* Make the second operand addressable */
-
- regMaskTP addrReg = genMakeRvalueAddressable(op2, RBM_ALLINT, RegSet::KEEP_REG, isSmallConst);
-
-#if CPU_LOAD_STORE_ARCH
- genRecoverReg(op1, RBM_ALLINT, RegSet::KEEP_REG);
-#else // !CPU_LOAD_STORE_ARCH
- /* Is op1 spilled and op2 in a register? */
-
- if ((op1->gtFlags & GTF_SPILLED) && (op2->InReg()) && (ins != INS_sub))
- {
- noway_assert(ins == INS_add || ins == INS_MUL || ins == INS_AND || ins == INS_OR || ins == INS_XOR);
-
- // genMakeRvalueAddressable(GT_LCL_VAR) shouldn't spill anything
- noway_assert(op2->gtOper != GT_LCL_VAR ||
- varTypeIsSmall(compiler->lvaTable[op2->gtLclVarCommon.gtLclNum].TypeGet()));
-
- reg = op2->gtRegNum;
- regMaskTP regMask = genRegMask(reg);
-
- /* Is the register holding op2 available? */
-
- if (regMask & regSet.rsMaskVars)
- {
- }
- else
- {
- /* Get the temp we spilled into. */
-
- TempDsc* temp = regSet.rsUnspillInPlace(op1, op1->gtRegNum);
-
- /* For 8bit operations, we need to make sure that op2 is
- in a byte-addressable registers */
-
- if (varTypeIsByte(treeType) && !(regMask & RBM_BYTE_REGS))
- {
- regNumber byteReg = regSet.rsGrabReg(RBM_BYTE_REGS);
-
- inst_RV_RV(INS_mov, byteReg, reg);
- regTracker.rsTrackRegTrash(byteReg);
-
- /* op2 couldn't have spilled as it was not sitting in
- RBM_BYTE_REGS, and regSet.rsGrabReg() will only spill its args */
- noway_assert(op2->InReg());
-
- regSet.rsUnlockReg(regMask);
- regSet.rsMarkRegFree(regMask);
-
- reg = byteReg;
- regMask = genRegMask(reg);
- op2->gtRegNum = reg;
- regSet.rsMarkRegUsed(op2);
- }
-
- inst_RV_ST(ins, reg, temp, 0, treeType);
-
- regTracker.rsTrackRegTrash(reg);
-
- /* Free the temp */
-
- compiler->tmpRlsTemp(temp);
-
- /* 'add'/'sub' set all CC flags, others only ZF */
-
- /* If we need to check overflow, for small types, the
- * flags can't be used as we perform the arithmetic
- * operation (on small registers) and then sign extend it
- *
- * NOTE : If we ever don't need to sign-extend the result,
- * we can use the flags
- */
-
- if (tree->gtSetFlags())
- {
- genFlagsEqualToReg(tree, reg);
- }
-
- /* The result is where the second operand is sitting. Mark result reg as free */
- regSet.rsMarkRegFree(genRegMask(reg));
-
- gcInfo.gcMarkRegPtrVal(reg, treeType);
-
- goto CHK_OVF;
- }
- }
-#endif // !CPU_LOAD_STORE_ARCH
-
- /* Make sure the first operand is still in a register */
- regSet.rsLockUsedReg(addrReg);
- genRecoverReg(op1, 0, RegSet::KEEP_REG);
- noway_assert(op1->InReg());
- regSet.rsUnlockUsedReg(addrReg);
-
- reg = op1->gtRegNum;
-
- // For 8 bit operations, we need to pick byte addressable registers
-
- if (varTypeIsByte(treeType) && !(genRegMask(reg) & RBM_BYTE_REGS))
- {
- regNumber byteReg = regSet.rsGrabReg(RBM_BYTE_REGS);
-
- inst_RV_RV(INS_mov, byteReg, reg);
-
- regTracker.rsTrackRegTrash(byteReg);
- regSet.rsMarkRegFree(genRegMask(reg));
-
- reg = byteReg;
- op1->gtRegNum = reg;
- regSet.rsMarkRegUsed(op1);
- }
-
- /* Make sure the operand is still addressable */
- addrReg = genKeepAddressable(op2, addrReg, genRegMask(reg));
-
- /* Free up the operand, if it's a regvar */
-
- genUpdateLife(op2);
-
- /* The register is about to be trashed */
-
- regTracker.rsTrackRegTrash(reg);
-
- {
- bool op2Released = false;
-
- // For overflow instructions, tree->gtType is the accurate type,
- // and gives us the size for the operands.
-
- emitAttr opSize = emitTypeSize(treeType);
-
- /* Compute the new value */
-
- if (isArith && !op2->InReg() && (op2->OperKind() & GTK_CONST)
-#if !CPU_HAS_FP_SUPPORT
- && (treeType == TYP_INT || treeType == TYP_I_IMPL)
-#endif
- )
- {
- ssize_t ival = op2->gtIntCon.gtIconVal;
-
- if (oper == GT_ADD)
- {
- genIncRegBy(reg, ival, tree, treeType, ovfl);
- }
- else if (oper == GT_SUB)
- {
- if (ovfl && ((tree->gtFlags & GTF_UNSIGNED) ||
- (ival == ((treeType == TYP_INT) ? INT32_MIN : SSIZE_T_MIN))) // -0x80000000 == 0x80000000.
- // Therefore we can't use -ival.
- )
- {
- /* For unsigned overflow, we have to use INS_sub to set
- the flags correctly */
-
- genDecRegBy(reg, ival, tree);
- }
- else
- {
- /* Else, we simply add the negative of the value */
-
- genIncRegBy(reg, -ival, tree, treeType, ovfl);
- }
- }
- else if (oper == GT_MUL)
- {
- genMulRegBy(reg, ival, tree, treeType, ovfl);
- }
- }
- else
- {
- // op2 could be a GT_COMMA (i.e. an assignment for a CSE def)
- op2 = op2->gtEffectiveVal();
- if (varTypeIsByte(treeType) && op2->InReg())
- {
- noway_assert(genRegMask(reg) & RBM_BYTE_REGS);
-
- regNumber op2reg = op2->gtRegNum;
- regMaskTP op2regMask = genRegMask(op2reg);
-
- if (!(op2regMask & RBM_BYTE_REGS))
- {
- regNumber byteReg = regSet.rsGrabReg(RBM_BYTE_REGS);
-
- inst_RV_RV(INS_mov, byteReg, op2reg);
- regTracker.rsTrackRegTrash(byteReg);
-
- genDoneAddressable(op2, addrReg, RegSet::KEEP_REG);
- op2Released = true;
-
- op2->gtRegNum = byteReg;
- }
- }
-
- inst_RV_TT(ins, reg, op2, 0, opSize, flags);
- }
-
- /* Free up anything that was tied up by the operand */
-
- if (!op2Released)
- {
- genDoneAddressable(op2, addrReg, RegSet::KEEP_REG);
- }
- }
- /* The result will be where the first operand is sitting */
-
- /* We must use RegSet::KEEP_REG since op1 can have a GC pointer here */
- genRecoverReg(op1, 0, RegSet::KEEP_REG);
-
- reg = op1->gtRegNum;
-
- /* 'add'/'sub' set all CC flags, others only ZF+SF */
-
- if (tree->gtSetFlags())
- genFlagsEqualToReg(tree, reg);
-
- genReleaseReg(op1);
-
-#if !CPU_LOAD_STORE_ARCH
-CHK_OVF:
-#endif // !CPU_LOAD_STORE_ARCH
-
- /* Do we need an overflow check */
-
- if (ovfl)
- genCheckOverflow(tree);
-
- genCodeForTree_DONE(tree, reg);
-}
-
-/*****************************************************************************
- *
- * Generate code for a simple binary arithmetic or logical assignment operator: x <op>= y.
- * Handles GT_ASG_AND, GT_ASG_OR, GT_ASG_XOR, GT_ASG_ADD, GT_ASG_SUB.
- */
-
-void CodeGen::genCodeForTreeSmpBinArithLogAsgOp(GenTree* tree, regMaskTP destReg, regMaskTP bestReg)
-{
- instruction ins;
- const genTreeOps oper = tree->OperGet();
- const var_types treeType = tree->TypeGet();
- GenTree* op1 = tree->gtOp.gtOp1;
- GenTree* op2 = tree->gtGetOp2();
- insFlags flags = tree->gtSetFlags() ? INS_FLAGS_SET : INS_FLAGS_DONT_CARE;
- regNumber reg = DUMMY_INIT(REG_CORRUPT);
- regMaskTP needReg = destReg;
- regMaskTP addrReg;
-
- /* Figure out what instruction to generate */
-
- bool isArith;
- switch (oper)
- {
- case GT_ASG_AND:
- ins = INS_AND;
- isArith = false;
- break;
- case GT_ASG_OR:
- ins = INS_OR;
- isArith = false;
- break;
- case GT_ASG_XOR:
- ins = INS_XOR;
- isArith = false;
- break;
- case GT_ASG_ADD:
- ins = INS_add;
- isArith = true;
- break;
- case GT_ASG_SUB:
- ins = INS_sub;
- isArith = true;
- break;
- default:
- unreached();
- }
-
- bool ovfl = false;
-
- if (isArith)
- {
- // We only reach here for GT_ASG_SUB, GT_ASG_ADD.
-
- ovfl = tree->gtOverflow();
-
- // We can't use += with overflow if the value cannot be changed
- // in case of an overflow-exception which the "+" might cause
- noway_assert(!ovfl ||
- ((op1->gtOper == GT_LCL_VAR || op1->gtOper == GT_LCL_FLD) && !compiler->compCurBB->hasTryIndex()));
-
- /* Do not allow overflow instructions with refs/byrefs */
-
- noway_assert(!ovfl || !varTypeIsGC(treeType));
-
- // We disallow overflow and byte-ops here as it is too much trouble
- noway_assert(!ovfl || !varTypeIsByte(treeType));
-
- /* Is the second operand a constant? */
-
- if (op2->IsIntCnsFitsInI32())
- {
- int ival = (int)op2->gtIntCon.gtIconVal;
-
- /* What is the target of the assignment? */
-
- switch (op1->gtOper)
- {
- case GT_REG_VAR:
-
- REG_VAR4:
-
- reg = op1->gtRegVar.gtRegNum;
-
- /* No registers are needed for addressing */
-
- addrReg = RBM_NONE;
-#if !CPU_LOAD_STORE_ARCH
- INCDEC_REG:
-#endif
- /* We're adding a constant to a register */
-
- if (oper == GT_ASG_ADD)
- genIncRegBy(reg, ival, tree, treeType, ovfl);
- else if (ovfl && ((tree->gtFlags & GTF_UNSIGNED) ||
- ival == ((treeType == TYP_INT) ? INT32_MIN : SSIZE_T_MIN)) // -0x80000000 ==
- // 0x80000000.
- // Therefore we can't
- // use -ival.
- )
- /* For unsigned overflow, we have to use INS_sub to set
- the flags correctly */
- genDecRegBy(reg, ival, tree);
- else
- genIncRegBy(reg, -ival, tree, treeType, ovfl);
-
- break;
-
- case GT_LCL_VAR:
-
- /* Does the variable live in a register? */
-
- if (genMarkLclVar(op1))
- goto REG_VAR4;
-
- __fallthrough;
-
- default:
-
- /* Make the target addressable for load/store */
- addrReg = genMakeAddressable2(op1, needReg, RegSet::KEEP_REG, true, true);
-
-#if !CPU_LOAD_STORE_ARCH
- // For CPU_LOAD_STORE_ARCH, we always load from memory then store to memory
-
- /* For small types with overflow check, we need to
- sign/zero extend the result, so we need it in a reg */
-
- if (ovfl && genTypeSize(treeType) < sizeof(int))
-#endif // !CPU_LOAD_STORE_ARCH
- {
- // Load op1 into a reg
-
- reg = regSet.rsGrabReg(RBM_ALLINT & ~addrReg);
-
- inst_RV_TT(INS_mov, reg, op1);
-
- // Issue the add/sub and the overflow check
-
- inst_RV_IV(ins, reg, ival, emitActualTypeSize(treeType), flags);
- regTracker.rsTrackRegTrash(reg);
-
- if (ovfl)
- {
- genCheckOverflow(tree);
- }
-
- /* Store the (sign/zero extended) result back to
- the stack location of the variable */
-
- inst_TT_RV(ins_Store(op1->TypeGet()), op1, reg);
-
- break;
- }
-#if !CPU_LOAD_STORE_ARCH
- else
- {
- /* Add/subtract the new value into/from the target */
-
- if (op1->InReg())
- {
- reg = op1->gtRegNum;
- goto INCDEC_REG;
- }
-
- /* Special case: inc/dec (up to P3, or for small code, or blended code outside loops) */
- if (!ovfl && (ival == 1 || ival == -1) &&
- !compiler->optAvoidIncDec(compiler->compCurBB->getBBWeight(compiler)))
- {
- noway_assert(oper == GT_ASG_SUB || oper == GT_ASG_ADD);
- if (oper == GT_ASG_SUB)
- ival = -ival;
-
- ins = (ival > 0) ? INS_inc : INS_dec;
- inst_TT(ins, op1);
- }
- else
- {
- inst_TT_IV(ins, op1, ival);
- }
-
- if ((op1->gtOper == GT_LCL_VAR) && (!ovfl || treeType == TYP_INT))
- {
- if (tree->gtSetFlags())
- genFlagsEqualToVar(tree, op1->gtLclVarCommon.gtLclNum);
- }
-
- break;
- }
-#endif // !CPU_LOAD_STORE_ARCH
- } // end switch (op1->gtOper)
-
- genDoneAddressable(op1, addrReg, RegSet::KEEP_REG);
-
- genCodeForTreeSmpOpAsg_DONE_ASSG(tree, addrReg, reg, ovfl);
- return;
- } // end if (op2->IsIntCnsFitsInI32())
- } // end if (isArith)
-
- noway_assert(!varTypeIsGC(treeType) || ins == INS_sub || ins == INS_add);
-
- /* Is the target a register or local variable? */
-
- switch (op1->gtOper)
- {
- case GT_LCL_VAR:
-
- /* Does the target variable live in a register? */
-
- if (!genMarkLclVar(op1))
- break;
-
- __fallthrough;
-
- case GT_REG_VAR:
-
- /* Get hold of the target register */
-
- reg = op1->gtRegVar.gtRegNum;
-
- /* Make sure the target of the store is available */
-
- if (regSet.rsMaskUsed & genRegMask(reg))
- {
- regSet.rsSpillReg(reg);
- }
-
- /* Make the RHS addressable */
-
- addrReg = genMakeRvalueAddressable(op2, 0, RegSet::KEEP_REG, false);
-
- /* Compute the new value into the target register */
- CLANG_FORMAT_COMMENT_ANCHOR;
-
-#if CPU_HAS_BYTE_REGS
-
- // Fix 383833 X86 ILGEN
- regNumber reg2;
- if (op2->InReg())
- {
- reg2 = op2->gtRegNum;
- }
- else
- {
- reg2 = REG_STK;
- }
-
- // We can only generate a byte ADD,SUB,OR,AND operation when reg and reg2 are both BYTE registers
- // when op2 is in memory then reg2==REG_STK and we will need to force op2 into a register
- //
- if (varTypeIsByte(treeType) &&
- (((genRegMask(reg) & RBM_BYTE_REGS) == 0) || ((genRegMask(reg2) & RBM_BYTE_REGS) == 0)))
- {
- // We will force op2 into a register (via sign/zero extending load)
- // for the cases where op2 is in memory and thus could have
- // an unmapped page just beyond its location
- //
- if ((op2->OperIsIndir() || (op2->gtOper == GT_CLS_VAR)) && varTypeIsSmall(op2->TypeGet()))
- {
- genCodeForTree(op2, 0);
- assert(op2->InReg());
- }
-
- inst_RV_TT(ins, reg, op2, 0, EA_4BYTE, flags);
-
- bool canOmit = false;
-
- if (varTypeIsUnsigned(treeType))
- {
- // When op2 is a byte sized constant we can omit the zero extend instruction
- if ((op2->gtOper == GT_CNS_INT) && ((op2->gtIntCon.gtIconVal & 0xFF) == op2->gtIntCon.gtIconVal))
- {
- canOmit = true;
- }
- }
- else // treeType is signed
- {
- // When op2 is a positive 7-bit or smaller constant
- // we can omit the sign extension sequence.
- if ((op2->gtOper == GT_CNS_INT) && ((op2->gtIntCon.gtIconVal & 0x7F) == op2->gtIntCon.gtIconVal))
- {
- canOmit = true;
- }
- }
-
- if (!canOmit)
- {
- // If reg is a byte reg then we can use a movzx/movsx instruction
- //
- if ((genRegMask(reg) & RBM_BYTE_REGS) != 0)
- {
- instruction extendIns = ins_Move_Extend(treeType, true);
- inst_RV_RV(extendIns, reg, reg, treeType, emitTypeSize(treeType));
- }
- else // we can't encode a movzx/movsx instruction
- {
- if (varTypeIsUnsigned(treeType))
- {
- // otherwise, we must zero the upper 24 bits of 'reg'
- inst_RV_IV(INS_AND, reg, 0xFF, EA_4BYTE);
- }
- else // treeType is signed
- {
- // otherwise, we must sign extend the result in the non-byteable register 'reg'
- // We will shift the register left 24 bits, thus putting the sign-bit into the high bit
- // then we do an arithmetic shift back 24 bits which propagate the sign bit correctly.
- //
- inst_RV_SH(INS_SHIFT_LEFT_LOGICAL, EA_4BYTE, reg, 24);
- inst_RV_SH(INS_SHIFT_RIGHT_ARITHM, EA_4BYTE, reg, 24);
- }
- }
- }
- }
- else
-#endif // CPU_HAS_BYTE_REGS
- {
- inst_RV_TT(ins, reg, op2, 0, emitTypeSize(treeType), flags);
- }
-
- /* The zero flag is now equal to the register value */
-
- if (tree->gtSetFlags())
- genFlagsEqualToReg(tree, reg);
-
- /* Remember that we trashed the target */
-
- regTracker.rsTrackRegTrash(reg);
-
- /* Free up anything that was tied up by the RHS */
-
- genDoneAddressable(op2, addrReg, RegSet::KEEP_REG);
-
- genCodeForTreeSmpOpAsg_DONE_ASSG(tree, addrReg, reg, ovfl);
- return;
-
- default:
- break;
- } // end switch (op1->gtOper)
-
-#if !CPU_LOAD_STORE_ARCH
- /* Special case: "x ^= -1" is actually "not(x)" */
-
- if (oper == GT_ASG_XOR)
- {
- if (op2->gtOper == GT_CNS_INT && op2->gtIntCon.gtIconVal == -1)
- {
- addrReg = genMakeAddressable(op1, RBM_ALLINT, RegSet::KEEP_REG, true);
- inst_TT(INS_NOT, op1);
- genDoneAddressable(op1, addrReg, RegSet::KEEP_REG);
-
- genCodeForTreeSmpOpAsg_DONE_ASSG(tree, addrReg, tree->gtRegNum, ovfl);
- return;
- }
- }
-#endif // !CPU_LOAD_STORE_ARCH
-
- /* Setup target mask for op2 (byte-regs for small operands) */
-
- unsigned needMask;
- needMask = (varTypeIsByte(treeType)) ? RBM_BYTE_REGS : RBM_ALLINT;
-
- /* Is the second operand a constant? */
-
- if (op2->IsIntCnsFitsInI32())
- {
- int ival = (int)op2->gtIntCon.gtIconVal;
-
- /* Make the target addressable */
- addrReg = genMakeAddressable(op1, needReg, RegSet::FREE_REG, true);
-
- inst_TT_IV(ins, op1, ival, 0, emitTypeSize(treeType), flags);
-
- genDoneAddressable(op1, addrReg, RegSet::FREE_REG);
-
- genCodeForTreeSmpOpAsg_DONE_ASSG(tree, addrReg, tree->gtRegNum, ovfl);
- return;
- }
-
- /* Is the value or the address to be computed first? */
-
- if (tree->gtFlags & GTF_REVERSE_OPS)
- {
- /* Compute the new value into a register */
-
- genComputeReg(op2, needMask, RegSet::EXACT_REG, RegSet::KEEP_REG);
-
- /* Make the target addressable for load/store */
- addrReg = genMakeAddressable2(op1, 0, RegSet::KEEP_REG, true, true);
- regSet.rsLockUsedReg(addrReg);
-
-#if !CPU_LOAD_STORE_ARCH
- // For CPU_LOAD_STORE_ARCH, we always load from memory then store to memory
- /* For small types with overflow check, we need to
- sign/zero extend the result, so we need it in a reg */
-
- if (ovfl && genTypeSize(treeType) < sizeof(int))
-#endif // !CPU_LOAD_STORE_ARCH
- {
- reg = regSet.rsPickReg();
- regSet.rsLockReg(genRegMask(reg));
-
- noway_assert(genIsValidReg(reg));
-
- /* Generate "ldr reg, [var]" */
-
- inst_RV_TT(ins_Load(op1->TypeGet()), reg, op1);
-
- if (op1->gtOper == GT_LCL_VAR)
- regTracker.rsTrackRegLclVar(reg, op1->gtLclVar.gtLclNum);
- else
- regTracker.rsTrackRegTrash(reg);
-
- /* Make sure the new value is in a register */
-
- genRecoverReg(op2, 0, RegSet::KEEP_REG);
-
- /* Compute the new value */
-
- inst_RV_RV(ins, reg, op2->gtRegNum, treeType, emitTypeSize(treeType), flags);
-
- if (ovfl)
- genCheckOverflow(tree);
-
- /* Move the new value back to the variable */
- /* Generate "str reg, [var]" */
-
- inst_TT_RV(ins_Store(op1->TypeGet()), op1, reg);
- regSet.rsUnlockReg(genRegMask(reg));
-
- if (op1->gtOper == GT_LCL_VAR)
- regTracker.rsTrackRegLclVar(reg, op1->gtLclVarCommon.gtLclNum);
- }
-#if !CPU_LOAD_STORE_ARCH
- else
- {
- /* Make sure the new value is in a register */
-
- genRecoverReg(op2, 0, RegSet::KEEP_REG);
-
- /* Add the new value into the target */
-
- inst_TT_RV(ins, op1, op2->gtRegNum);
- }
-#endif // !CPU_LOAD_STORE_ARCH
- /* Free up anything that was tied up either side */
- regSet.rsUnlockUsedReg(addrReg);
- genDoneAddressable(op1, addrReg, RegSet::KEEP_REG);
- genReleaseReg(op2);
- }
- else
- {
- /* Make the target addressable */
-
- addrReg = genMakeAddressable2(op1, RBM_ALLINT & ~op2->gtRsvdRegs, RegSet::KEEP_REG, true, true);
-
- /* Compute the new value into a register */
-
- genComputeReg(op2, needMask, RegSet::EXACT_REG, RegSet::KEEP_REG);
- regSet.rsLockUsedReg(genRegMask(op2->gtRegNum));
-
- /* Make sure the target is still addressable */
-
- addrReg = genKeepAddressable(op1, addrReg);
- regSet.rsLockUsedReg(addrReg);
-
-#if !CPU_LOAD_STORE_ARCH
- // For CPU_LOAD_STORE_ARCH, we always load from memory then store to memory
-
- /* For small types with overflow check, we need to
- sign/zero extend the result, so we need it in a reg */
-
- if (ovfl && genTypeSize(treeType) < sizeof(int))
-#endif // !CPU_LOAD_STORE_ARCH
- {
- reg = regSet.rsPickReg();
-
- inst_RV_TT(INS_mov, reg, op1);
-
- inst_RV_RV(ins, reg, op2->gtRegNum, treeType, emitTypeSize(treeType), flags);
- regTracker.rsTrackRegTrash(reg);
-
- if (ovfl)
- genCheckOverflow(tree);
-
- inst_TT_RV(ins_Store(op1->TypeGet()), op1, reg);
-
- if (op1->gtOper == GT_LCL_VAR)
- regTracker.rsTrackRegLclVar(reg, op1->gtLclVar.gtLclNum);
- }
-#if !CPU_LOAD_STORE_ARCH
- else
- {
- /* Add the new value into the target */
-
- inst_TT_RV(ins, op1, op2->gtRegNum);
- }
-#endif
-
- /* Free up anything that was tied up either side */
- regSet.rsUnlockUsedReg(addrReg);
- genDoneAddressable(op1, addrReg, RegSet::KEEP_REG);
-
- regSet.rsUnlockUsedReg(genRegMask(op2->gtRegNum));
- genReleaseReg(op2);
- }
-
- genCodeForTreeSmpOpAsg_DONE_ASSG(tree, addrReg, reg, ovfl);
-}
-
-/*****************************************************************************
- *
- * Generate code for GT_UMOD.
- */
-
-void CodeGen::genCodeForUnsignedMod(GenTree* tree, regMaskTP destReg, regMaskTP bestReg)
-{
- assert(tree->OperGet() == GT_UMOD);
-
- GenTree* op1 = tree->gtOp.gtOp1;
- GenTree* op2 = tree->gtOp.gtOp2;
- const var_types treeType = tree->TypeGet();
- regMaskTP needReg = destReg;
- regNumber reg;
-
- /* Is this a division by an integer constant? */
-
- noway_assert(op2);
- if (compiler->fgIsUnsignedModOptimizable(op2))
- {
- /* Generate the operand into some register */
-
- genCompIntoFreeReg(op1, needReg, RegSet::FREE_REG);
- noway_assert(op1->InReg());
-
- reg = op1->gtRegNum;
-
- /* Generate the appropriate sequence */
- size_t ival = op2->gtIntCon.gtIconVal - 1;
- inst_RV_IV(INS_AND, reg, ival, emitActualTypeSize(treeType));
-
- /* The register is now trashed */
-
- regTracker.rsTrackRegTrash(reg);
-
- genCodeForTree_DONE(tree, reg);
- return;
- }
-
- genCodeForGeneralDivide(tree, destReg, bestReg);
-}
-
-/*****************************************************************************
- *
- * Generate code for GT_MOD.
- */
-
-void CodeGen::genCodeForSignedMod(GenTree* tree, regMaskTP destReg, regMaskTP bestReg)
-{
- assert(tree->OperGet() == GT_MOD);
-
- GenTree* op1 = tree->gtOp.gtOp1;
- GenTree* op2 = tree->gtOp.gtOp2;
- const var_types treeType = tree->TypeGet();
- regMaskTP needReg = destReg;
- regNumber reg;
-
- /* Is this a division by an integer constant? */
-
- noway_assert(op2);
- if (compiler->fgIsSignedModOptimizable(op2))
- {
- ssize_t ival = op2->gtIntCon.gtIconVal;
- BasicBlock* skip = genCreateTempLabel();
-
- /* Generate the operand into some register */
-
- genCompIntoFreeReg(op1, needReg, RegSet::FREE_REG);
- noway_assert(op1->InReg());
-
- reg = op1->gtRegNum;
-
- /* Generate the appropriate sequence */
-
- inst_RV_IV(INS_AND, reg, (int)(ival - 1) | 0x80000000, EA_4BYTE, INS_FLAGS_SET);
-
- /* The register is now trashed */
-
- regTracker.rsTrackRegTrash(reg);
-
- /* Check and branch for a postive value */
- emitJumpKind jmpGEL = genJumpKindForOper(GT_GE, CK_LOGICAL);
- inst_JMP(jmpGEL, skip);
-
- /* Generate the rest of the sequence and we're done */
-
- genIncRegBy(reg, -1, NULL, treeType);
- ival = -ival;
- if ((treeType == TYP_LONG) && ((int)ival != ival))
- {
- regNumber immReg = regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(reg));
- instGen_Set_Reg_To_Imm(EA_8BYTE, immReg, ival);
- inst_RV_RV(INS_OR, reg, immReg, TYP_LONG);
- }
- else
- {
- inst_RV_IV(INS_OR, reg, (int)ival, emitActualTypeSize(treeType));
- }
- genIncRegBy(reg, 1, NULL, treeType);
-
- /* Define the 'skip' label and we're done */
-
- genDefineTempLabel(skip);
-
- genCodeForTree_DONE(tree, reg);
- return;
- }
-
- genCodeForGeneralDivide(tree, destReg, bestReg);
-}
-
-/*****************************************************************************
- *
- * Generate code for GT_UDIV.
- */
-
-void CodeGen::genCodeForUnsignedDiv(GenTree* tree, regMaskTP destReg, regMaskTP bestReg)
-{
- assert(tree->OperGet() == GT_UDIV);
-
- GenTree* op1 = tree->gtOp.gtOp1;
- GenTree* op2 = tree->gtOp.gtOp2;
- const var_types treeType = tree->TypeGet();
- regMaskTP needReg = destReg;
- regNumber reg;
-
- /* Is this a division by an integer constant? */
-
- noway_assert(op2);
- if (compiler->fgIsUnsignedDivOptimizable(op2))
- {
- size_t ival = op2->gtIntCon.gtIconVal;
-
- /* Division by 1 must be handled elsewhere */
-
- noway_assert(ival != 1 || compiler->opts.MinOpts());
-
- /* Generate the operand into some register */
-
- genCompIntoFreeReg(op1, needReg, RegSet::FREE_REG);
- noway_assert(op1->InReg());
-
- reg = op1->gtRegNum;
-
- /* Generate "shr reg, log2(value)" */
-
- inst_RV_SH(INS_SHIFT_RIGHT_LOGICAL, emitTypeSize(treeType), reg, genLog2(ival));
-
- /* The register is now trashed */
-
- regTracker.rsTrackRegTrash(reg);
-
- genCodeForTree_DONE(tree, reg);
- return;
- }
-
- genCodeForGeneralDivide(tree, destReg, bestReg);
-}
-
-/*****************************************************************************
- *
- * Generate code for GT_DIV.
- */
-
-void CodeGen::genCodeForSignedDiv(GenTree* tree, regMaskTP destReg, regMaskTP bestReg)
-{
- assert(tree->OperGet() == GT_DIV);
-
- GenTree* op1 = tree->gtOp.gtOp1;
- GenTree* op2 = tree->gtOp.gtOp2;
- const var_types treeType = tree->TypeGet();
- regMaskTP needReg = destReg;
- regNumber reg;
-
- /* Is this a division by an integer constant? */
-
- noway_assert(op2);
- if (compiler->fgIsSignedDivOptimizable(op2))
- {
- ssize_t ival_s = op2->gtIntConCommon.IconValue();
- assert(ival_s > 0); // Postcondition of compiler->fgIsSignedDivOptimizable...
- size_t ival = static_cast<size_t>(ival_s);
-
- /* Division by 1 must be handled elsewhere */
-
- noway_assert(ival != 1);
-
- BasicBlock* onNegDivisee = genCreateTempLabel();
-
- /* Generate the operand into some register */
-
- genCompIntoFreeReg(op1, needReg, RegSet::FREE_REG);
- noway_assert(op1->InReg());
-
- reg = op1->gtRegNum;
-
- if (ival == 2)
- {
- /* Generate "sar reg, log2(value)" */
-
- inst_RV_SH(INS_SHIFT_RIGHT_ARITHM, emitTypeSize(treeType), reg, genLog2(ival), INS_FLAGS_SET);
-
- // Check and branch for a postive value, skipping the INS_ADDC instruction
- emitJumpKind jmpGEL = genJumpKindForOper(GT_GE, CK_LOGICAL);
- inst_JMP(jmpGEL, onNegDivisee);
-
- // Add the carry flag to 'reg'
- inst_RV_IV(INS_ADDC, reg, 0, emitActualTypeSize(treeType));
-
- /* Define the 'onNegDivisee' label and we're done */
-
- genDefineTempLabel(onNegDivisee);
-
- /* The register is now trashed */
-
- regTracker.rsTrackRegTrash(reg);
-
- /* The result is the same as the operand */
-
- reg = op1->gtRegNum;
- }
- else
- {
- /* Generate the following sequence */
- /*
- test reg, reg
- jns onNegDivisee
- add reg, ival-1
- onNegDivisee:
- sar reg, log2(ival)
- */
-
- instGen_Compare_Reg_To_Zero(emitTypeSize(treeType), reg);
-
- // Check and branch for a postive value, skipping the INS_add instruction
- emitJumpKind jmpGEL = genJumpKindForOper(GT_GE, CK_LOGICAL);
- inst_JMP(jmpGEL, onNegDivisee);
-
- inst_RV_IV(INS_add, reg, (int)ival - 1, emitActualTypeSize(treeType));
-
- /* Define the 'onNegDivisee' label and we're done */
-
- genDefineTempLabel(onNegDivisee);
-
- /* Generate "sar reg, log2(value)" */
-
- inst_RV_SH(INS_SHIFT_RIGHT_ARITHM, emitTypeSize(treeType), reg, genLog2(ival));
-
- /* The register is now trashed */
-
- regTracker.rsTrackRegTrash(reg);
-
- /* The result is the same as the operand */
-
- reg = op1->gtRegNum;
- }
-
- genCodeForTree_DONE(tree, reg);
- return;
- }
-
- genCodeForGeneralDivide(tree, destReg, bestReg);
-}
-
-/*****************************************************************************
- *
- * Generate code for a general divide. Handles the general case for GT_UMOD, GT_MOD, GT_UDIV, GT_DIV
- * (if op2 is not a power of 2 constant).
- */
-
-void CodeGen::genCodeForGeneralDivide(GenTree* tree, regMaskTP destReg, regMaskTP bestReg)
-{
- assert(tree->OperGet() == GT_UMOD || tree->OperGet() == GT_MOD || tree->OperGet() == GT_UDIV ||
- tree->OperGet() == GT_DIV);
-
- GenTree* op1 = tree->gtOp.gtOp1;
- GenTree* op2 = tree->gtOp.gtOp2;
- const var_types treeType = tree->TypeGet();
- regMaskTP needReg = destReg;
- regNumber reg;
- instruction ins;
- bool gotOp1;
- regMaskTP addrReg;
-
-#if USE_HELPERS_FOR_INT_DIV
- noway_assert(!"Unreachable: fgMorph should have transformed this into a JitHelper");
-#endif
-
-#if defined(_TARGET_XARCH_)
-
- /* Which operand are we supposed to evaluate first? */
-
- if (tree->gtFlags & GTF_REVERSE_OPS)
- {
- /* We'll evaluate 'op2' first */
-
- gotOp1 = false;
- destReg &= ~op1->gtRsvdRegs;
-
- /* Also if op1 is an enregistered LCL_VAR then exclude its register as well */
- if (op1->gtOper == GT_LCL_VAR)
- {
- unsigned varNum = op1->gtLclVarCommon.gtLclNum;
- noway_assert(varNum < compiler->lvaCount);
- LclVarDsc* varDsc = compiler->lvaTable + varNum;
- if (varDsc->lvRegister)
- {
- destReg &= ~genRegMask(varDsc->lvRegNum);
- }
- }
- }
- else
- {
- /* We'll evaluate 'op1' first */
-
- gotOp1 = true;
-
- regMaskTP op1Mask;
- if (RBM_EAX & op2->gtRsvdRegs)
- op1Mask = RBM_ALLINT & ~op2->gtRsvdRegs;
- else
- op1Mask = RBM_EAX; // EAX would be ideal
-
- /* Generate the dividend into EAX and hold on to it. freeOnly=true */
-
- genComputeReg(op1, op1Mask, RegSet::ANY_REG, RegSet::KEEP_REG, true);
- }
-
- /* We want to avoid using EAX or EDX for the second operand */
-
- destReg = regSet.rsMustExclude(destReg, RBM_EAX | RBM_EDX);
-
- /* Make the second operand addressable */
- op2 = genCodeForCommaTree(op2);
-
- /* Special case: if op2 is a local var we are done */
-
- if (op2->gtOper == GT_LCL_VAR || op2->gtOper == GT_LCL_FLD)
- {
- if (!op2->InReg())
- addrReg = genMakeRvalueAddressable(op2, destReg, RegSet::KEEP_REG, false);
- else
- addrReg = 0;
- }
- else
- {
- genComputeReg(op2, destReg, RegSet::ANY_REG, RegSet::KEEP_REG);
-
- noway_assert(op2->InReg());
- addrReg = genRegMask(op2->gtRegNum);
- }
-
- /* Make sure we have the dividend in EAX */
-
- if (gotOp1)
- {
- /* We've previously computed op1 into EAX */
-
- genRecoverReg(op1, RBM_EAX, RegSet::KEEP_REG);
- }
- else
- {
- /* Compute op1 into EAX and hold on to it */
-
- genComputeReg(op1, RBM_EAX, RegSet::EXACT_REG, RegSet::KEEP_REG, true);
- }
-
- noway_assert(op1->InReg());
- noway_assert(op1->gtRegNum == REG_EAX);
-
- /* We can now safely (we think) grab EDX */
-
- regSet.rsGrabReg(RBM_EDX);
- regSet.rsLockReg(RBM_EDX);
-
- /* Convert the integer in EAX into a un/signed long in EDX:EAX */
-
- const genTreeOps oper = tree->OperGet();
-
- if (oper == GT_UMOD || oper == GT_UDIV)
- instGen_Set_Reg_To_Zero(EA_PTRSIZE, REG_EDX);
- else
- instGen(INS_cdq);
-
- /* Make sure the divisor is still addressable */
-
- addrReg = genKeepAddressable(op2, addrReg, RBM_EAX);
-
- /* Perform the division */
-
- if (oper == GT_UMOD || oper == GT_UDIV)
- inst_TT(INS_UNSIGNED_DIVIDE, op2);
- else
- inst_TT(INS_SIGNED_DIVIDE, op2);
-
- /* Free up anything tied up by the divisor's address */
-
- genDoneAddressable(op2, addrReg, RegSet::KEEP_REG);
-
- /* Unlock and free EDX */
-
- regSet.rsUnlockReg(RBM_EDX);
-
- /* Free up op1 (which is in EAX) as well */
-
- genReleaseReg(op1);
-
- /* Both EAX and EDX are now trashed */
-
- regTracker.rsTrackRegTrash(REG_EAX);
- regTracker.rsTrackRegTrash(REG_EDX);
-
- /* Figure out which register the result is in */
-
- reg = (oper == GT_DIV || oper == GT_UDIV) ? REG_EAX : REG_EDX;
-
- /* Don't forget to mark the first operand as using EAX and EDX */
-
- op1->gtRegNum = reg;
-
- genCodeForTree_DONE(tree, reg);
-
-#elif defined(_TARGET_ARM_)
-
- /* Which operand are we supposed to evaluate first? */
-
- if (tree->gtFlags & GTF_REVERSE_OPS)
- {
- /* We'll evaluate 'op2' first */
-
- gotOp1 = false;
- destReg &= ~op1->gtRsvdRegs;
-
- /* Also if op1 is an enregistered LCL_VAR then exclude its register as well */
- if (op1->gtOper == GT_LCL_VAR)
- {
- unsigned varNum = op1->gtLclVarCommon.gtLclNum;
- noway_assert(varNum < compiler->lvaCount);
- LclVarDsc* varDsc = compiler->lvaTable + varNum;
- if (varDsc->lvRegister)
- {
- destReg &= ~genRegMask(varDsc->lvRegNum);
- }
- }
- }
- else
- {
- /* We'll evaluate 'op1' first */
-
- gotOp1 = true;
- regMaskTP op1Mask = RBM_ALLINT & ~op2->gtRsvdRegs;
-
- /* Generate the dividend into a register and hold on to it. */
-
- genComputeReg(op1, op1Mask, RegSet::ANY_REG, RegSet::KEEP_REG, true);
- }
-
- /* Evaluate the second operand into a register and hold onto it. */
-
- genComputeReg(op2, destReg, RegSet::ANY_REG, RegSet::KEEP_REG);
-
- noway_assert(op2->InReg());
- addrReg = genRegMask(op2->gtRegNum);
-
- if (gotOp1)
- {
- // Recover op1 if spilled
- genRecoverReg(op1, RBM_NONE, RegSet::KEEP_REG);
- }
- else
- {
- /* Compute op1 into any register and hold on to it */
- genComputeReg(op1, RBM_ALLINT, RegSet::ANY_REG, RegSet::KEEP_REG, true);
- }
- noway_assert(op1->InReg());
-
- reg = regSet.rsPickReg(needReg, bestReg);
-
- // Perform the divison
-
- const genTreeOps oper = tree->OperGet();
-
- if (oper == GT_UMOD || oper == GT_UDIV)
- ins = INS_udiv;
- else
- ins = INS_sdiv;
-
- getEmitter()->emitIns_R_R_R(ins, EA_4BYTE, reg, op1->gtRegNum, op2->gtRegNum);
-
- if (oper == GT_UMOD || oper == GT_MOD)
- {
- getEmitter()->emitIns_R_R_R(INS_mul, EA_4BYTE, reg, op2->gtRegNum, reg);
- getEmitter()->emitIns_R_R_R(INS_sub, EA_4BYTE, reg, op1->gtRegNum, reg);
- }
- /* Free up op1 and op2 */
- genReleaseReg(op1);
- genReleaseReg(op2);
-
- genCodeForTree_DONE(tree, reg);
-
-#else
-#error "Unknown _TARGET_"
-#endif
-}
-
-/*****************************************************************************
- *
- * Generate code for an assignment shift (x <op>= ). Handles GT_ASG_LSH, GT_ASG_RSH, GT_ASG_RSZ.
- */
-
-void CodeGen::genCodeForAsgShift(GenTree* tree, regMaskTP destReg, regMaskTP bestReg)
-{
- assert(tree->OperGet() == GT_ASG_LSH || tree->OperGet() == GT_ASG_RSH || tree->OperGet() == GT_ASG_RSZ);
-
- const genTreeOps oper = tree->OperGet();
- GenTree* op1 = tree->gtOp.gtOp1;
- GenTree* op2 = tree->gtOp.gtOp2;
- const var_types treeType = tree->TypeGet();
- insFlags flags = tree->gtSetFlags() ? INS_FLAGS_SET : INS_FLAGS_DONT_CARE;
- regMaskTP needReg = destReg;
- regNumber reg;
- instruction ins;
- regMaskTP addrReg;
-
- switch (oper)
- {
- case GT_ASG_LSH:
- ins = INS_SHIFT_LEFT_LOGICAL;
- break;
- case GT_ASG_RSH:
- ins = INS_SHIFT_RIGHT_ARITHM;
- break;
- case GT_ASG_RSZ:
- ins = INS_SHIFT_RIGHT_LOGICAL;
- break;
- default:
- unreached();
- }
-
- noway_assert(!varTypeIsGC(treeType));
- noway_assert(op2);
-
- /* Shifts by a constant amount are easier */
-
- if (op2->IsCnsIntOrI())
- {
- /* Make the target addressable */
-
- addrReg = genMakeAddressable(op1, needReg, RegSet::KEEP_REG, true);
-
- /* Are we shifting a register left by 1 bit? */
-
- if ((oper == GT_ASG_LSH) && (op2->gtIntCon.gtIconVal == 1) && op1->InReg())
- {
- /* The target lives in a register */
-
- reg = op1->gtRegNum;
-
- /* "add reg, reg" is cheaper than "shl reg, 1" */
-
- inst_RV_RV(INS_add, reg, reg, treeType, emitActualTypeSize(treeType), flags);
- }
- else
- {
-#if CPU_LOAD_STORE_ARCH
- if (!op1->InReg())
- {
- regSet.rsLockUsedReg(addrReg);
-
- // Load op1 into a reg
-
- reg = regSet.rsPickReg(RBM_ALLINT);
-
- inst_RV_TT(INS_mov, reg, op1);
-
- // Issue the shift
-
- inst_RV_IV(ins, reg, (int)op2->gtIntCon.gtIconVal, emitActualTypeSize(treeType), flags);
- regTracker.rsTrackRegTrash(reg);
-
- /* Store the (sign/zero extended) result back to the stack location of the variable */
-
- inst_TT_RV(ins_Store(op1->TypeGet()), op1, reg);
-
- regSet.rsUnlockUsedReg(addrReg);
- }
- else
-#endif // CPU_LOAD_STORE_ARCH
- {
- /* Shift by the constant value */
-
- inst_TT_SH(ins, op1, (int)op2->gtIntCon.gtIconVal);
- }
- }
-
- /* If the target is a register, it has a new value */
-
- if (op1->InReg())
- regTracker.rsTrackRegTrash(op1->gtRegNum);
-
- genDoneAddressable(op1, addrReg, RegSet::KEEP_REG);
-
- /* The zero flag is now equal to the target value */
- /* X86: But only if the shift count is != 0 */
-
- if (op2->gtIntCon.gtIconVal != 0)
- {
- if (tree->gtSetFlags())
- {
- if (op1->gtOper == GT_LCL_VAR)
- {
- genFlagsEqualToVar(tree, op1->gtLclVarCommon.gtLclNum);
- }
- else if (op1->gtOper == GT_REG_VAR)
- {
- genFlagsEqualToReg(tree, op1->gtRegNum);
- }
- }
- }
- else
- {
- // It is possible for the shift count to equal 0 with valid
- // IL, and not be optimized away, in the case where the node
- // is of a small type. The sequence of instructions looks like
- // ldsfld, shr, stsfld and executed on a char field. This will
- // never happen with code produced by our compilers, because the
- // compilers will insert a conv.u2 before the stsfld (which will
- // lead us down a different codepath in the JIT and optimize away
- // the shift by zero). This case is not worth optimizing and we
- // will just make sure to generate correct code for it.
-
- genFlagsEqualToNone();
- }
- }
- else
- {
- regMaskTP op2Regs = RBM_NONE;
- if (REG_SHIFT != REG_NA)
- op2Regs = RBM_SHIFT;
-
- regMaskTP tempRegs;
-
- if (tree->gtFlags & GTF_REVERSE_OPS)
- {
- tempRegs = regSet.rsMustExclude(op2Regs, op1->gtRsvdRegs);
- genCodeForTree(op2, tempRegs);
- regSet.rsMarkRegUsed(op2);
-
- tempRegs = regSet.rsMustExclude(RBM_ALLINT, genRegMask(op2->gtRegNum));
- addrReg = genMakeAddressable(op1, tempRegs, RegSet::KEEP_REG, true);
-
- genRecoverReg(op2, op2Regs, RegSet::KEEP_REG);
- }
- else
- {
- /* Make the target addressable avoiding op2->RsvdRegs [and RBM_SHIFT] */
- regMaskTP excludeMask = op2->gtRsvdRegs;
- if (REG_SHIFT != REG_NA)
- excludeMask |= RBM_SHIFT;
-
- tempRegs = regSet.rsMustExclude(RBM_ALLINT, excludeMask);
- addrReg = genMakeAddressable(op1, tempRegs, RegSet::KEEP_REG, true);
-
- /* Load the shift count into the necessary register */
- genComputeReg(op2, op2Regs, RegSet::EXACT_REG, RegSet::KEEP_REG);
- }
-
- /* Make sure the address registers are still here */
- addrReg = genKeepAddressable(op1, addrReg, op2Regs);
-
-#ifdef _TARGET_XARCH_
- /* Perform the shift */
- inst_TT_CL(ins, op1);
-#else
- /* Perform the shift */
- noway_assert(op2->InReg());
- op2Regs = genRegMask(op2->gtRegNum);
-
- regSet.rsLockUsedReg(addrReg | op2Regs);
- inst_TT_RV(ins, op1, op2->gtRegNum, 0, emitTypeSize(treeType), flags);
- regSet.rsUnlockUsedReg(addrReg | op2Regs);
-#endif
- /* Free the address registers */
- genDoneAddressable(op1, addrReg, RegSet::KEEP_REG);
-
- /* If the value is in a register, it's now trash */
-
- if (op1->InReg())
- regTracker.rsTrackRegTrash(op1->gtRegNum);
-
- /* Release the op2 [RBM_SHIFT] operand */
-
- genReleaseReg(op2);
- }
-
- genCodeForTreeSmpOpAsg_DONE_ASSG(tree, addrReg, /* unused for ovfl=false */ REG_NA, /* ovfl */ false);
-}
-
-/*****************************************************************************
- *
- * Generate code for a shift. Handles GT_LSH, GT_RSH, GT_RSZ.
- */
-
-void CodeGen::genCodeForShift(GenTree* tree, regMaskTP destReg, regMaskTP bestReg)
-{
- assert(tree->OperIsShift());
-
- const genTreeOps oper = tree->OperGet();
- GenTree* op1 = tree->gtOp.gtOp1;
- GenTree* op2 = tree->gtOp.gtOp2;
- const var_types treeType = tree->TypeGet();
- insFlags flags = tree->gtSetFlags() ? INS_FLAGS_SET : INS_FLAGS_DONT_CARE;
- regMaskTP needReg = destReg;
- regNumber reg;
- instruction ins;
-
- switch (oper)
- {
- case GT_LSH:
- ins = INS_SHIFT_LEFT_LOGICAL;
- break;
- case GT_RSH:
- ins = INS_SHIFT_RIGHT_ARITHM;
- break;
- case GT_RSZ:
- ins = INS_SHIFT_RIGHT_LOGICAL;
- break;
- default:
- unreached();
- }
-
- /* Is the shift count constant? */
- noway_assert(op2);
- if (op2->IsIntCnsFitsInI32())
- {
- // TODO: Check to see if we could generate a LEA instead!
-
- /* Compute the left operand into any free register */
-
- genCompIntoFreeReg(op1, needReg, RegSet::KEEP_REG);
-
- noway_assert(op1->InReg());
- reg = op1->gtRegNum;
-
- /* Are we shifting left by 1 bit? (or 2 bits for fast code) */
-
- // On ARM, until proven otherwise by performance numbers, just do the shift.
- // It's no bigger than add (16 bits for low registers, 32 bits for high registers).
- // It's smaller than two "add reg, reg".
-
- CLANG_FORMAT_COMMENT_ANCHOR;
-
-#ifndef _TARGET_ARM_
- if (oper == GT_LSH)
- {
- emitAttr size = emitActualTypeSize(treeType);
- if (op2->gtIntConCommon.IconValue() == 1)
- {
- /* "add reg, reg" is smaller and faster than "shl reg, 1" */
- inst_RV_RV(INS_add, reg, reg, treeType, size, flags);
- }
- else if ((op2->gtIntConCommon.IconValue() == 2) && (compiler->compCodeOpt() == Compiler::FAST_CODE))
- {
- /* two "add reg, reg" instructions are faster than "shl reg, 2" */
- inst_RV_RV(INS_add, reg, reg, treeType);
- inst_RV_RV(INS_add, reg, reg, treeType, size, flags);
- }
- else
- goto DO_SHIFT_BY_CNS;
- }
- else
-#endif // _TARGET_ARM_
- {
-#ifndef _TARGET_ARM_
- DO_SHIFT_BY_CNS:
-#endif // _TARGET_ARM_
- // If we are shifting 'reg' by zero bits and do not need the flags to be set
- // then we can just skip emitting the instruction as 'reg' is already correct.
- //
- if ((op2->gtIntConCommon.IconValue() != 0) || tree->gtSetFlags())
- {
- /* Generate the appropriate shift instruction */
- inst_RV_SH(ins, emitTypeSize(treeType), reg, (int)op2->gtIntConCommon.IconValue(), flags);
- }
- }
- }
- else
- {
- /* Calculate a useful register mask for computing op1 */
- needReg = regSet.rsNarrowHint(regSet.rsRegMaskFree(), needReg);
- regMaskTP op2RegMask;
-#ifdef _TARGET_XARCH_
- op2RegMask = RBM_ECX;
-#else
- op2RegMask = RBM_NONE;
-#endif
- needReg = regSet.rsMustExclude(needReg, op2RegMask);
-
- regMaskTP tempRegs;
-
- /* Which operand are we supposed to evaluate first? */
- if (tree->gtFlags & GTF_REVERSE_OPS)
- {
- /* Load the shift count [into ECX on XARCH] */
- tempRegs = regSet.rsMustExclude(op2RegMask, op1->gtRsvdRegs);
- genComputeReg(op2, tempRegs, RegSet::EXACT_REG, RegSet::KEEP_REG, false);
-
- /* We must not target the register that is holding op2 */
- needReg = regSet.rsMustExclude(needReg, genRegMask(op2->gtRegNum));
-
- /* Now evaluate 'op1' into a free register */
- genComputeReg(op1, needReg, RegSet::ANY_REG, RegSet::KEEP_REG, true);
-
- /* Recover op2 into ECX */
- genRecoverReg(op2, op2RegMask, RegSet::KEEP_REG);
- }
- else
- {
- /* Compute op1 into a register, trying to avoid op2->rsvdRegs and ECX */
- tempRegs = regSet.rsMustExclude(needReg, op2->gtRsvdRegs);
- genComputeReg(op1, tempRegs, RegSet::ANY_REG, RegSet::KEEP_REG, true);
-
- /* Load the shift count [into ECX on XARCH] */
- genComputeReg(op2, op2RegMask, RegSet::EXACT_REG, RegSet::KEEP_REG, false);
- }
-
- noway_assert(op2->InReg());
-#ifdef _TARGET_XARCH_
- noway_assert(genRegMask(op2->gtRegNum) == op2RegMask);
-#endif
- // Check for the case of op1 being spilled during the evaluation of op2
- if (op1->gtFlags & GTF_SPILLED)
- {
- // The register has been spilled -- reload it to any register except ECX
- regSet.rsLockUsedReg(op2RegMask);
- regSet.rsUnspillReg(op1, 0, RegSet::KEEP_REG);
- regSet.rsUnlockUsedReg(op2RegMask);
- }
-
- noway_assert(op1->InReg());
- reg = op1->gtRegNum;
-
-#ifdef _TARGET_ARM_
- /* Perform the shift */
- getEmitter()->emitIns_R_R(ins, EA_4BYTE, reg, op2->gtRegNum, flags);
-#else
- /* Perform the shift */
- inst_RV_CL(ins, reg);
-#endif
- genReleaseReg(op2);
- }
-
- noway_assert(op1->InReg());
- noway_assert(reg == op1->gtRegNum);
-
- /* The register is now trashed */
- genReleaseReg(op1);
- regTracker.rsTrackRegTrash(reg);
-
- genCodeForTree_DONE(tree, reg);
-}
-
-/*****************************************************************************
- *
- * Generate code for a top-level relational operator (not one that is part of a GT_JTRUE tree).
- * Handles GT_EQ, GT_NE, GT_LT, GT_LE, GT_GE, GT_GT.
- */
-
-void CodeGen::genCodeForRelop(GenTree* tree, regMaskTP destReg, regMaskTP bestReg)
-{
- assert(tree->OperGet() == GT_EQ || tree->OperGet() == GT_NE || tree->OperGet() == GT_LT ||
- tree->OperGet() == GT_LE || tree->OperGet() == GT_GE || tree->OperGet() == GT_GT);
-
- const genTreeOps oper = tree->OperGet();
- GenTree* op1 = tree->gtOp.gtOp1;
- const var_types treeType = tree->TypeGet();
- regMaskTP needReg = destReg;
- regNumber reg;
-
- // Longs and float comparisons are converted to "?:"
- noway_assert(!compiler->fgMorphRelopToQmark(op1));
-
- // Check if we can use the currently set flags. Else set them
-
- emitJumpKind jumpKind = genCondSetFlags(tree);
-
- // Grab a register to materialize the bool value into
-
- bestReg = regSet.rsRegMaskCanGrab() & RBM_BYTE_REGS;
-
- // Check that the predictor did the right job
- noway_assert(bestReg);
-
- // If needReg is in bestReg then use it
- if (needReg & bestReg)
- reg = regSet.rsGrabReg(needReg & bestReg);
- else
- reg = regSet.rsGrabReg(bestReg);
-
-#if defined(_TARGET_ARM_)
-
- // Generate:
- // jump-if-true L_true
- // mov reg, 0
- // jmp L_end
- // L_true:
- // mov reg, 1
- // L_end:
-
- BasicBlock* L_true;
- BasicBlock* L_end;
-
- L_true = genCreateTempLabel();
- L_end = genCreateTempLabel();
-
- inst_JMP(jumpKind, L_true);
- getEmitter()->emitIns_R_I(INS_mov, EA_4BYTE, reg, 0); // Executes when the cond is false
- inst_JMP(EJ_jmp, L_end);
- genDefineTempLabel(L_true);
- getEmitter()->emitIns_R_I(INS_mov, EA_4BYTE, reg, 1); // Executes when the cond is true
- genDefineTempLabel(L_end);
-
- regTracker.rsTrackRegTrash(reg);
-
-#elif defined(_TARGET_XARCH_)
- regMaskTP regs = genRegMask(reg);
- noway_assert(regs & RBM_BYTE_REGS);
-
- // Set (lower byte of) reg according to the flags
-
- /* Look for the special case where just want to transfer the carry bit */
-
- if (jumpKind == EJ_jb)
- {
- inst_RV_RV(INS_SUBC, reg, reg);
- inst_RV(INS_NEG, reg, TYP_INT);
- regTracker.rsTrackRegTrash(reg);
- }
- else if (jumpKind == EJ_jae)
- {
- inst_RV_RV(INS_SUBC, reg, reg);
- genIncRegBy(reg, 1, tree, TYP_INT);
- regTracker.rsTrackRegTrash(reg);
- }
- else
- {
- inst_SET(jumpKind, reg);
-
- regTracker.rsTrackRegTrash(reg);
-
- if (treeType == TYP_INT)
- {
- // Set the higher bytes to 0
- inst_RV_RV(ins_Move_Extend(TYP_UBYTE, true), reg, reg, TYP_UBYTE, emitTypeSize(TYP_UBYTE));
- }
- else
- {
- noway_assert(treeType == TYP_BYTE);
- }
- }
-#else
- NYI("TARGET");
-#endif // _TARGET_XXX
-
- genCodeForTree_DONE(tree, reg);
-}
-
-//------------------------------------------------------------------------
-// genCodeForCopyObj: Generate code for a CopyObj node
-//
-// Arguments:
-// tree - The CopyObj node we are going to generate code for.
-// destReg - The register mask for register(s), if any, that will be defined.
-//
-// Return Value:
-// None
-
-void CodeGen::genCodeForCopyObj(GenTree* tree, regMaskTP destReg)
-{
- // If the value class doesn't have any fields that are GC refs or
- // the target isn't on the GC-heap, we can merge it with CPBLK.
- // GC fields cannot be copied directly, instead we will
- // need to use a jit-helper for that.
- assert(tree->gtOper == GT_ASG);
- assert(tree->gtOp.gtOp1->gtOper == GT_OBJ);
-
- GenTreeObj* cpObjOp = tree->gtOp.gtOp1->AsObj();
- assert(cpObjOp->HasGCPtr());
-
-#ifdef _TARGET_ARM_
- if (cpObjOp->IsVolatile())
- {
- // Emit a memory barrier instruction before the CopyBlk
- instGen_MemoryBarrier();
- }
-#endif
- assert(tree->gtOp.gtOp2->OperIsIndir());
- GenTree* srcObj = tree->gtOp.gtOp2->AsIndir()->Addr();
- GenTree* dstObj = cpObjOp->Addr();
-
- noway_assert(dstObj->gtType == TYP_BYREF || dstObj->gtType == TYP_I_IMPL);
-
-#ifdef DEBUG
- CORINFO_CLASS_HANDLE clsHnd = (CORINFO_CLASS_HANDLE)cpObjOp->gtClass;
- size_t debugBlkSize = roundUp(compiler->info.compCompHnd->getClassSize(clsHnd), TARGET_POINTER_SIZE);
-
- // Since we round up, we are not handling the case where we have a non-pointer sized struct with GC pointers.
- // The EE currently does not allow this. Let's assert it just to be safe.
- noway_assert(compiler->info.compCompHnd->getClassSize(clsHnd) == debugBlkSize);
-#endif
-
- size_t blkSize = cpObjOp->gtSlots * TARGET_POINTER_SIZE;
- unsigned slots = cpObjOp->gtSlots;
- BYTE* gcPtrs = cpObjOp->gtGcPtrs;
- unsigned gcPtrCount = cpObjOp->gtGcPtrCount;
- assert(blkSize == cpObjOp->gtBlkSize);
-
- GenTree* treeFirst;
- GenTree* treeSecond;
- regNumber regFirst, regSecond;
-
- // Check what order the object-ptrs have to be evaluated in ?
-
- if (tree->gtFlags & GTF_REVERSE_OPS)
- {
- treeFirst = srcObj;
- treeSecond = dstObj;
-#if CPU_USES_BLOCK_MOVE
- regFirst = REG_ESI;
- regSecond = REG_EDI;
-#else
- regFirst = REG_ARG_1;
- regSecond = REG_ARG_0;
-#endif
- }
- else
- {
- treeFirst = dstObj;
- treeSecond = srcObj;
-#if CPU_USES_BLOCK_MOVE
- regFirst = REG_EDI;
- regSecond = REG_ESI;
-#else
- regFirst = REG_ARG_0;
- regSecond = REG_ARG_1;
-#endif
- }
-
- bool dstIsOnStack = (dstObj->gtOper == GT_ADDR && (dstObj->gtFlags & GTF_ADDR_ONSTACK));
- bool srcIsOnStack = (srcObj->gtOper == GT_ADDR && (srcObj->gtFlags & GTF_ADDR_ONSTACK));
- emitAttr srcType = (varTypeIsGC(srcObj) && !srcIsOnStack) ? EA_BYREF : EA_PTRSIZE;
- emitAttr dstType = (varTypeIsGC(dstObj) && !dstIsOnStack) ? EA_BYREF : EA_PTRSIZE;
-
-#if CPU_USES_BLOCK_MOVE
- // Materialize the trees in the order desired
-
- genComputeReg(treeFirst, genRegMask(regFirst), RegSet::EXACT_REG, RegSet::KEEP_REG, true);
- genComputeReg(treeSecond, genRegMask(regSecond), RegSet::EXACT_REG, RegSet::KEEP_REG, true);
- genRecoverReg(treeFirst, genRegMask(regFirst), RegSet::KEEP_REG);
-
- // Grab ECX because it will be trashed by the helper
- //
- regSet.rsGrabReg(RBM_ECX);
-
- while (blkSize >= TARGET_POINTER_SIZE)
- {
- if (*gcPtrs++ == TYPE_GC_NONE || dstIsOnStack)
- {
- // Note that we can use movsd even if it is a GC pointer being transfered
- // because the value is not cached anywhere. If we did this in two moves,
- // we would have to make certain we passed the appropriate GC info on to
- // the emitter.
- instGen(INS_movsp);
- }
- else
- {
- // This helper will act like a MOVSD
- // -- inputs EDI and ESI are byrefs
- // -- including incrementing of ESI and EDI by 4
- // -- helper will trash ECX
- //
- regMaskTP argRegs = genRegMask(regFirst) | genRegMask(regSecond);
- regSet.rsLockUsedReg(argRegs);
- genEmitHelperCall(CORINFO_HELP_ASSIGN_BYREF,
- 0, // argSize
- EA_PTRSIZE); // retSize
- regSet.rsUnlockUsedReg(argRegs);
- }
-
- blkSize -= TARGET_POINTER_SIZE;
- }
-
- // "movsd/movsq" as well as CPX_BYREF_ASG modify all three registers
-
- regTracker.rsTrackRegTrash(REG_EDI);
- regTracker.rsTrackRegTrash(REG_ESI);
- regTracker.rsTrackRegTrash(REG_ECX);
-
- gcInfo.gcMarkRegSetNpt(RBM_ESI | RBM_EDI);
-
- /* The emitter won't record CORINFO_HELP_ASSIGN_BYREF in the GC tables as
- it is a emitNoGChelper. However, we have to let the emitter know that
- the GC liveness has changed. We do this by creating a new label.
- */
-
- noway_assert(emitter::emitNoGChelper(CORINFO_HELP_ASSIGN_BYREF));
-
- genDefineTempLabel(&dummyBB);
-
-#else // !CPU_USES_BLOCK_MOVE
-
-#ifndef _TARGET_ARM_
-// Currently only the ARM implementation is provided
-#error "COPYBLK for non-ARM && non-CPU_USES_BLOCK_MOVE"
-#endif
-
- // Materialize the trees in the order desired
- bool helperUsed;
- regNumber regDst;
- regNumber regSrc;
- regNumber regTemp;
-
- if ((gcPtrCount > 0) && !dstIsOnStack)
- {
- genComputeReg(treeFirst, genRegMask(regFirst), RegSet::EXACT_REG, RegSet::KEEP_REG, true);
- genComputeReg(treeSecond, genRegMask(regSecond), RegSet::EXACT_REG, RegSet::KEEP_REG, true);
- genRecoverReg(treeFirst, genRegMask(regFirst), RegSet::KEEP_REG);
-
- /* The helper is a Asm-routine that will trash R2,R3 and LR */
- {
- /* Spill any callee-saved registers which are being used */
- regMaskTP spillRegs = RBM_CALLEE_TRASH_NOGC & regSet.rsMaskUsed;
-
- if (spillRegs)
- {
- regSet.rsSpillRegs(spillRegs);
- }
- }
-
- // Grab R2 (aka REG_TMP_1) because it will be trashed by the helper
- // We will also use it as the temp register for our load/store sequences
- //
- assert(REG_R2 == REG_TMP_1);
- regTemp = regSet.rsGrabReg(RBM_R2);
- helperUsed = true;
- }
- else
- {
- genCompIntoFreeReg(treeFirst, (RBM_ALLINT & ~treeSecond->gtRsvdRegs), RegSet::KEEP_REG);
- genCompIntoFreeReg(treeSecond, RBM_ALLINT, RegSet::KEEP_REG);
- genRecoverReg(treeFirst, RBM_ALLINT, RegSet::KEEP_REG);
-
- // Grab any temp register to use for our load/store sequences
- //
- regTemp = regSet.rsGrabReg(RBM_ALLINT);
- helperUsed = false;
- }
- assert(dstObj->InReg());
- assert(srcObj->InReg());
-
- regDst = dstObj->gtRegNum;
- regSrc = srcObj->gtRegNum;
-
- assert(regDst != regTemp);
- assert(regSrc != regTemp);
-
- instruction loadIns = ins_Load(TYP_I_IMPL); // INS_ldr
- instruction storeIns = ins_Store(TYP_I_IMPL); // INS_str
-
- size_t offset = 0;
- while (blkSize >= TARGET_POINTER_SIZE)
- {
- CorInfoGCType gcType;
- CorInfoGCType gcTypeNext = TYPE_GC_NONE;
- var_types type = TYP_I_IMPL;
-
- gcType = (CorInfoGCType)(*gcPtrs++);
- if (blkSize > TARGET_POINTER_SIZE)
- gcTypeNext = (CorInfoGCType)(*gcPtrs);
-
- if (gcType == TYPE_GC_REF)
- type = TYP_REF;
- else if (gcType == TYPE_GC_BYREF)
- type = TYP_BYREF;
-
- if (helperUsed)
- {
- assert(regDst == REG_ARG_0);
- assert(regSrc == REG_ARG_1);
- assert(regTemp == REG_R2);
- }
-
- blkSize -= TARGET_POINTER_SIZE;
-
- emitAttr opSize = emitTypeSize(type);
-
- if (!helperUsed || (gcType == TYPE_GC_NONE))
- {
- getEmitter()->emitIns_R_R_I(loadIns, opSize, regTemp, regSrc, offset);
- getEmitter()->emitIns_R_R_I(storeIns, opSize, regTemp, regDst, offset);
- offset += TARGET_POINTER_SIZE;
-
- if ((helperUsed && (gcTypeNext != TYPE_GC_NONE)) || ((offset >= 128) && (blkSize > 0)))
- {
- getEmitter()->emitIns_R_I(INS_add, srcType, regSrc, offset);
- getEmitter()->emitIns_R_I(INS_add, dstType, regDst, offset);
- offset = 0;
- }
- }
- else
- {
- assert(offset == 0);
-
- // The helper will act like this:
- // -- inputs R0 and R1 are byrefs
- // -- helper will perform copy from *R1 into *R0
- // -- helper will perform post increment of R0 and R1 by 4
- // -- helper will trash R2
- // -- helper will trash R3
- // -- calling the helper implicitly trashes LR
- //
- assert(helperUsed);
- regMaskTP argRegs = genRegMask(regFirst) | genRegMask(regSecond);
- regSet.rsLockUsedReg(argRegs);
- genEmitHelperCall(CORINFO_HELP_ASSIGN_BYREF,
- 0, // argSize
- EA_PTRSIZE); // retSize
-
- regSet.rsUnlockUsedReg(argRegs);
- regTracker.rsTrackRegMaskTrash(RBM_CALLEE_TRASH_NOGC);
- }
- }
-
- regTracker.rsTrackRegTrash(regDst);
- regTracker.rsTrackRegTrash(regSrc);
- regTracker.rsTrackRegTrash(regTemp);
-
- gcInfo.gcMarkRegSetNpt(genRegMask(regDst) | genRegMask(regSrc));
-
- /* The emitter won't record CORINFO_HELP_ASSIGN_BYREF in the GC tables as
- it is a emitNoGChelper. However, we have to let the emitter know that
- the GC liveness has changed. We do this by creating a new label.
- */
-
- noway_assert(emitter::emitNoGChelper(CORINFO_HELP_ASSIGN_BYREF));
-
- genDefineTempLabel(&dummyBB);
-
-#endif // !CPU_USES_BLOCK_MOVE
-
- assert(blkSize == 0);
-
- genReleaseReg(dstObj);
- genReleaseReg(srcObj);
-
- genCodeForTree_DONE(tree, REG_NA);
-
-#ifdef _TARGET_ARM_
- if (cpObjOp->IsVolatile())
- {
- // Emit a memory barrier instruction after the CopyBlk
- instGen_MemoryBarrier();
- }
-#endif
-}
-
-//------------------------------------------------------------------------
-// genCodeForBlkOp: Generate code for a block copy or init operation
-//
-// Arguments:
-// tree - The block assignment
-// destReg - The expected destination register
-//
-void CodeGen::genCodeForBlkOp(GenTree* tree, regMaskTP destReg)
-{
- genTreeOps oper = tree->OperGet();
- GenTree* dest = tree->gtOp.gtOp1;
- GenTree* src = tree->gtGetOp2();
- regMaskTP needReg = destReg;
- regMaskTP regs = regSet.rsMaskUsed;
- GenTree* opsPtr[3];
- regMaskTP regsPtr[3];
- GenTree* destPtr;
- GenTree* srcPtrOrVal;
-
- noway_assert(tree->OperIsBlkOp());
-
- bool isCopyBlk = false;
- bool isInitBlk = false;
- bool hasGCpointer = false;
- unsigned blockSize = dest->AsBlk()->gtBlkSize;
- GenTree* sizeNode = nullptr;
- bool sizeIsConst = true;
- if (dest->gtOper == GT_DYN_BLK)
- {
- sizeNode = dest->AsDynBlk()->gtDynamicSize;
- sizeIsConst = false;
- }
-
- if (tree->OperIsCopyBlkOp())
- {
- isCopyBlk = true;
- if (dest->gtOper == GT_OBJ)
- {
- if (dest->AsObj()->gtGcPtrCount != 0)
- {
- genCodeForCopyObj(tree, destReg);
- return;
- }
- }
- }
- else
- {
- isInitBlk = true;
- }
-
- // Ensure that we have an address in the CopyBlk case.
- if (isCopyBlk)
- {
- // TODO-1stClassStructs: Allow a lclVar here.
- assert(src->OperIsIndir());
- srcPtrOrVal = src->AsIndir()->Addr();
- }
- else
- {
- srcPtrOrVal = src;
- }
-
-#ifdef _TARGET_ARM_
- if (dest->AsBlk()->IsVolatile())
- {
- // Emit a memory barrier instruction before the InitBlk/CopyBlk
- instGen_MemoryBarrier();
- }
-#endif
- {
- destPtr = dest->AsBlk()->Addr();
- noway_assert(destPtr->TypeGet() == TYP_BYREF || varTypeIsIntegral(destPtr->TypeGet()));
- noway_assert(
- (isCopyBlk && (srcPtrOrVal->TypeGet() == TYP_BYREF || varTypeIsIntegral(srcPtrOrVal->TypeGet()))) ||
- (isInitBlk && varTypeIsIntegral(srcPtrOrVal->TypeGet())));
-
- noway_assert(destPtr && srcPtrOrVal);
-
-#if CPU_USES_BLOCK_MOVE
- regs = isInitBlk ? RBM_EAX : RBM_ESI; // What is the needReg for Val/Src
-
- /* Some special code for block moves/inits for constant sizes */
-
- //
- // Is this a fixed size COPYBLK?
- // or a fixed size INITBLK with a constant init value?
- //
- if ((sizeIsConst) && (isCopyBlk || (srcPtrOrVal->IsCnsIntOrI())))
- {
- size_t length = blockSize;
- size_t initVal = 0;
- instruction ins_P, ins_PR, ins_B;
-
- if (isInitBlk)
- {
- ins_P = INS_stosp;
- ins_PR = INS_r_stosp;
- ins_B = INS_stosb;
-
- /* Properly extend the init constant from a U1 to a U4 */
- initVal = 0xFF & ((unsigned)srcPtrOrVal->gtIntCon.gtIconVal);
-
- /* If it is a non-zero value we have to replicate */
- /* the byte value four times to form the DWORD */
- /* Then we change this new value into the tree-node */
-
- if (initVal)
- {
- initVal = initVal | (initVal << 8) | (initVal << 16) | (initVal << 24);
-#ifdef _TARGET_64BIT_
- if (length > 4)
- {
- initVal = initVal | (initVal << 32);
- srcPtrOrVal->gtType = TYP_LONG;
- }
- else
- {
- srcPtrOrVal->gtType = TYP_INT;
- }
-#endif // _TARGET_64BIT_
- }
- srcPtrOrVal->gtIntCon.gtIconVal = initVal;
- }
- else
- {
- ins_P = INS_movsp;
- ins_PR = INS_r_movsp;
- ins_B = INS_movsb;
- }
-
- // Determine if we will be using SSE2
- unsigned movqLenMin = 8;
- unsigned movqLenMax = 24;
-
- bool bWillUseSSE2 = false;
- bool bWillUseOnlySSE2 = false;
- bool bNeedEvaluateCnst = true; // If we only use SSE2, we will just load the constant there.
-
-#ifdef _TARGET_64BIT_
-
-// Until we get SSE2 instructions that move 16 bytes at a time instead of just 8
-// there is no point in wasting space on the bigger instructions
-
-#else // !_TARGET_64BIT_
-
- if (compiler->opts.compCanUseSSE2)
- {
- unsigned curBBweight = compiler->compCurBB->getBBWeight(compiler);
-
- /* Adjust for BB weight */
- if (curBBweight == BB_ZERO_WEIGHT)
- {
- // Don't bother with this optimization in
- // rarely run blocks
- movqLenMax = movqLenMin = 0;
- }
- else if (curBBweight < BB_UNITY_WEIGHT)
- {
- // Be less aggressive when we are inside a conditional
- movqLenMax = 16;
- }
- else if (curBBweight >= (BB_LOOP_WEIGHT * BB_UNITY_WEIGHT) / 2)
- {
- // Be more aggressive when we are inside a loop
- movqLenMax = 48;
- }
-
- if ((compiler->compCodeOpt() == Compiler::FAST_CODE) || isInitBlk)
- {
- // Be more aggressive when optimizing for speed
- // InitBlk uses fewer instructions
- movqLenMax += 16;
- }
-
- if (compiler->compCodeOpt() != Compiler::SMALL_CODE && length >= movqLenMin && length <= movqLenMax)
- {
- bWillUseSSE2 = true;
-
- if ((length % 8) == 0)
- {
- bWillUseOnlySSE2 = true;
- if (isInitBlk && (initVal == 0))
- {
- bNeedEvaluateCnst = false;
- noway_assert((srcPtrOrVal->OperGet() == GT_CNS_INT));
- }
- }
- }
- }
-
-#endif // !_TARGET_64BIT_
-
- const bool bWillTrashRegSrc = (isCopyBlk && !bWillUseOnlySSE2);
- /* Evaluate dest and src/val */
-
- if (tree->gtFlags & GTF_REVERSE_OPS)
- {
- if (bNeedEvaluateCnst)
- {
- genComputeReg(srcPtrOrVal, regs, RegSet::EXACT_REG, RegSet::KEEP_REG, bWillTrashRegSrc);
- }
- genComputeReg(destPtr, RBM_EDI, RegSet::EXACT_REG, RegSet::KEEP_REG, !bWillUseOnlySSE2);
- if (bNeedEvaluateCnst)
- {
- genRecoverReg(srcPtrOrVal, regs, RegSet::KEEP_REG);
- }
- }
- else
- {
- genComputeReg(destPtr, RBM_EDI, RegSet::EXACT_REG, RegSet::KEEP_REG, !bWillUseOnlySSE2);
- if (bNeedEvaluateCnst)
- {
- genComputeReg(srcPtrOrVal, regs, RegSet::EXACT_REG, RegSet::KEEP_REG, bWillTrashRegSrc);
- }
- genRecoverReg(destPtr, RBM_EDI, RegSet::KEEP_REG);
- }
-
- bool bTrashedESI = false;
- bool bTrashedEDI = false;
-
- if (bWillUseSSE2)
- {
- int blkDisp = 0;
- regNumber xmmReg = REG_XMM0;
-
- if (isInitBlk)
- {
- if (initVal)
- {
- getEmitter()->emitIns_R_R(INS_mov_i2xmm, EA_4BYTE, xmmReg, REG_EAX);
- getEmitter()->emitIns_R_R(INS_punpckldq, EA_4BYTE, xmmReg, xmmReg);
- }
- else
- {
- getEmitter()->emitIns_R_R(INS_xorps, EA_8BYTE, xmmReg, xmmReg);
- }
- }
-
- JITLOG_THIS(compiler, (LL_INFO100, "Using XMM instructions for %3d byte %s while compiling %s\n",
- length, isInitBlk ? "initblk" : "copyblk", compiler->info.compFullName));
-
- while (length > 7)
- {
- if (isInitBlk)
- {
- getEmitter()->emitIns_AR_R(INS_movq, EA_8BYTE, xmmReg, REG_EDI, blkDisp);
- }
- else
- {
- getEmitter()->emitIns_R_AR(INS_movq, EA_8BYTE, xmmReg, REG_ESI, blkDisp);
- getEmitter()->emitIns_AR_R(INS_movq, EA_8BYTE, xmmReg, REG_EDI, blkDisp);
- }
- blkDisp += 8;
- length -= 8;
- }
-
- if (length > 0)
- {
- noway_assert(bNeedEvaluateCnst);
- noway_assert(!bWillUseOnlySSE2);
-
- if (isCopyBlk)
- {
- inst_RV_IV(INS_add, REG_ESI, blkDisp, emitActualTypeSize(srcPtrOrVal->TypeGet()));
- bTrashedESI = true;
- }
-
- inst_RV_IV(INS_add, REG_EDI, blkDisp, emitActualTypeSize(destPtr->TypeGet()));
- bTrashedEDI = true;
-
- if (length >= REGSIZE_BYTES)
- {
- instGen(ins_P);
- length -= REGSIZE_BYTES;
- }
- }
- }
- else if (compiler->compCodeOpt() == Compiler::SMALL_CODE)
- {
- /* For small code, we can only use ins_DR to generate fast
- and small code. We also can't use "rep movsb" because
- we may not atomically reading and writing the DWORD */
-
- noway_assert(bNeedEvaluateCnst);
-
- goto USE_DR;
- }
- else if (length <= 4 * REGSIZE_BYTES)
- {
- noway_assert(bNeedEvaluateCnst);
-
- while (length >= REGSIZE_BYTES)
- {
- instGen(ins_P);
- length -= REGSIZE_BYTES;
- }
-
- bTrashedEDI = true;
- if (isCopyBlk)
- bTrashedESI = true;
- }
- else
- {
- USE_DR:
- noway_assert(bNeedEvaluateCnst);
-
- /* set ECX to length/REGSIZE_BYTES (in pointer-sized words) */
- genSetRegToIcon(REG_ECX, length / REGSIZE_BYTES, TYP_I_IMPL);
-
- length &= (REGSIZE_BYTES - 1);
-
- instGen(ins_PR);
-
- regTracker.rsTrackRegTrash(REG_ECX);
-
- bTrashedEDI = true;
- if (isCopyBlk)
- bTrashedESI = true;
- }
-
- /* Now take care of the remainder */
- CLANG_FORMAT_COMMENT_ANCHOR;
-
-#ifdef _TARGET_64BIT_
- if (length > 4)
- {
- noway_assert(bNeedEvaluateCnst);
- noway_assert(length < 8);
-
- instGen((isInitBlk) ? INS_stosd : INS_movsd);
- length -= 4;
-
- bTrashedEDI = true;
- if (isCopyBlk)
- bTrashedESI = true;
- }
-
-#endif // _TARGET_64BIT_
-
- if (length)
- {
- noway_assert(bNeedEvaluateCnst);
-
- while (length--)
- {
- instGen(ins_B);
- }
-
- bTrashedEDI = true;
- if (isCopyBlk)
- bTrashedESI = true;
- }
-
- noway_assert(bTrashedEDI == !bWillUseOnlySSE2);
- if (bTrashedEDI)
- regTracker.rsTrackRegTrash(REG_EDI);
- if (bTrashedESI)
- regTracker.rsTrackRegTrash(REG_ESI);
- // else No need to trash EAX as it wasnt destroyed by the "rep stos"
-
- genReleaseReg(destPtr);
- if (bNeedEvaluateCnst)
- genReleaseReg(srcPtrOrVal);
- }
- else
- {
- //
- // This a variable-sized COPYBLK/INITBLK,
- // or a fixed size INITBLK with a variable init value,
- //
-
- // What order should the Dest, Val/Src, and Size be calculated
-
- compiler->fgOrderBlockOps(tree, RBM_EDI, regs, RBM_ECX, opsPtr, regsPtr); // OUT arguments
-
- noway_assert((isInitBlk && (regs == RBM_EAX)) || (isCopyBlk && (regs == RBM_ESI)));
- genComputeReg(opsPtr[0], regsPtr[0], RegSet::EXACT_REG, RegSet::KEEP_REG, (regsPtr[0] != RBM_EAX));
- genComputeReg(opsPtr[1], regsPtr[1], RegSet::EXACT_REG, RegSet::KEEP_REG, (regsPtr[1] != RBM_EAX));
- if (opsPtr[2] != nullptr)
- {
- genComputeReg(opsPtr[2], regsPtr[2], RegSet::EXACT_REG, RegSet::KEEP_REG, (regsPtr[2] != RBM_EAX));
- }
- genRecoverReg(opsPtr[0], regsPtr[0], RegSet::KEEP_REG);
- genRecoverReg(opsPtr[1], regsPtr[1], RegSet::KEEP_REG);
-
- noway_assert((destPtr->InReg()) && // Dest
- (destPtr->gtRegNum == REG_EDI));
-
- noway_assert((srcPtrOrVal->InReg()) && // Val/Src
- (genRegMask(srcPtrOrVal->gtRegNum) == regs));
-
- if (sizeIsConst)
- {
- inst_RV_IV(INS_mov, REG_ECX, blockSize, EA_PTRSIZE);
- }
- else
- {
- noway_assert((sizeNode->InReg()) && // Size
- (sizeNode->gtRegNum == REG_ECX));
- }
-
- if (isInitBlk)
- instGen(INS_r_stosb);
- else
- instGen(INS_r_movsb);
-
- regTracker.rsTrackRegTrash(REG_EDI);
- regTracker.rsTrackRegTrash(REG_ECX);
-
- if (isCopyBlk)
- regTracker.rsTrackRegTrash(REG_ESI);
- // else No need to trash EAX as it wasnt destroyed by the "rep stos"
-
- genReleaseReg(opsPtr[0]);
- genReleaseReg(opsPtr[1]);
- if (opsPtr[2] != nullptr)
- {
- genReleaseReg(opsPtr[2]);
- }
- }
-
-#else // !CPU_USES_BLOCK_MOVE
-
-#ifndef _TARGET_ARM_
-// Currently only the ARM implementation is provided
-#error "COPYBLK/INITBLK non-ARM && non-CPU_USES_BLOCK_MOVE"
-#endif
- //
- // Is this a fixed size COPYBLK?
- // or a fixed size INITBLK with a constant init value?
- //
- if (sizeIsConst && (isCopyBlk || (srcPtrOrVal->OperGet() == GT_CNS_INT)))
- {
- GenTree* dstOp = destPtr;
- GenTree* srcOp = srcPtrOrVal;
- unsigned length = blockSize;
- unsigned fullStoreCount = length / TARGET_POINTER_SIZE;
- unsigned initVal = 0;
- bool useLoop = false;
-
- if (isInitBlk)
- {
- /* Properly extend the init constant from a U1 to a U4 */
- initVal = 0xFF & ((unsigned)srcOp->gtIntCon.gtIconVal);
-
- /* If it is a non-zero value we have to replicate */
- /* the byte value four times to form the DWORD */
- /* Then we store this new value into the tree-node */
-
- if (initVal != 0)
- {
- initVal = initVal | (initVal << 8) | (initVal << 16) | (initVal << 24);
- srcPtrOrVal->gtIntCon.gtIconVal = initVal;
- }
- }
-
- // Will we be using a loop to implement this INITBLK/COPYBLK?
- if ((isCopyBlk && (fullStoreCount >= 8)) || (isInitBlk && (fullStoreCount >= 16)))
- {
- useLoop = true;
- }
-
- regMaskTP usedRegs;
- regNumber regDst;
- regNumber regSrc;
- regNumber regTemp;
-
- /* Evaluate dest and src/val */
-
- if (tree->gtFlags & GTF_REVERSE_OPS)
- {
- genComputeReg(srcOp, (needReg & ~dstOp->gtRsvdRegs), RegSet::ANY_REG, RegSet::KEEP_REG, useLoop);
- assert(srcOp->InReg());
-
- genComputeReg(dstOp, needReg, RegSet::ANY_REG, RegSet::KEEP_REG, useLoop);
- assert(dstOp->InReg());
- regDst = dstOp->gtRegNum;
-
- genRecoverReg(srcOp, needReg, RegSet::KEEP_REG);
- regSrc = srcOp->gtRegNum;
- }
- else
- {
- genComputeReg(dstOp, (needReg & ~srcOp->gtRsvdRegs), RegSet::ANY_REG, RegSet::KEEP_REG, useLoop);
- assert(dstOp->InReg());
-
- genComputeReg(srcOp, needReg, RegSet::ANY_REG, RegSet::KEEP_REG, useLoop);
- assert(srcOp->InReg());
- regSrc = srcOp->gtRegNum;
-
- genRecoverReg(dstOp, needReg, RegSet::KEEP_REG);
- regDst = dstOp->gtRegNum;
- }
- assert(dstOp->InReg());
- assert(srcOp->InReg());
-
- regDst = dstOp->gtRegNum;
- regSrc = srcOp->gtRegNum;
- usedRegs = (genRegMask(regSrc) | genRegMask(regDst));
- bool dstIsOnStack = (dstOp->gtOper == GT_ADDR && (dstOp->gtFlags & GTF_ADDR_ONSTACK));
- emitAttr dstType = (varTypeIsGC(dstOp) && !dstIsOnStack) ? EA_BYREF : EA_PTRSIZE;
- emitAttr srcType;
-
- if (isCopyBlk)
- {
- // Prefer a low register,but avoid one of the ones we've already grabbed
- regTemp = regSet.rsGrabReg(regSet.rsNarrowHint(regSet.rsRegMaskCanGrab() & ~usedRegs, RBM_LOW_REGS));
- usedRegs |= genRegMask(regTemp);
- bool srcIsOnStack = (srcOp->gtOper == GT_ADDR && (srcOp->gtFlags & GTF_ADDR_ONSTACK));
- srcType = (varTypeIsGC(srcOp) && !srcIsOnStack) ? EA_BYREF : EA_PTRSIZE;
- }
- else
- {
- regTemp = REG_STK;
- srcType = EA_PTRSIZE;
- }
-
- instruction loadIns = ins_Load(TYP_I_IMPL); // INS_ldr
- instruction storeIns = ins_Store(TYP_I_IMPL); // INS_str
-
- int finalOffset;
-
- // Can we emit a small number of ldr/str instructions to implement this INITBLK/COPYBLK?
- if (!useLoop)
- {
- for (unsigned i = 0; i < fullStoreCount; i++)
- {
- if (isCopyBlk)
- {
- getEmitter()->emitIns_R_R_I(loadIns, EA_4BYTE, regTemp, regSrc, i * TARGET_POINTER_SIZE);
- getEmitter()->emitIns_R_R_I(storeIns, EA_4BYTE, regTemp, regDst, i * TARGET_POINTER_SIZE);
- gcInfo.gcMarkRegSetNpt(genRegMask(regTemp));
- regTracker.rsTrackRegTrash(regTemp);
- }
- else
- {
- getEmitter()->emitIns_R_R_I(storeIns, EA_4BYTE, regSrc, regDst, i * TARGET_POINTER_SIZE);
- }
- }
-
- finalOffset = fullStoreCount * TARGET_POINTER_SIZE;
- length -= finalOffset;
- }
- else // We will use a loop to implement this INITBLK/COPYBLK
- {
- unsigned pairStoreLoopCount = fullStoreCount / 2;
-
- // We need a second temp register for CopyBlk
- regNumber regTemp2 = REG_STK;
- if (isCopyBlk)
- {
- // Prefer a low register, but avoid one of the ones we've already grabbed
- regTemp2 =
- regSet.rsGrabReg(regSet.rsNarrowHint(regSet.rsRegMaskCanGrab() & ~usedRegs, RBM_LOW_REGS));
- usedRegs |= genRegMask(regTemp2);
- }
-
- // Pick and initialize the loop counter register
- regNumber regLoopIndex;
- regLoopIndex =
- regSet.rsGrabReg(regSet.rsNarrowHint(regSet.rsRegMaskCanGrab() & ~usedRegs, RBM_LOW_REGS));
- genSetRegToIcon(regLoopIndex, pairStoreLoopCount, TYP_INT);
-
- // Create and define the Basic Block for the loop top
- BasicBlock* loopTopBlock = genCreateTempLabel();
- genDefineTempLabel(loopTopBlock);
-
- // The loop body
- if (isCopyBlk)
- {
- getEmitter()->emitIns_R_R_I(loadIns, EA_4BYTE, regTemp, regSrc, 0);
- getEmitter()->emitIns_R_R_I(loadIns, EA_4BYTE, regTemp2, regSrc, TARGET_POINTER_SIZE);
- getEmitter()->emitIns_R_R_I(storeIns, EA_4BYTE, regTemp, regDst, 0);
- getEmitter()->emitIns_R_R_I(storeIns, EA_4BYTE, regTemp2, regDst, TARGET_POINTER_SIZE);
- getEmitter()->emitIns_R_I(INS_add, srcType, regSrc, 2 * TARGET_POINTER_SIZE);
- gcInfo.gcMarkRegSetNpt(genRegMask(regTemp));
- gcInfo.gcMarkRegSetNpt(genRegMask(regTemp2));
- regTracker.rsTrackRegTrash(regSrc);
- regTracker.rsTrackRegTrash(regTemp);
- regTracker.rsTrackRegTrash(regTemp2);
- }
- else // isInitBlk
- {
- getEmitter()->emitIns_R_R_I(storeIns, EA_4BYTE, regSrc, regDst, 0);
- getEmitter()->emitIns_R_R_I(storeIns, EA_4BYTE, regSrc, regDst, TARGET_POINTER_SIZE);
- }
-
- getEmitter()->emitIns_R_I(INS_add, dstType, regDst, 2 * TARGET_POINTER_SIZE);
- regTracker.rsTrackRegTrash(regDst);
- getEmitter()->emitIns_R_I(INS_sub, EA_4BYTE, regLoopIndex, 1, INS_FLAGS_SET);
- emitJumpKind jmpGTS = genJumpKindForOper(GT_GT, CK_SIGNED);
- inst_JMP(jmpGTS, loopTopBlock);
-
- regTracker.rsTrackRegIntCns(regLoopIndex, 0);
-
- length -= (pairStoreLoopCount * (2 * TARGET_POINTER_SIZE));
-
- if (length & TARGET_POINTER_SIZE)
- {
- if (isCopyBlk)
- {
- getEmitter()->emitIns_R_R_I(loadIns, EA_4BYTE, regTemp, regSrc, 0);
- getEmitter()->emitIns_R_R_I(storeIns, EA_4BYTE, regTemp, regDst, 0);
- }
- else
- {
- getEmitter()->emitIns_R_R_I(storeIns, EA_4BYTE, regSrc, regDst, 0);
- }
- finalOffset = TARGET_POINTER_SIZE;
- length -= TARGET_POINTER_SIZE;
- }
- else
- {
- finalOffset = 0;
- }
- }
-
- if (length & sizeof(short))
- {
- loadIns = ins_Load(TYP_USHORT); // INS_ldrh
- storeIns = ins_Store(TYP_USHORT); // INS_strh
-
- if (isCopyBlk)
- {
- getEmitter()->emitIns_R_R_I(loadIns, EA_2BYTE, regTemp, regSrc, finalOffset);
- getEmitter()->emitIns_R_R_I(storeIns, EA_2BYTE, regTemp, regDst, finalOffset);
- gcInfo.gcMarkRegSetNpt(genRegMask(regTemp));
- regTracker.rsTrackRegTrash(regTemp);
- }
- else
- {
- getEmitter()->emitIns_R_R_I(storeIns, EA_2BYTE, regSrc, regDst, finalOffset);
- }
- length -= sizeof(short);
- finalOffset += sizeof(short);
- }
-
- if (length & sizeof(char))
- {
- loadIns = ins_Load(TYP_UBYTE); // INS_ldrb
- storeIns = ins_Store(TYP_UBYTE); // INS_strb
-
- if (isCopyBlk)
- {
- getEmitter()->emitIns_R_R_I(loadIns, EA_1BYTE, regTemp, regSrc, finalOffset);
- getEmitter()->emitIns_R_R_I(storeIns, EA_1BYTE, regTemp, regDst, finalOffset);
- gcInfo.gcMarkRegSetNpt(genRegMask(regTemp));
- regTracker.rsTrackRegTrash(regTemp);
- }
- else
- {
- getEmitter()->emitIns_R_R_I(storeIns, EA_1BYTE, regSrc, regDst, finalOffset);
- }
- length -= sizeof(char);
- }
- assert(length == 0);
-
- genReleaseReg(dstOp);
- genReleaseReg(srcOp);
- }
- else
- {
- //
- // This a variable-sized COPYBLK/INITBLK,
- // or a fixed size INITBLK with a variable init value,
- //
-
- // What order should the Dest, Val/Src, and Size be calculated
-
- regMaskTP regsToLock = RBM_ARG_0 | RBM_ARG_1 | RBM_ARG_2;
-
- compiler->fgOrderBlockOps(tree, RBM_ARG_0, RBM_ARG_1, RBM_ARG_2, opsPtr, regsPtr); // OUT arguments
-
- genComputeReg(opsPtr[0], regsPtr[0], RegSet::EXACT_REG, RegSet::KEEP_REG);
- genComputeReg(opsPtr[1], regsPtr[1], RegSet::EXACT_REG, RegSet::KEEP_REG);
- if (opsPtr[2] != nullptr)
- {
- genComputeReg(opsPtr[2], regsPtr[2], RegSet::EXACT_REG, RegSet::KEEP_REG);
- }
- else
- {
- regSet.rsLockReg(RBM_ARG_2);
- regsToLock &= ~RBM_ARG_2;
- }
- genRecoverReg(opsPtr[0], regsPtr[0], RegSet::KEEP_REG);
- genRecoverReg(opsPtr[1], regsPtr[1], RegSet::KEEP_REG);
-
- noway_assert((destPtr->InReg()) && // Dest
- (destPtr->gtRegNum == REG_ARG_0));
-
- noway_assert((srcPtrOrVal->InReg()) && // Val/Src
- (srcPtrOrVal->gtRegNum == REG_ARG_1));
-
- if (sizeIsConst)
- {
- inst_RV_IV(INS_mov, REG_ARG_2, blockSize, EA_PTRSIZE);
- }
- else
- {
- noway_assert((sizeNode->InReg()) && // Size
- (sizeNode->gtRegNum == REG_ARG_2));
- }
-
- regSet.rsLockUsedReg(regsToLock);
-
- genEmitHelperCall(isCopyBlk ? CORINFO_HELP_MEMCPY
- /* GT_INITBLK */
- : CORINFO_HELP_MEMSET,
- 0, EA_UNKNOWN);
-
- regTracker.rsTrackRegMaskTrash(RBM_CALLEE_TRASH);
-
- regSet.rsUnlockUsedReg(regsToLock);
- genReleaseReg(opsPtr[0]);
- genReleaseReg(opsPtr[1]);
- if (opsPtr[2] != nullptr)
- {
- genReleaseReg(opsPtr[2]);
- }
- else
- {
- regSet.rsUnlockReg(RBM_ARG_2);
- }
- }
-
- if (isCopyBlk && dest->AsBlk()->IsVolatile())
- {
- // Emit a memory barrier instruction after the CopyBlk
- instGen_MemoryBarrier();
- }
-#endif // !CPU_USES_BLOCK_MOVE
- }
-}
-BasicBlock dummyBB;
-
-#ifdef _PREFAST_
-#pragma warning(push)
-#pragma warning(disable : 21000) // Suppress PREFast warning about overly large function
-#endif
-void CodeGen::genCodeForTreeSmpOp(GenTree* tree, regMaskTP destReg, regMaskTP bestReg)
-{
- const genTreeOps oper = tree->OperGet();
- const var_types treeType = tree->TypeGet();
- GenTree* op1 = tree->gtOp.gtOp1;
- GenTree* op2 = tree->gtGetOp2IfPresent();
- regNumber reg = DUMMY_INIT(REG_CORRUPT);
- regMaskTP regs = regSet.rsMaskUsed;
- regMaskTP needReg = destReg;
- insFlags flags = tree->gtSetFlags() ? INS_FLAGS_SET : INS_FLAGS_DONT_CARE;
- emitAttr size;
- instruction ins;
- regMaskTP addrReg;
- GenTree* opsPtr[3];
- regMaskTP regsPtr[3];
-
-#ifdef DEBUG
- addrReg = 0xDEADCAFE;
-#endif
-
- noway_assert(tree->OperKind() & GTK_SMPOP);
-
- switch (oper)
- {
- case GT_ASG:
- if (tree->OperIsBlkOp() && op1->gtOper != GT_LCL_VAR)
- {
- genCodeForBlkOp(tree, destReg);
- }
- else
- {
- genCodeForTreeSmpOpAsg(tree);
- }
- return;
-
- case GT_ASG_LSH:
- case GT_ASG_RSH:
- case GT_ASG_RSZ:
- genCodeForAsgShift(tree, destReg, bestReg);
- return;
-
- case GT_ASG_AND:
- case GT_ASG_OR:
- case GT_ASG_XOR:
- case GT_ASG_ADD:
- case GT_ASG_SUB:
- genCodeForTreeSmpBinArithLogAsgOp(tree, destReg, bestReg);
- return;
-
- case GT_CHS:
- addrReg = genMakeAddressable(op1, 0, RegSet::KEEP_REG, true);
-#ifdef _TARGET_XARCH_
- // Note that the specialCase here occurs when the treeType specifies a byte sized operation
- // and we decided to enregister the op1 LclVar in a non-byteable register (ESI or EDI)
- //
- bool specialCase;
- specialCase = false;
- if (op1->gtOper == GT_REG_VAR)
- {
- /* Get hold of the target register */
-
- reg = op1->gtRegVar.gtRegNum;
- if (varTypeIsByte(treeType) && !(genRegMask(reg) & RBM_BYTE_REGS))
- {
- regNumber byteReg = regSet.rsGrabReg(RBM_BYTE_REGS);
-
- inst_RV_RV(INS_mov, byteReg, reg);
- regTracker.rsTrackRegTrash(byteReg);
-
- inst_RV(INS_NEG, byteReg, treeType, emitTypeSize(treeType));
- var_types op1Type = op1->TypeGet();
- instruction wideningIns = ins_Move_Extend(op1Type, true);
- inst_RV_RV(wideningIns, reg, byteReg, op1Type, emitTypeSize(op1Type));
- regTracker.rsTrackRegTrash(reg);
- specialCase = true;
- }
- }
-
- if (!specialCase)
- {
- inst_TT(INS_NEG, op1, 0, 0, emitTypeSize(treeType));
- }
-#else // not _TARGET_XARCH_
- if (op1->InReg())
- {
- inst_TT_IV(INS_NEG, op1, 0, 0, emitTypeSize(treeType), flags);
- }
- else
- {
- // Fix 388382 ARM JitStress WP7
- var_types op1Type = op1->TypeGet();
- regNumber reg = regSet.rsPickFreeReg();
- inst_RV_TT(ins_Load(op1Type), reg, op1, 0, emitTypeSize(op1Type));
- regTracker.rsTrackRegTrash(reg);
- inst_RV_IV(INS_NEG, reg, 0, emitTypeSize(treeType), flags);
- inst_TT_RV(ins_Store(op1Type), op1, reg, 0, emitTypeSize(op1Type));
- }
-#endif
- if (op1->InReg())
- regTracker.rsTrackRegTrash(op1->gtRegNum);
- genDoneAddressable(op1, addrReg, RegSet::KEEP_REG);
-
- genCodeForTreeSmpOpAsg_DONE_ASSG(tree, addrReg, tree->gtRegNum, /* ovfl */ false);
- return;
-
- case GT_AND:
- case GT_OR:
- case GT_XOR:
- case GT_ADD:
- case GT_SUB:
- case GT_MUL:
- genCodeForTreeSmpBinArithLogOp(tree, destReg, bestReg);
- return;
-
- case GT_UMOD:
- genCodeForUnsignedMod(tree, destReg, bestReg);
- return;
-
- case GT_MOD:
- genCodeForSignedMod(tree, destReg, bestReg);
- return;
-
- case GT_UDIV:
- genCodeForUnsignedDiv(tree, destReg, bestReg);
- return;
-
- case GT_DIV:
- genCodeForSignedDiv(tree, destReg, bestReg);
- return;
-
- case GT_LSH:
- case GT_RSH:
- case GT_RSZ:
- genCodeForShift(tree, destReg, bestReg);
- return;
-
- case GT_NEG:
- case GT_NOT:
-
- /* Generate the operand into some register */
-
- genCompIntoFreeReg(op1, needReg, RegSet::FREE_REG);
- noway_assert(op1->InReg());
-
- reg = op1->gtRegNum;
-
- /* Negate/reverse the value in the register */
-
- inst_RV((oper == GT_NEG) ? INS_NEG : INS_NOT, reg, treeType);
-
- /* The register is now trashed */
-
- regTracker.rsTrackRegTrash(reg);
-
- genCodeForTree_DONE(tree, reg);
- return;
-
- case GT_IND:
- case GT_NULLCHECK: // At this point, explicit null checks are just like inds...
-
- /* Make sure the operand is addressable */
-
- addrReg = genMakeAddressable(tree, RBM_ALLINT, RegSet::KEEP_REG, true);
-
- genDoneAddressable(tree, addrReg, RegSet::KEEP_REG);
-
- /* Figure out the size of the value being loaded */
-
- size = EA_ATTR(genTypeSize(tree->gtType));
-
- /* Pick a register for the value */
-
- if (needReg == RBM_ALLINT && bestReg == 0)
- {
- /* Absent a better suggestion, pick a useless register */
-
- bestReg = regSet.rsExcludeHint(regSet.rsRegMaskFree(), ~regTracker.rsUselessRegs());
- }
-
- reg = regSet.rsPickReg(needReg, bestReg);
-
- if (op1->IsCnsIntOrI() && op1->IsIconHandle(GTF_ICON_TLS_HDL))
- {
- noway_assert(size == EA_PTRSIZE);
- getEmitter()->emitIns_R_C(ins_Load(TYP_I_IMPL), EA_PTRSIZE, reg, FLD_GLOBAL_FS,
- (int)op1->gtIntCon.gtIconVal);
- }
- else
- {
- /* Generate "mov reg, [addr]" or "movsx/movzx reg, [addr]" */
-
- inst_mov_RV_ST(reg, tree);
- }
-
-#ifdef _TARGET_ARM_
- if (tree->gtFlags & GTF_IND_VOLATILE)
- {
- // Emit a memory barrier instruction after the load
- instGen_MemoryBarrier();
- }
-#endif
-
- /* Note the new contents of the register we used */
-
- regTracker.rsTrackRegTrash(reg);
-
-#ifdef DEBUG
- /* Update the live set of register variables */
- if (compiler->opts.varNames)
- genUpdateLife(tree);
-#endif
-
- /* Now we can update the register pointer information */
-
- // genDoneAddressable(tree, addrReg, RegSet::KEEP_REG);
- gcInfo.gcMarkRegPtrVal(reg, treeType);
-
- genCodeForTree_DONE_LIFE(tree, reg);
- return;
-
- case GT_CAST:
-
- genCodeForNumericCast(tree, destReg, bestReg);
- return;
-
- case GT_JTRUE:
-
- /* Is this a test of a relational operator? */
-
- if (op1->OperIsCompare())
- {
- /* Generate the conditional jump */
-
- genCondJump(op1);
-
- genUpdateLife(tree);
- return;
- }
-
-#ifdef DEBUG
- compiler->gtDispTree(tree);
-#endif
- NO_WAY("ISSUE: can we ever have a jumpCC without a compare node?");
- break;
-
- case GT_SWITCH:
- genCodeForSwitch(tree);
- return;
-
- case GT_RETFILT:
- noway_assert(tree->gtType == TYP_VOID || op1 != 0);
- if (op1 == 0) // endfinally
- {
- reg = REG_NA;
-
-#ifdef _TARGET_XARCH_
- /* Return using a pop-jmp sequence. As the "try" block calls
- the finally with a jmp, this leaves the x86 call-ret stack
- balanced in the normal flow of path. */
-
- noway_assert(isFramePointerRequired());
- inst_RV(INS_pop_hide, REG_EAX, TYP_I_IMPL);
- inst_RV(INS_i_jmp, REG_EAX, TYP_I_IMPL);
-#elif defined(_TARGET_ARM_)
-// Nothing needed for ARM
-#else
- NYI("TARGET");
-#endif
- }
- else // endfilter
- {
- genComputeReg(op1, RBM_INTRET, RegSet::EXACT_REG, RegSet::FREE_REG);
- noway_assert(op1->InReg());
- noway_assert(op1->gtRegNum == REG_INTRET);
- /* The return value has now been computed */
- reg = op1->gtRegNum;
-
- /* Return */
- instGen_Return(0);
- }
-
- genCodeForTree_DONE(tree, reg);
- return;
-
- case GT_RETURN:
-
- // 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();
- }
-
- /* Is there a return value and/or an exit statement? */
-
- if (op1)
- {
- if (op1->gtType == TYP_VOID)
- {
- // We're returning nothing, just generate the block (shared epilog calls).
- genCodeForTree(op1, 0);
- }
-#ifdef _TARGET_ARM_
- else if (op1->gtType == TYP_STRUCT)
- {
- if (op1->gtOper == GT_CALL)
- {
- // We have a return call() because we failed to tail call.
- // In any case, just generate the call and be done.
- assert(compiler->IsHfa(op1));
- genCodeForCall(op1->AsCall(), true);
- genMarkTreeInReg(op1, REG_FLOATRET);
- }
- else
- {
- assert(op1->gtOper == GT_LCL_VAR);
- assert(compiler->IsHfa(compiler->lvaGetStruct(op1->gtLclVarCommon.gtLclNum)));
- genLoadIntoFltRetRegs(op1);
- }
- }
- else if (op1->TypeGet() == TYP_FLOAT)
- {
- // This can only occur when we are returning a non-HFA struct
- // that is composed of a single float field and we performed
- // struct promotion and enregistered the float field.
- //
- genComputeReg(op1, 0, RegSet::ANY_REG, RegSet::FREE_REG);
- getEmitter()->emitIns_R_R(INS_vmov_f2i, EA_4BYTE, REG_INTRET, op1->gtRegNum);
- }
-#endif // _TARGET_ARM_
- else
- {
- // we can now go through this code for compiler->genReturnBB. I've regularized all the code.
-
- // noway_assert(compiler->compCurBB != compiler->genReturnBB);
-
- noway_assert(op1->gtType != TYP_VOID);
-
- /* Generate the return value into the return register */
-
- genComputeReg(op1, RBM_INTRET, RegSet::EXACT_REG, RegSet::FREE_REG);
-
- /* The result must now be in the return register */
-
- noway_assert(op1->InReg());
- noway_assert(op1->gtRegNum == REG_INTRET);
- }
-
- /* The return value has now been computed */
-
- reg = op1->gtRegNum;
-
- genCodeForTree_DONE(tree, reg);
- }
-
-#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
-#ifdef DEBUG
- 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
- return;
-
- case GT_COMMA:
-
- if (tree->gtFlags & GTF_REVERSE_OPS)
- {
- if (tree->gtType == TYP_VOID)
- {
- genEvalSideEffects(op2);
- genUpdateLife(op2);
- genEvalSideEffects(op1);
- genUpdateLife(tree);
- return;
- }
-
- // Generate op2
- genCodeForTree(op2, needReg);
- genUpdateLife(op2);
-
- noway_assert(op2->InReg());
-
- regSet.rsMarkRegUsed(op2);
-
- // Do side effects of op1
- genEvalSideEffects(op1);
-
- // Recover op2 if spilled
- genRecoverReg(op2, RBM_NONE, RegSet::KEEP_REG);
-
- regSet.rsMarkRegFree(genRegMask(op2->gtRegNum));
-
- // set gc info if we need so
- gcInfo.gcMarkRegPtrVal(op2->gtRegNum, treeType);
-
- genUpdateLife(tree);
- genCodeForTree_DONE(tree, op2->gtRegNum);
-
- return;
- }
- 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. The morpher is
- responsible for resetting the type of GT_COMMA nodes
- to TYP_VOID if op2 isn't meant to yield a result. */
-
- genEvalSideEffects(op2);
- genUpdateLife(tree);
- return;
- }
-
- /* Generate the second operand, i.e. the 'real' value */
-
- genCodeForTree(op2, needReg);
- noway_assert(op2->InReg());
-
- /* The result of 'op2' is also the final result */
-
- reg = op2->gtRegNum;
-
- /* Remember whether we set the flags */
-
- tree->gtFlags |= (op2->gtFlags & GTF_ZSF_SET);
-
- genCodeForTree_DONE(tree, reg);
- return;
- }
-
- case GT_BOX:
- genCodeForTree(op1, needReg);
- noway_assert(op1->InReg());
-
- /* The result of 'op1' is also the final result */
-
- reg = op1->gtRegNum;
-
- /* Remember whether we set the flags */
-
- tree->gtFlags |= (op1->gtFlags & GTF_ZSF_SET);
-
- genCodeForTree_DONE(tree, reg);
- return;
-
- case GT_QMARK:
-
- genCodeForQmark(tree, destReg, bestReg);
- return;
-
- case GT_NOP:
-
-#if OPT_BOOL_OPS
- if (op1 == NULL)
- return;
-#endif
- __fallthrough;
-
- case GT_INIT_VAL:
-
- /* Generate the operand into some register */
-
- genCodeForTree(op1, needReg);
-
- /* The result is the same as the operand */
-
- reg = op1->gtRegNum;
-
- genCodeForTree_DONE(tree, reg);
- return;
-
- case GT_INTRINSIC:
-
- switch (tree->gtIntrinsic.gtIntrinsicId)
- {
- case CORINFO_INTRINSIC_Round:
- {
- noway_assert(tree->gtType == TYP_INT);
-
-#if FEATURE_STACK_FP_X87
- genCodeForTreeFlt(op1);
-
- /* Store the FP value into the temp */
- TempDsc* temp = compiler->tmpGetTemp(TYP_INT);
-
- FlatFPX87_MoveToTOS(&compCurFPState, op1->gtRegNum);
- FlatFPX87_Kill(&compCurFPState, op1->gtRegNum);
- inst_FS_ST(INS_fistp, EA_4BYTE, temp, 0);
-
- reg = regSet.rsPickReg(needReg, bestReg);
- regTracker.rsTrackRegTrash(reg);
-
- inst_RV_ST(INS_mov, reg, temp, 0, TYP_INT);
-
- compiler->tmpRlsTemp(temp);
-#else
- genCodeForTreeFloat(tree, needReg, bestReg);
- return;
-#endif
- }
- break;
-
- default:
- noway_assert(!"unexpected math intrinsic");
- }
-
- genCodeForTree_DONE(tree, reg);
- return;
-
- case GT_LCLHEAP:
-
- reg = genLclHeap(op1);
- genCodeForTree_DONE(tree, reg);
- return;
-
- case GT_EQ:
- case GT_NE:
- case GT_LT:
- case GT_LE:
- case GT_GE:
- case GT_GT:
- genCodeForRelop(tree, destReg, bestReg);
- return;
-
- case GT_ADDR:
-
- genCodeForTreeSmpOp_GT_ADDR(tree, destReg, bestReg);
- return;
-
-#ifdef _TARGET_XARCH_
- case GT_LOCKADD:
-
- // This is for a locked add operation. We know that the resulting value doesn't "go" anywhere.
- // For reference, op1 is the location. op2 is the addend or the value.
- if (op2->OperIsConst())
- {
- noway_assert(op2->TypeGet() == TYP_INT);
- ssize_t cns = op2->gtIntCon.gtIconVal;
-
- genComputeReg(op1, RBM_NONE, RegSet::ANY_REG, RegSet::KEEP_REG);
- switch (cns)
- {
- case 1:
- instGen(INS_lock);
- instEmit_RM(INS_inc, op1, op1, 0);
- break;
- case -1:
- instGen(INS_lock);
- instEmit_RM(INS_dec, op1, op1, 0);
- break;
- default:
- assert((int)cns == cns); // By test above for AMD64.
- instGen(INS_lock);
- inst_AT_IV(INS_add, EA_4BYTE, op1, (int)cns, 0);
- break;
- }
- genReleaseReg(op1);
- }
- else
- {
- // non constant addend means it needs to go into a register.
- ins = INS_add;
- goto LockBinOpCommon;
- }
-
- genFlagsEqualToNone(); // We didn't compute a result into a register.
- genUpdateLife(tree); // We didn't compute an operand into anything.
- return;
-
- case GT_XADD:
- ins = INS_xadd;
- goto LockBinOpCommon;
- case GT_XCHG:
- ins = INS_xchg;
- goto LockBinOpCommon;
- LockBinOpCommon:
- {
- // Compute the second operand into a register. xadd and xchg are r/m32, r32. So even if op2
- // is a constant, it needs to be in a register. This should be the output register if
- // possible.
- //
- // For reference, gtOp1 is the location. gtOp2 is the addend or the value.
-
- GenTree* location = op1;
- GenTree* value = op2;
-
- // Again, a friendly reminder. IL calling convention is left to right.
- if (tree->gtFlags & GTF_REVERSE_OPS)
- {
- // The atomic operations destroy this argument, so force it into a scratch register
- reg = regSet.rsPickFreeReg();
- genComputeReg(value, genRegMask(reg), RegSet::EXACT_REG, RegSet::KEEP_REG);
-
- // Must evaluate location into a register
- genCodeForTree(location, needReg, RBM_NONE);
- assert(location->InReg());
- regSet.rsMarkRegUsed(location);
- regSet.rsLockUsedReg(genRegMask(location->gtRegNum));
- genRecoverReg(value, RBM_NONE, RegSet::KEEP_REG);
- regSet.rsUnlockUsedReg(genRegMask(location->gtRegNum));
-
- if (ins != INS_xchg)
- {
- // xchg implies the lock prefix, but xadd and add require it.
- instGen(INS_lock);
- }
- instEmit_RM_RV(ins, EA_4BYTE, location, reg, 0);
- genReleaseReg(value);
- regTracker.rsTrackRegTrash(reg);
- genReleaseReg(location);
- }
- else
- {
- regMaskTP addrReg;
- if (genMakeIndAddrMode(location, tree, false, /* not for LEA */
- needReg, RegSet::KEEP_REG, &addrReg))
- {
- genUpdateLife(location);
-
- reg = regSet.rsPickFreeReg();
- genComputeReg(value, genRegMask(reg), RegSet::EXACT_REG, RegSet::KEEP_REG);
- addrReg = genKeepAddressable(location, addrReg, genRegMask(reg));
-
- if (ins != INS_xchg)
- {
- // xchg implies the lock prefix, but xadd and add require it.
- instGen(INS_lock);
- }
-
- // instEmit_RM_RV(ins, EA_4BYTE, location, reg, 0);
- // inst_TT_RV(ins, location, reg);
- sched_AM(ins, EA_4BYTE, reg, false, location, 0);
-
- genReleaseReg(value);
- regTracker.rsTrackRegTrash(reg);
- genDoneAddressable(location, addrReg, RegSet::KEEP_REG);
- }
- else
- {
- // Must evalute location into a register.
- genCodeForTree(location, needReg, RBM_NONE);
- assert(location->InReg());
- regSet.rsMarkRegUsed(location);
-
- // xadd destroys this argument, so force it into a scratch register
- reg = regSet.rsPickFreeReg();
- genComputeReg(value, genRegMask(reg), RegSet::EXACT_REG, RegSet::KEEP_REG);
- regSet.rsLockUsedReg(genRegMask(value->gtRegNum));
- genRecoverReg(location, RBM_NONE, RegSet::KEEP_REG);
- regSet.rsUnlockUsedReg(genRegMask(value->gtRegNum));
-
- if (ins != INS_xchg)
- {
- // xchg implies the lock prefix, but xadd and add require it.
- instGen(INS_lock);
- }
-
- instEmit_RM_RV(ins, EA_4BYTE, location, reg, 0);
-
- genReleaseReg(value);
- regTracker.rsTrackRegTrash(reg);
- genReleaseReg(location);
- }
- }
-
- // The flags are equal to the target of the tree (i.e. the result of the add), not to the
- // result in the register. If tree is actually GT_IND->GT_ADDR->GT_LCL_VAR, we could use
- // that information to set the flags. Doesn't seem like there is a good reason for that.
- // Therefore, trash the flags.
- genFlagsEqualToNone();
-
- if (ins == INS_add)
- {
- // If the operator was add, then we were called from the GT_LOCKADD
- // case. In that case we don't use the result, so we don't need to
- // update anything.
- genUpdateLife(tree);
- }
- else
- {
- genCodeForTree_DONE(tree, reg);
- }
- }
- return;
-
-#endif // _TARGET_XARCH_
-
- case GT_ARR_LENGTH:
- {
- // Make the corresponding ind(a + c) node, and do codegen for that.
- GenTree* addr = compiler->gtNewOperNode(GT_ADD, TYP_BYREF, tree->gtArrLen.ArrRef(),
- compiler->gtNewIconNode(tree->AsArrLen()->ArrLenOffset()));
- tree->SetOper(GT_IND);
- tree->gtFlags |= GTF_IND_ARR_LEN; // Record that this node represents an array length expression.
- assert(tree->TypeGet() == TYP_INT);
- tree->gtOp.gtOp1 = addr;
- genCodeForTree(tree, destReg, bestReg);
- return;
- }
-
- case GT_OBJ:
- // All GT_OBJ nodes must have been morphed prior to this.
- noway_assert(!"Should not see a GT_OBJ node during CodeGen.");
-
- default:
-#ifdef DEBUG
- compiler->gtDispTree(tree);
-#endif
- noway_assert(!"unexpected unary/binary operator");
- } // end switch (oper)
-
- unreached();
-}
-#ifdef _PREFAST_
-#pragma warning(pop) // End suppress PREFast warning about overly large function
-#endif
-
-regNumber CodeGen::genIntegerCast(GenTree* tree, regMaskTP needReg, regMaskTP bestReg)
-{
- instruction ins;
- emitAttr size;
- bool unsv;
- bool andv = false;
- regNumber reg;
- GenTree* op1 = tree->gtOp.gtOp1->gtEffectiveVal();
- var_types dstType = tree->CastToType();
- var_types srcType = op1->TypeGet();
-
- if (genTypeSize(srcType) < genTypeSize(dstType))
- {
- // Widening cast
-
- /* we need the source size */
-
- size = EA_ATTR(genTypeSize(srcType));
-
- noway_assert(size < EA_PTRSIZE);
-
- unsv = varTypeIsUnsigned(srcType);
- ins = ins_Move_Extend(srcType, op1->InReg());
-
- /*
- Special case: for a cast of byte to char we first
- have to expand the byte (w/ sign extension), then
- mask off the high bits.
- Use 'movsx' followed by 'and'
- */
- if (!unsv && varTypeIsUnsigned(dstType) && genTypeSize(dstType) < EA_4BYTE)
- {
- noway_assert(genTypeSize(dstType) == EA_2BYTE && size == EA_1BYTE);
- andv = true;
- }
- }
- else
- {
- // Narrowing cast, or sign-changing cast
-
- noway_assert(genTypeSize(srcType) >= genTypeSize(dstType));
-
- size = EA_ATTR(genTypeSize(dstType));
-
- unsv = varTypeIsUnsigned(dstType);
- ins = ins_Move_Extend(dstType, op1->InReg());
- }
-
- noway_assert(size < EA_PTRSIZE);
-
- // Set bestReg to the same register a op1 if op1 is a regVar and is available
- if (op1->InReg())
- {
- regMaskTP op1RegMask = genRegMask(op1->gtRegNum);
- if ((((op1RegMask & bestReg) != 0) || (bestReg == 0)) && ((op1RegMask & regSet.rsRegMaskFree()) != 0))
- {
- bestReg = op1RegMask;
- }
- }
-
- /* Is the value sitting in a non-byte-addressable register? */
-
- if (op1->InReg() && (size == EA_1BYTE) && !isByteReg(op1->gtRegNum))
- {
- if (unsv)
- {
- // for unsigned values we can AND, so it needs not be a byte register
-
- reg = regSet.rsPickReg(needReg, bestReg);
-
- ins = INS_AND;
- }
- else
- {
- /* Move the value into a byte register */
-
- reg = regSet.rsGrabReg(RBM_BYTE_REGS);
- }
-
- if (reg != op1->gtRegNum)
- {
- /* Move the value into that register */
-
- regTracker.rsTrackRegCopy(reg, op1->gtRegNum);
- inst_RV_RV(INS_mov, reg, op1->gtRegNum, srcType);
-
- /* The value has a new home now */
-
- op1->gtRegNum = reg;
- }
- }
- else
- {
- /* Pick a register for the value (general case) */
-
- reg = regSet.rsPickReg(needReg, bestReg);
-
- // if we (might) need to set the flags and the value is in the same register
- // and we have an unsigned value then use AND instead of MOVZX
- if (tree->gtSetFlags() && unsv && op1->InReg() && (op1->gtRegNum == reg))
- {
-#ifdef _TARGET_X86_
- noway_assert(ins == INS_movzx);
-#endif
- ins = INS_AND;
- }
- }
-
- if (ins == INS_AND)
- {
- noway_assert(andv == false && unsv);
-
- /* Generate "and reg, MASK */
-
- insFlags flags = tree->gtSetFlags() ? INS_FLAGS_SET : INS_FLAGS_DONT_CARE;
- inst_RV_IV(INS_AND, reg, (size == EA_1BYTE) ? 0xFF : 0xFFFF, EA_4BYTE, flags);
-
- if (tree->gtSetFlags())
- genFlagsEqualToReg(tree, reg);
- }
- else
- {
-#ifdef _TARGET_XARCH_
- noway_assert(ins == INS_movsx || ins == INS_movzx);
-#endif
-
- /* Generate "movsx/movzx reg, [addr]" */
-
- inst_RV_ST(ins, size, reg, op1);
-
- /* Mask off high bits for cast from byte to char */
-
- if (andv)
- {
-#ifdef _TARGET_XARCH_
- noway_assert(genTypeSize(dstType) == 2 && ins == INS_movsx);
-#endif
- insFlags flags = tree->gtSetFlags() ? INS_FLAGS_SET : INS_FLAGS_DONT_CARE;
- inst_RV_IV(INS_AND, reg, 0xFFFF, EA_4BYTE, flags);
-
- if (tree->gtSetFlags())
- genFlagsEqualToReg(tree, reg);
- }
- }
-
- regTracker.rsTrackRegTrash(reg);
- return reg;
-}
-
-void CodeGen::genCodeForNumericCast(GenTree* tree, regMaskTP destReg, regMaskTP bestReg)
-{
- GenTree* op1 = tree->gtOp.gtOp1;
- var_types dstType = tree->CastToType();
- var_types baseType = TYP_INT;
- regNumber reg = DUMMY_INIT(REG_CORRUPT);
- regMaskTP needReg = destReg;
- regMaskTP addrReg;
- emitAttr size;
- BOOL unsv;
-
- /*
- * Constant casts should have been folded earlier
- * If not finite don't bother
- * We don't do this optimization for debug code/no optimization
- */
-
- noway_assert(
- (op1->gtOper != GT_CNS_INT && op1->gtOper != GT_CNS_LNG && op1->gtOper != GT_CNS_DBL) || tree->gtOverflow() ||
- (op1->gtOper == GT_CNS_DBL && !_finite(op1->gtDblCon.gtDconVal)) ||
- (op1->gtOper == GT_CNS_DBL && op1->gtDblCon.gtDconVal <= -1.0 && varTypeIsUnsigned(tree->CastToType())) ||
- !compiler->opts.OptEnabled(CLFLG_CONSTANTFOLD));
-
- noway_assert(dstType != TYP_VOID);
-
- /* What type are we casting from? */
-
- switch (op1->TypeGet())
- {
- case TYP_LONG:
-
- /* Special case: the long is generated via the mod of long
- with an int. This is really an int and need not be
- converted to a reg pair. NOTE: the flag only indicates
- that this is a case to TYP_INT, it hasn't actually
- verified the second operand of the MOD! */
-
- if (((op1->gtOper == GT_MOD) || (op1->gtOper == GT_UMOD)) && (op1->gtFlags & GTF_MOD_INT_RESULT))
- {
-
- /* Verify that the op2 of the mod node is
- 1) An integer tree, or
- 2) A long constant that is small enough to fit in an integer
- */
-
- GenTree* modop2 = op1->gtOp.gtOp2;
- if ((genActualType(modop2->gtType) == TYP_INT) ||
- ((modop2->gtOper == GT_CNS_LNG) && (modop2->gtLngCon.gtLconVal == (int)modop2->gtLngCon.gtLconVal)))
- {
- genCodeForTree(op1, destReg, bestReg);
-
-#ifdef _TARGET_64BIT_
- reg = op1->gtRegNum;
-#else // _TARGET_64BIT_
- reg = genRegPairLo(op1->gtRegPair);
-#endif //_TARGET_64BIT_
-
- genCodeForTree_DONE(tree, reg);
- return;
- }
- }
-
- /* Make the operand addressable. When gtOverflow() is true,
- hold on to the addrReg as we will need it to access the higher dword */
-
- op1 = genCodeForCommaTree(op1); // Strip off any commas (necessary, since we seem to generate code for op1
- // twice!)
- // See, e.g., the TYP_INT case below...
-
- addrReg = genMakeAddressable2(op1, 0, tree->gtOverflow() ? RegSet::KEEP_REG : RegSet::FREE_REG, false);
-
- /* Load the lower half of the value into some register */
-
- if (op1->InReg())
- {
- /* Can we simply use the low part of the value? */
- reg = genRegPairLo(op1->gtRegPair);
-
- if (tree->gtOverflow())
- goto REG_OK;
-
- regMaskTP loMask;
- loMask = genRegMask(reg);
- if (loMask & regSet.rsRegMaskFree())
- bestReg = loMask;
- }
-
- // for cast overflow we need to preserve addrReg for testing the hiDword
- // so we lock it to prevent regSet.rsPickReg from picking it.
- if (tree->gtOverflow())
- regSet.rsLockUsedReg(addrReg);
-
- reg = regSet.rsPickReg(needReg, bestReg);
-
- if (tree->gtOverflow())
- regSet.rsUnlockUsedReg(addrReg);
-
- noway_assert(genStillAddressable(op1));
-
- REG_OK:
- if (!op1->InReg() || (reg != genRegPairLo(op1->gtRegPair)))
- {
- /* Generate "mov reg, [addr-mode]" */
- inst_RV_TT(ins_Load(TYP_INT), reg, op1);
- }
-
- /* conv.ovf.i8i4, or conv.ovf.u8u4 */
-
- if (tree->gtOverflow())
- {
- regNumber hiReg = (op1->InReg()) ? genRegPairHi(op1->gtRegPair) : REG_NA;
-
- emitJumpKind jmpNotEqual = genJumpKindForOper(GT_NE, CK_SIGNED);
- emitJumpKind jmpLTS = genJumpKindForOper(GT_LT, CK_SIGNED);
-
- switch (dstType)
- {
- case TYP_INT:
- // conv.ovf.i8.i4
- /* Generate the following sequence
-
- test loDWord, loDWord // set flags
- jl neg
- pos: test hiDWord, hiDWord // set flags
- jne ovf
- jmp done
- neg: cmp hiDWord, 0xFFFFFFFF
- jne ovf
- done:
-
- */
-
- instGen_Compare_Reg_To_Zero(EA_4BYTE, reg);
- if (tree->gtFlags & GTF_UNSIGNED) // conv.ovf.u8.i4 (i4 > 0 and upper bits 0)
- {
- genJumpToThrowHlpBlk(jmpLTS, SCK_OVERFLOW);
- goto UPPER_BITS_ZERO;
- }
-
-#if CPU_LOAD_STORE_ARCH
- // This is tricky.
- // We will generate code like
- // if (...)
- // {
- // ...
- // }
- // else
- // {
- // ...
- // }
- // We load the tree op1 into regs when we generate code for if clause.
- // When we generate else clause, we see the tree is already loaded into reg, and start use it
- // directly.
- // Well, when the code is run, we may execute else clause without going through if clause.
- //
- genCodeForTree(op1, 0);
-#endif
-
- BasicBlock* neg;
- BasicBlock* done;
-
- neg = genCreateTempLabel();
- done = genCreateTempLabel();
-
- // Is the loDWord positive or negative
- inst_JMP(jmpLTS, neg);
-
- // If loDWord is positive, hiDWord should be 0 (sign extended loDWord)
-
- if (hiReg < REG_STK)
- {
- instGen_Compare_Reg_To_Zero(EA_4BYTE, hiReg);
- }
- else
- {
- inst_TT_IV(INS_cmp, op1, 0x00000000, 4);
- }
-
- genJumpToThrowHlpBlk(jmpNotEqual, SCK_OVERFLOW);
- inst_JMP(EJ_jmp, done);
-
- // If loDWord is negative, hiDWord should be -1 (sign extended loDWord)
-
- genDefineTempLabel(neg);
-
- if (hiReg < REG_STK)
- {
- inst_RV_IV(INS_cmp, hiReg, 0xFFFFFFFFL, EA_4BYTE);
- }
- else
- {
- inst_TT_IV(INS_cmp, op1, 0xFFFFFFFFL, 4);
- }
- genJumpToThrowHlpBlk(jmpNotEqual, SCK_OVERFLOW);
-
- // Done
-
- genDefineTempLabel(done);
-
- break;
-
- case TYP_UINT: // conv.ovf.u8u4
- UPPER_BITS_ZERO:
- // Just check that the upper DWord is 0
-
- if (hiReg < REG_STK)
- {
- instGen_Compare_Reg_To_Zero(EA_4BYTE, hiReg); // set flags
- }
- else
- {
- inst_TT_IV(INS_cmp, op1, 0, 4);
- }
-
- genJumpToThrowHlpBlk(jmpNotEqual, SCK_OVERFLOW);
- break;
-
- default:
- noway_assert(!"Unexpected dstType");
- break;
- }
-
- genDoneAddressable(op1, addrReg, RegSet::KEEP_REG);
- }
-
- regTracker.rsTrackRegTrash(reg);
- genDoneAddressable(op1, addrReg, RegSet::FREE_REG);
-
- genCodeForTree_DONE(tree, reg);
- return;
-
- case TYP_BOOL:
- case TYP_BYTE:
- case TYP_SHORT:
- case TYP_USHORT:
- case TYP_UBYTE:
- break;
-
- case TYP_UINT:
- case TYP_INT:
- break;
-
-#if FEATURE_STACK_FP_X87
- case TYP_FLOAT:
- NO_WAY("OPCAST from TYP_FLOAT should have been converted into a helper call");
- break;
-
- case TYP_DOUBLE:
- if (compiler->opts.compCanUseSSE2)
- {
- // do the SSE2 based cast inline
- // getting the fp operand
-
- regMaskTP addrRegInt = 0;
- regMaskTP addrRegFlt = 0;
-
- // make the operand addressable
- // We don't want to collapse constant doubles into floats, as the SSE2 instruction
- // operates on doubles. Note that these (casts from constant doubles) usually get
- // folded, but we don't do it for some cases (infinitys, etc). So essentially this
- // shouldn't affect performance or size at all. We're fixing this for #336067
- op1 = genMakeAddressableStackFP(op1, &addrRegInt, &addrRegFlt, false);
- if (!addrRegFlt && !op1->IsRegVar())
- {
- // we have the address
-
- inst_RV_TT(INS_movsdsse2, REG_XMM0, op1, 0, EA_8BYTE);
- genDoneAddressableStackFP(op1, addrRegInt, addrRegFlt, RegSet::KEEP_REG);
- genUpdateLife(op1);
-
- reg = regSet.rsPickReg(needReg);
- getEmitter()->emitIns_R_R(INS_cvttsd2si, EA_8BYTE, reg, REG_XMM0);
-
- regTracker.rsTrackRegTrash(reg);
- genCodeForTree_DONE(tree, reg);
- }
- else
- {
- // we will need to use a temp to get it into the xmm reg
- var_types typeTemp = op1->TypeGet();
- TempDsc* temp = compiler->tmpGetTemp(typeTemp);
-
- size = EA_ATTR(genTypeSize(typeTemp));
-
- if (addrRegFlt)
- {
- // On the fp stack; Take reg to top of stack
-
- FlatFPX87_MoveToTOS(&compCurFPState, op1->gtRegNum);
- }
- else
- {
- // op1->IsRegVar()
- // pick a register
- reg = regSet.PickRegFloat();
- if (!op1->IsRegVarDeath())
- {
- // Load it on the fp stack
- genLoadStackFP(op1, reg);
- }
- else
- {
- // if it's dying, genLoadStackFP just renames it and then we move reg to TOS
- genLoadStackFP(op1, reg);
- FlatFPX87_MoveToTOS(&compCurFPState, reg);
- }
- }
-
- // pop it off the fp stack
- compCurFPState.Pop();
-
- getEmitter()->emitIns_S(INS_fstp, size, temp->tdTempNum(), 0);
- // pick a reg
- reg = regSet.rsPickReg(needReg);
-
- inst_RV_ST(INS_movsdsse2, REG_XMM0, temp, 0, TYP_DOUBLE, EA_8BYTE);
- getEmitter()->emitIns_R_R(INS_cvttsd2si, EA_8BYTE, reg, REG_XMM0);
-
- // done..release the temp
- compiler->tmpRlsTemp(temp);
-
- // the reg is now trashed
- regTracker.rsTrackRegTrash(reg);
- genDoneAddressableStackFP(op1, addrRegInt, addrRegFlt, RegSet::KEEP_REG);
- genUpdateLife(op1);
- genCodeForTree_DONE(tree, reg);
- }
- }
-#else
- case TYP_FLOAT:
- case TYP_DOUBLE:
- genCodeForTreeFloat(tree, needReg, bestReg);
-#endif // FEATURE_STACK_FP_X87
- return;
-
- default:
- noway_assert(!"unexpected cast type");
- }
-
- if (tree->gtOverflow())
- {
- /* Compute op1 into a register, and free the register */
-
- genComputeReg(op1, destReg, RegSet::ANY_REG, RegSet::FREE_REG);
- reg = op1->gtRegNum;
-
- /* Do we need to compare the value, or just check masks */
-
- ssize_t typeMin = DUMMY_INIT(~0), typeMax = DUMMY_INIT(0);
- ssize_t typeMask;
-
- switch (dstType)
- {
- case TYP_BYTE:
- typeMask = ssize_t((int)0xFFFFFF80);
- typeMin = SCHAR_MIN;
- typeMax = SCHAR_MAX;
- unsv = (tree->gtFlags & GTF_UNSIGNED);
- break;
- case TYP_SHORT:
- typeMask = ssize_t((int)0xFFFF8000);
- typeMin = SHRT_MIN;
- typeMax = SHRT_MAX;
- unsv = (tree->gtFlags & GTF_UNSIGNED);
- break;
- case TYP_INT:
- typeMask = ssize_t((int)0x80000000L);
-#ifdef _TARGET_64BIT_
- unsv = (tree->gtFlags & GTF_UNSIGNED);
- typeMin = INT_MIN;
- typeMax = INT_MAX;
-#else // _TARGET_64BIT_
- noway_assert((tree->gtFlags & GTF_UNSIGNED) != 0);
- unsv = true;
-#endif // _TARGET_64BIT_
- break;
- case TYP_UBYTE:
- unsv = true;
- typeMask = ssize_t((int)0xFFFFFF00L);
- break;
- case TYP_USHORT:
- unsv = true;
- typeMask = ssize_t((int)0xFFFF0000L);
- break;
- case TYP_UINT:
- unsv = true;
-#ifdef _TARGET_64BIT_
- typeMask = 0xFFFFFFFF00000000LL;
-#else // _TARGET_64BIT_
- typeMask = 0x80000000L;
- noway_assert((tree->gtFlags & GTF_UNSIGNED) == 0);
-#endif // _TARGET_64BIT_
- break;
- default:
- NO_WAY("Unknown type");
- return;
- }
-
- // If we just have to check a mask.
- // This must be conv.ovf.u4u1, conv.ovf.u4u2, conv.ovf.u4i4,
- // or conv.i4u4
-
- if (unsv)
- {
- inst_RV_IV(INS_TEST, reg, typeMask, emitActualTypeSize(baseType));
- emitJumpKind jmpNotEqual = genJumpKindForOper(GT_NE, CK_SIGNED);
- genJumpToThrowHlpBlk(jmpNotEqual, SCK_OVERFLOW);
- }
- else
- {
- // Check the value is in range.
- // This must be conv.ovf.i4i1, etc.
-
- // Compare with the MAX
-
- noway_assert(typeMin != DUMMY_INIT(~0) && typeMax != DUMMY_INIT(0));
-
- inst_RV_IV(INS_cmp, reg, typeMax, emitActualTypeSize(baseType));
- emitJumpKind jmpGTS = genJumpKindForOper(GT_GT, CK_SIGNED);
- genJumpToThrowHlpBlk(jmpGTS, SCK_OVERFLOW);
-
- // Compare with the MIN
-
- inst_RV_IV(INS_cmp, reg, typeMin, emitActualTypeSize(baseType));
- emitJumpKind jmpLTS = genJumpKindForOper(GT_LT, CK_SIGNED);
- genJumpToThrowHlpBlk(jmpLTS, SCK_OVERFLOW);
- }
-
- genCodeForTree_DONE(tree, reg);
- return;
- }
-
- /* Make the operand addressable */
-
- addrReg = genMakeAddressable(op1, needReg, RegSet::FREE_REG, true);
-
- reg = genIntegerCast(tree, needReg, bestReg);
-
- genDoneAddressable(op1, addrReg, RegSet::FREE_REG);
-
- genCodeForTree_DONE(tree, reg);
-}
-
-/*****************************************************************************
- *
- * Generate code for a leaf node of type GT_ADDR
- */
-
-void CodeGen::genCodeForTreeSmpOp_GT_ADDR(GenTree* tree, regMaskTP destReg, regMaskTP bestReg)
-{
- genTreeOps oper = tree->OperGet();
- const var_types treeType = tree->TypeGet();
- GenTree* op1;
- regNumber reg;
- regMaskTP needReg = destReg;
- regMaskTP addrReg;
-
-#ifdef DEBUG
- reg = (regNumber)0xFEEFFAAF; // to detect uninitialized use
- addrReg = 0xDEADCAFE;
-#endif
-
- // We should get here for ldloca, ldarga, ldslfda, ldelema,
- // or ldflda.
- if (oper == GT_ARR_ELEM)
- {
- op1 = tree;
- }
- else
- {
- op1 = tree->gtOp.gtOp1;
- }
-
- // (tree=op1, needReg=0, keepReg=RegSet::FREE_REG, smallOK=true)
- if (oper == GT_ARR_ELEM)
- {
- // To get the address of the array element,
- // we first call genMakeAddrArrElem to make the element addressable.
- // (That is, for example, we first emit code to calculate EBX, and EAX.)
- // And then use lea to obtain the address.
- // (That is, for example, we then emit
- // lea EBX, bword ptr [EBX+4*EAX+36]
- // to obtain the address of the array element.)
- addrReg = genMakeAddrArrElem(op1, tree, RBM_NONE, RegSet::FREE_REG);
- }
- else
- {
- addrReg = genMakeAddressable(op1, 0, RegSet::FREE_REG, true);
- }
-
- noway_assert(treeType == TYP_BYREF || treeType == TYP_I_IMPL);
-
- // We want to reuse one of the scratch registers that were used
- // in forming the address mode as the target register for the lea.
- // If bestReg is unset or if it is set to one of the registers used to
- // form the address (i.e. addrReg), we calculate the scratch register
- // to use as the target register for the LEA
-
- bestReg = regSet.rsUseIfZero(bestReg, addrReg);
- bestReg = regSet.rsNarrowHint(bestReg, addrReg);
-
- /* Even if addrReg is regSet.rsRegMaskCanGrab(), regSet.rsPickReg() won't spill
- it since keepReg==false.
- If addrReg can't be grabbed, regSet.rsPickReg() won't touch it anyway.
- So this is guaranteed not to spill addrReg */
-
- reg = regSet.rsPickReg(needReg, bestReg);
-
- // Slight workaround, force the inst routine to think that
- // value being loaded is an int (since that is what what
- // LEA will return) otherwise it would try to allocate
- // two registers for a long etc.
- noway_assert(treeType == TYP_I_IMPL || treeType == TYP_BYREF);
- op1->gtType = treeType;
-
- inst_RV_TT(INS_lea, reg, op1, 0, (treeType == TYP_BYREF) ? EA_BYREF : EA_PTRSIZE);
-
- // The Lea instruction above better not have tried to put the
- // 'value' pointed to by 'op1' in a register, LEA will not work.
- noway_assert(!(op1->InReg()));
-
- genDoneAddressable(op1, addrReg, RegSet::FREE_REG);
- // gcInfo.gcMarkRegSetNpt(genRegMask(reg));
- noway_assert((gcInfo.gcRegGCrefSetCur & genRegMask(reg)) == 0);
-
- regTracker.rsTrackRegTrash(reg); // reg does have foldable value in it
- gcInfo.gcMarkRegPtrVal(reg, treeType);
-
- genCodeForTree_DONE(tree, reg);
-}
-
-#ifdef _TARGET_ARM_
-
-/*****************************************************************************
- *
- * Move (load/store) between float ret regs and struct promoted variable.
- *
- * varDsc - The struct variable to be loaded from or stored into.
- * isLoadIntoFlt - Perform a load operation if "true" or store if "false."
- *
- */
-void CodeGen::genLdStFltRetRegsPromotedVar(LclVarDsc* varDsc, bool isLoadIntoFlt)
-{
- regNumber curReg = REG_FLOATRET;
-
- unsigned lclLast = varDsc->lvFieldLclStart + varDsc->lvFieldCnt - 1;
- for (unsigned lclNum = varDsc->lvFieldLclStart; lclNum <= lclLast; ++lclNum)
- {
- LclVarDsc* varDscFld = &compiler->lvaTable[lclNum];
-
- // Is the struct field promoted and sitting in a register?
- if (varDscFld->lvRegister)
- {
- // Move from the struct field into curReg if load
- // else move into struct field from curReg if store
- regNumber srcReg = (isLoadIntoFlt) ? varDscFld->lvRegNum : curReg;
- regNumber dstReg = (isLoadIntoFlt) ? curReg : varDscFld->lvRegNum;
- if (srcReg != dstReg)
- {
- inst_RV_RV(ins_Copy(varDscFld->TypeGet()), dstReg, srcReg, varDscFld->TypeGet());
- regTracker.rsTrackRegCopy(dstReg, srcReg);
- }
- }
- else
- {
- // This field is in memory, do a move between the field and float registers.
- emitAttr size = (varDscFld->TypeGet() == TYP_DOUBLE) ? EA_8BYTE : EA_4BYTE;
- if (isLoadIntoFlt)
- {
- getEmitter()->emitIns_R_S(ins_Load(varDscFld->TypeGet()), size, curReg, lclNum, 0);
- regTracker.rsTrackRegTrash(curReg);
- }
- else
- {
- getEmitter()->emitIns_S_R(ins_Store(varDscFld->TypeGet()), size, curReg, lclNum, 0);
- }
- }
-
- // Advance the current reg.
- curReg = (varDscFld->TypeGet() == TYP_DOUBLE) ? REG_NEXT(REG_NEXT(curReg)) : REG_NEXT(curReg);
- }
-}
-
-void CodeGen::genLoadIntoFltRetRegs(GenTree* tree)
-{
- assert(tree->TypeGet() == TYP_STRUCT);
- assert(tree->gtOper == GT_LCL_VAR);
- LclVarDsc* varDsc = compiler->lvaTable + tree->gtLclVarCommon.gtLclNum;
- int slots = varDsc->lvSize() / REGSIZE_BYTES;
- if (varDsc->lvPromoted)
- {
- genLdStFltRetRegsPromotedVar(varDsc, true);
- }
- else
- {
- if (slots <= 2)
- {
- // Use the load float/double instruction.
- inst_RV_TT(ins_Load((slots == 1) ? TYP_FLOAT : TYP_DOUBLE), REG_FLOATRET, tree, 0,
- (slots == 1) ? EA_4BYTE : EA_8BYTE);
- }
- else
- {
- // Use the load store multiple instruction.
- regNumber reg = regSet.rsPickReg(RBM_ALLINT);
- inst_RV_TT(INS_lea, reg, tree, 0, EA_PTRSIZE);
- regTracker.rsTrackRegTrash(reg);
- getEmitter()->emitIns_R_R_I(INS_vldm, EA_4BYTE, REG_FLOATRET, reg, slots * REGSIZE_BYTES);
- }
- }
- genMarkTreeInReg(tree, REG_FLOATRET);
-}
-
-void CodeGen::genStoreFromFltRetRegs(GenTree* tree)
-{
- assert(tree->TypeGet() == TYP_STRUCT);
- assert(tree->OperGet() == GT_ASG);
-
- // LHS should be lcl var or fld.
- GenTree* op1 = tree->gtOp.gtOp1;
-
- // TODO: We had a bug where op1 was a GT_IND, the result of morphing a GT_BOX, and not properly
- // handling multiple levels of inlined functions that return HFA on the right-hand-side.
- // So, make the op1 check a noway_assert (that exists in non-debug builds) so we'll fall
- // back to MinOpts with no inlining, if we don't have what we expect. We don't want to
- // do the full IsHfa() check in non-debug, since that involves VM calls, so leave that
- // as a regular assert().
- noway_assert((op1->gtOper == GT_LCL_VAR) || (op1->gtOper == GT_LCL_FLD));
- unsigned varNum = op1->gtLclVarCommon.gtLclNum;
- assert(compiler->IsHfa(compiler->lvaGetStruct(varNum)));
-
- // The RHS should be a call.
- GenTree* op2 = tree->gtOp.gtOp2;
- assert(op2->gtOper == GT_CALL);
-
- // Generate code for call and copy the return registers into the local.
- regMaskTP retMask = genCodeForCall(op2->AsCall(), true);
-
- // Ret mask should be contiguously set from s0, up to s3 or starting from d0 upto d3.
- CLANG_FORMAT_COMMENT_ANCHOR;
-
-#ifdef DEBUG
- regMaskTP mask = ((retMask >> REG_FLOATRET) + 1);
- assert((mask & (mask - 1)) == 0);
- assert(mask <= (1 << MAX_HFA_RET_SLOTS));
- assert((retMask & (((regMaskTP)RBM_FLOATRET) - 1)) == 0);
-#endif
-
- int slots = genCountBits(retMask & RBM_ALLFLOAT);
-
- LclVarDsc* varDsc = &compiler->lvaTable[varNum];
-
- if (varDsc->lvPromoted)
- {
- genLdStFltRetRegsPromotedVar(varDsc, false);
- }
- else
- {
- if (slots <= 2)
- {
- inst_TT_RV(ins_Store((slots == 1) ? TYP_FLOAT : TYP_DOUBLE), op1, REG_FLOATRET, 0,
- (slots == 1) ? EA_4BYTE : EA_8BYTE);
- }
- else
- {
- regNumber reg = regSet.rsPickReg(RBM_ALLINT);
- inst_RV_TT(INS_lea, reg, op1, 0, EA_PTRSIZE);
- regTracker.rsTrackRegTrash(reg);
- getEmitter()->emitIns_R_R_I(INS_vstm, EA_4BYTE, REG_FLOATRET, reg, slots * REGSIZE_BYTES);
- }
- }
-}
-
-#endif // _TARGET_ARM_
-
-/*****************************************************************************
- *
- * Generate code for a GT_ASG tree
- */
-
-#ifdef _PREFAST_
-#pragma warning(push)
-#pragma warning(disable : 21000) // Suppress PREFast warning about overly large function
-#endif
-void CodeGen::genCodeForTreeSmpOpAsg(GenTree* tree)
-{
- noway_assert(tree->gtOper == GT_ASG);
-
- GenTree* op1 = tree->gtOp.gtOp1;
- GenTree* op2 = tree->gtOp.gtOp2;
- regMaskTP needReg = RBM_ALLINT;
- regMaskTP bestReg = RBM_CORRUPT;
- regMaskTP addrReg = DUMMY_INIT(RBM_CORRUPT);
- bool ovfl = false; // Do we need an overflow check
- bool volat = false; // Is this a volatile store
- regMaskTP regGC;
- instruction ins;
- unsigned lclVarNum = compiler->lvaCount;
- unsigned lclILoffs = DUMMY_INIT(0);
-
-#ifdef _TARGET_ARM_
- if (tree->gtType == TYP_STRUCT)
- {
- // We use copy block to assign structs, however to receive HFAs in registers
- // from a CALL, we use assignment, var = (hfa) call();
- assert(compiler->IsHfa(tree));
- genStoreFromFltRetRegs(tree);
- return;
- }
-#endif
-
-#ifdef DEBUG
- if (varTypeIsFloating(op1) != varTypeIsFloating(op2))
- {
- if (varTypeIsFloating(op1))
- assert(!"Bad IL: Illegal assignment of integer into float!");
- else
- assert(!"Bad IL: Illegal assignment of float into integer!");
- }
-#endif
-
- if ((tree->gtFlags & GTF_REVERSE_OPS) == 0)
- {
- op1 = genCodeForCommaTree(op1); // Strip away any comma expressions.
- }
-
- /* Is the target a register or local variable? */
- switch (op1->gtOper)
- {
- unsigned varNum;
- LclVarDsc* varDsc;
-
- 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;
- }
-
- /* Check against dead store ? (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))
- goto REG_VAR2;
-
- break;
-
- REG_VAR2:
-
- /* Get hold of the target register */
-
- regNumber op1Reg;
-
- op1Reg = op1->gtRegVar.gtRegNum;
-
-#ifdef DEBUG
- /* Compute the RHS (hopefully) into the variable's register.
- For debuggable code, op1Reg may already be part of regSet.rsMaskVars,
- as variables are kept alive everywhere. So we have to be
- careful if we want to compute the value directly into
- the variable's register. */
-
- bool needToUpdateRegSetCheckLevel;
- needToUpdateRegSetCheckLevel = false;
-#endif
-
- // We should only be accessing lvVarIndex if varDsc is tracked.
- assert(varDsc->lvTracked);
-
- if (VarSetOps::IsMember(compiler, genUpdateLiveSetForward(op2), varDsc->lvVarIndex))
- {
- noway_assert(compiler->opts.compDbgCode);
-
- /* The predictor might expect us to generate op2 directly
- into the var's register. However, since the variable is
- already alive, first kill it and its register. */
-
- if (rpCanAsgOperWithoutReg(op2, true))
- {
- genUpdateLife(VarSetOps::RemoveElem(compiler, compiler->compCurLife, varDsc->lvVarIndex));
- needReg = regSet.rsNarrowHint(needReg, genRegMask(op1Reg));
-#ifdef DEBUG
- needToUpdateRegSetCheckLevel = true;
-#endif
- }
- }
- else
- {
- needReg = regSet.rsNarrowHint(needReg, genRegMask(op1Reg));
- }
-
-#ifdef DEBUG
-
- /* Special cases: op2 is a GT_CNS_INT */
-
- if (op2->gtOper == GT_CNS_INT && !(op1->gtFlags & GTF_VAR_DEATH))
- {
- /* Save the old life status */
-
- VarSetOps::Assign(compiler, genTempOldLife, compiler->compCurLife);
- VarSetOps::AddElemD(compiler, compiler->compCurLife, varDsc->lvVarIndex);
-
- /* Set a flag to avoid printing the message
- and remember that life was changed. */
-
- genTempLiveChg = false;
- }
-#endif
-
-#ifdef DEBUG
- if (needToUpdateRegSetCheckLevel)
- compiler->compRegSetCheckLevel++;
-#endif
- genCodeForTree(op2, needReg, genRegMask(op1Reg));
-#ifdef DEBUG
- if (needToUpdateRegSetCheckLevel)
- compiler->compRegSetCheckLevel--;
- noway_assert(compiler->compRegSetCheckLevel >= 0);
-#endif
- noway_assert(op2->InReg());
-
- /* Make sure the value ends up in the right place ... */
-
- if (op2->gtRegNum != op1Reg)
- {
- /* Make sure the target of the store is available */
-
- if (regSet.rsMaskUsed & genRegMask(op1Reg))
- regSet.rsSpillReg(op1Reg);
-
-#ifdef _TARGET_ARM_
- if (op1->TypeGet() == TYP_FLOAT)
- {
- // This can only occur when we are returning a non-HFA struct
- // that is composed of a single float field.
- //
- inst_RV_RV(INS_vmov_i2f, op1Reg, op2->gtRegNum, op1->TypeGet());
- }
- else
-#endif // _TARGET_ARM_
- {
- inst_RV_RV(INS_mov, op1Reg, op2->gtRegNum, op1->TypeGet());
- }
-
- /* The value has been transferred to 'op1Reg' */
-
- regTracker.rsTrackRegCopy(op1Reg, op2->gtRegNum);
-
- if ((genRegMask(op2->gtRegNum) & regSet.rsMaskUsed) == 0)
- gcInfo.gcMarkRegSetNpt(genRegMask(op2->gtRegNum));
-
- gcInfo.gcMarkRegPtrVal(op1Reg, tree->TypeGet());
- }
- else
- {
- // First we need to remove it from the original reg set mask (or else trigger an
- // assert when we add it to the other reg set mask).
- gcInfo.gcMarkRegSetNpt(genRegMask(op1Reg));
- gcInfo.gcMarkRegPtrVal(op1Reg, tree->TypeGet());
-
- // The emitter has logic that tracks the GCness of registers and asserts if you
- // try to do bad things to a GC pointer (like lose its GCness).
-
- // An explict cast of a GC pointer to an int (which is legal if the
- // pointer is pinned) is encoded as an assignment of a GC source
- // to a integer variable. Unfortunately if the source was the last
- // use, and the source register gets reused by the destination, no
- // code gets emitted (That is where we are at right now). The emitter
- // thinks the register is a GC pointer (it did not see the cast).
- // This causes asserts, as well as bad GC info since we will continue
- // to report the register as a GC pointer even if we do arithmetic
- // with it. So force the emitter to see the change in the type
- // of variable by placing a label.
- // We only have to do this check at this point because in the
- // CAST morphing, we create a temp and assignment whenever we
- // have a cast that loses its GCness.
-
- if (varTypeGCtype(op2->TypeGet()) != varTypeGCtype(op1->TypeGet()))
- {
- void* label = getEmitter()->emitAddLabel(gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur,
- gcInfo.gcRegByrefSetCur);
- }
- }
-
- addrReg = 0;
-
- genCodeForTreeSmpOpAsg_DONE_ASSG(tree, addrReg, op1Reg, ovfl);
- goto LExit;
-
- case GT_LCL_FLD:
-
- // We only use GT_LCL_FLD for lvDoNotEnregister vars, so we don't have
- // to worry about it being enregistered.
- noway_assert(compiler->lvaTable[op1->gtLclFld.gtLclNum].lvRegister == 0);
- break;
-
- case GT_CLS_VAR:
-
- __fallthrough;
-
- case GT_IND:
- case GT_NULLCHECK:
-
- assert((op1->OperGet() == GT_CLS_VAR) || (op1->OperGet() == GT_IND));
-
- if (op1->gtFlags & GTF_IND_VOLATILE)
- {
- volat = true;
- }
-
- break;
-
- default:
- break;
- }
-
- /* Is the value being assigned a simple one? */
-
- noway_assert(op2);
- switch (op2->gtOper)
- {
- case GT_LCL_VAR:
-
- if (!genMarkLclVar(op2))
- goto SMALL_ASG;
-
- __fallthrough;
-
- case GT_REG_VAR:
-
- /* Is the target a byte/short/char value? */
-
- if (varTypeIsSmall(op1->TypeGet()))
- goto SMALL_ASG;
-
- if (tree->gtFlags & GTF_REVERSE_OPS)
- goto SMALL_ASG;
-
- /* Make the target addressable */
-
- op1 = genCodeForCommaTree(op1); // Strip away comma expressions.
-
- addrReg = genMakeAddressable(op1, needReg, RegSet::KEEP_REG, true);
-
- /* Does the write barrier helper do the assignment? */
-
- regGC = WriteBarrier(op1, op2, addrReg);
-
- // Was assignment done by the WriteBarrier
- if (regGC == RBM_NONE)
- {
-#ifdef _TARGET_ARM_
- if (volat)
- {
- // Emit a memory barrier instruction before the store
- instGen_MemoryBarrier();
- }
-#endif
-
- /* Move the value into the target */
-
- inst_TT_RV(ins_Store(op1->TypeGet()), op1, op2->gtRegVar.gtRegNum);
-
- // This is done in WriteBarrier when (regGC != RBM_NONE)
-
- /* Free up anything that was tied up by the LHS */
- genDoneAddressable(op1, addrReg, RegSet::KEEP_REG);
- }
-
- /* Free up the RHS */
- genUpdateLife(op2);
-
- /* Remember that we've also touched the op2 register */
-
- addrReg |= genRegMask(op2->gtRegVar.gtRegNum);
- break;
-
- case GT_CNS_INT:
-
- GenTreeIntConCommon* con;
- con = op2->AsIntConCommon();
- ssize_t ival;
- ival = con->IconValue();
- emitAttr size;
- size = emitTypeSize(tree->TypeGet());
-
- ins = ins_Store(op1->TypeGet());
-
- // If we are storing a constant into a local variable
- // we extend the size of the store here
- // this normally takes place in CodeGen::inst_TT_IV on x86.
- //
- if ((op1->gtOper == GT_LCL_VAR) && (size < EA_4BYTE))
- {
- unsigned varNum = op1->gtLclVarCommon.gtLclNum;
- LclVarDsc* varDsc = compiler->lvaTable + varNum;
-
- // Fix the immediate by sign extending if needed
- if (!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, regardless of
- // what the local var is typed as, so auto-promote it here
- // unless it is a field of a promoted struct
- if (!varDsc->lvIsStructField)
- {
- size = EA_SET_SIZE(size, EA_4BYTE);
- ins = ins_Store(TYP_INT);
- }
- }
-
- /* Make the target addressable */
-
- addrReg = genMakeAddressable(op1, needReg, RegSet::KEEP_REG, true);
-
-#ifdef _TARGET_ARM_
- if (volat)
- {
- // Emit a memory barrier instruction before the store
- instGen_MemoryBarrier();
- }
-#endif
-
- /* Move the value into the target */
-
- noway_assert(op1->gtOper != GT_REG_VAR);
- if (con->ImmedValNeedsReloc(compiler))
- {
- /* The constant is actually a handle that may need relocation
- applied to it. genComputeReg will do the right thing (see
- code in genCodeForTreeConst), so we'll just call it to load
- the constant into a register. */
-
- genComputeReg(op2, needReg & ~addrReg, RegSet::ANY_REG, RegSet::KEEP_REG);
- addrReg = genKeepAddressable(op1, addrReg, genRegMask(op2->gtRegNum));
- noway_assert(op2->InReg());
- inst_TT_RV(ins, op1, op2->gtRegNum);
- genReleaseReg(op2);
- }
- else
- {
- regSet.rsLockUsedReg(addrReg);
-
-#if REDUNDANT_LOAD
- bool copyIconFromReg = true;
- regNumber iconReg = REG_NA;
-
-#ifdef _TARGET_ARM_
- // Only if the constant can't be encoded in a small instruction,
- // look for another register to copy the value from. (Assumes
- // target is a small register.)
- if ((op1->InReg()) && !isRegPairType(tree->gtType) &&
- arm_Valid_Imm_For_Small_Mov(op1->gtRegNum, ival, INS_FLAGS_DONT_CARE))
- {
- copyIconFromReg = false;
- }
-#endif // _TARGET_ARM_
-
- if (copyIconFromReg)
- {
- iconReg = regTracker.rsIconIsInReg(ival);
- if (iconReg == REG_NA)
- copyIconFromReg = false;
- }
-
- if (copyIconFromReg && (isByteReg(iconReg) || (genTypeSize(tree->TypeGet()) == EA_PTRSIZE) ||
- (genTypeSize(tree->TypeGet()) == EA_4BYTE)))
- {
- /* Move the value into the target */
-
- inst_TT_RV(ins, op1, iconReg, 0, size);
- }
- else
-#endif // REDUNDANT_LOAD
- {
- inst_TT_IV(ins, op1, ival, 0, size);
- }
-
- regSet.rsUnlockUsedReg(addrReg);
- }
-
- /* Free up anything that was tied up by the LHS */
-
- genDoneAddressable(op1, addrReg, RegSet::KEEP_REG);
- break;
-
- default:
-
- SMALL_ASG:
-
- bool isWriteBarrier = false;
- regMaskTP needRegOp1 = RBM_ALLINT;
- RegSet::ExactReg mustReg = RegSet::ANY_REG; // set to RegSet::EXACT_REG for op1 and NOGC helpers
-
- /* Is the LHS more complex than the RHS? */
-
- if (tree->gtFlags & GTF_REVERSE_OPS)
- {
- /* Is the target a byte/short/char value? */
-
- if (varTypeIsSmall(op1->TypeGet()))
- {
- noway_assert(op1->gtOper != GT_LCL_VAR || (op1->gtFlags & GTF_VAR_CAST) ||
- // TODO: Why does this have to be true?
- compiler->lvaTable[op1->gtLclVarCommon.gtLclNum].lvIsStructField ||
- compiler->lvaTable[op1->gtLclVarCommon.gtLclNum].lvNormalizeOnLoad());
-
- if (op2->gtOper == GT_CAST && !op2->gtOverflow())
- {
- /* Special case: cast to small type */
-
- if (op2->CastToType() >= op1->gtType)
- {
- /* Make sure the cast operand is not > int */
-
- if (op2->CastFromType() <= TYP_INT)
- {
- /* Cast via a non-smaller type */
-
- op2 = op2->gtCast.CastOp();
- }
- }
- }
-
- if (op2->gtOper == GT_AND && op2->gtOp.gtOp2->gtOper == GT_CNS_INT)
- {
- unsigned mask;
- switch (op1->gtType)
- {
- case TYP_BYTE:
- mask = 0x000000FF;
- break;
- case TYP_SHORT:
- mask = 0x0000FFFF;
- break;
- case TYP_USHORT:
- mask = 0x0000FFFF;
- break;
- default:
- goto SIMPLE_SMALL;
- }
-
- if (unsigned(op2->gtOp.gtOp2->gtIntCon.gtIconVal) == mask)
- {
- /* Redundant AND */
-
- op2 = op2->gtOp.gtOp1;
- }
- }
-
- /* Must get the new value into a byte register */
-
- SIMPLE_SMALL:
- if (varTypeIsByte(op1->TypeGet()))
- genComputeReg(op2, RBM_BYTE_REGS, RegSet::EXACT_REG, RegSet::KEEP_REG);
- else
- goto NOT_SMALL;
- }
- else
- {
- NOT_SMALL:
- /* Generate the RHS into a register */
-
- isWriteBarrier = gcInfo.gcIsWriteBarrierAsgNode(tree);
- if (isWriteBarrier)
- {
-#if NOGC_WRITE_BARRIERS
- // Exclude the REG_WRITE_BARRIER from op2's needReg mask
- needReg = Target::exclude_WriteBarrierReg(needReg);
- mustReg = RegSet::EXACT_REG;
-#else // !NOGC_WRITE_BARRIERS
- // This code should be generic across architectures.
-
- // For the standard JIT Helper calls
- // op1 goes into REG_ARG_0 and
- // op2 goes into REG_ARG_1
- //
- needRegOp1 = RBM_ARG_0;
- needReg = RBM_ARG_1;
-#endif // !NOGC_WRITE_BARRIERS
- }
- genComputeReg(op2, needReg, mustReg, RegSet::KEEP_REG);
- }
-
- noway_assert(op2->InReg());
-
- /* Make the target addressable */
-
- op1 = genCodeForCommaTree(op1); // Strip off any comma expressions.
- addrReg = genMakeAddressable(op1, needRegOp1, RegSet::KEEP_REG, true);
-
- /* Make sure the RHS register hasn't been spilled;
- keep the register marked as "used", otherwise
- we might get the pointer lifetimes wrong.
- */
-
- if (varTypeIsByte(op1->TypeGet()))
- needReg = regSet.rsNarrowHint(RBM_BYTE_REGS, needReg);
-
- genRecoverReg(op2, needReg, RegSet::KEEP_REG);
- noway_assert(op2->InReg());
-
- /* Lock the RHS temporarily (lock only already used) */
-
- regSet.rsLockUsedReg(genRegMask(op2->gtRegNum));
-
- /* Make sure the LHS is still addressable */
-
- addrReg = genKeepAddressable(op1, addrReg);
-
- /* We can unlock (only already used ) the RHS register */
-
- regSet.rsUnlockUsedReg(genRegMask(op2->gtRegNum));
-
- /* Does the write barrier helper do the assignment? */
-
- regGC = WriteBarrier(op1, op2, addrReg);
-
- if (regGC != 0)
- {
- // Yes, assignment done by the WriteBarrier
- noway_assert(isWriteBarrier);
- }
- else
- {
-#ifdef _TARGET_ARM_
- if (volat)
- {
- // Emit a memory barrier instruction before the store
- instGen_MemoryBarrier();
- }
-#endif
-
- /* Move the value into the target */
-
- inst_TT_RV(ins_Store(op1->TypeGet()), op1, op2->gtRegNum);
- }
-
-#ifdef DEBUG
- /* Update the current liveness info */
- if (compiler->opts.varNames)
- genUpdateLife(tree);
-#endif
-
- // If op2 register is still in use, free it. (Might not be in use, if
- // a full-call write barrier was done, and the register was a caller-saved
- // register.)
- regMaskTP op2RM = genRegMask(op2->gtRegNum);
- if (op2RM & regSet.rsMaskUsed)
- regSet.rsMarkRegFree(genRegMask(op2->gtRegNum));
-
- // This is done in WriteBarrier when (regGC != 0)
- if (regGC == 0)
- {
- /* Free up anything that was tied up by the LHS */
- genDoneAddressable(op1, addrReg, RegSet::KEEP_REG);
- }
- }
- else
- {
- /* Make the target addressable */
-
- isWriteBarrier = gcInfo.gcIsWriteBarrierAsgNode(tree);
-
- if (isWriteBarrier)
- {
-#if NOGC_WRITE_BARRIERS
- /* Try to avoid RBM_TMP_0 */
- needRegOp1 = regSet.rsNarrowHint(needRegOp1, ~RBM_TMP_0);
- mustReg = RegSet::EXACT_REG; // For op2
-#else // !NOGC_WRITE_BARRIERS
- // This code should be generic across architectures.
-
- // For the standard JIT Helper calls
- // op1 goes into REG_ARG_0 and
- // op2 goes into REG_ARG_1
- //
- needRegOp1 = RBM_ARG_0;
- needReg = RBM_ARG_1;
- mustReg = RegSet::EXACT_REG; // For op2
-#endif // !NOGC_WRITE_BARRIERS
- }
-
- needRegOp1 = regSet.rsNarrowHint(needRegOp1, ~op2->gtRsvdRegs);
-
- op1 = genCodeForCommaTree(op1); // Strip away any comma expression.
-
- addrReg = genMakeAddressable(op1, needRegOp1, RegSet::KEEP_REG, true);
-
-#if CPU_HAS_BYTE_REGS
- /* Is the target a byte value? */
- if (varTypeIsByte(op1->TypeGet()))
- {
- /* Must get the new value into a byte register */
- needReg = regSet.rsNarrowHint(RBM_BYTE_REGS, needReg);
- mustReg = RegSet::EXACT_REG;
-
- if (op2->gtType >= op1->gtType)
- op2->gtFlags |= GTF_SMALL_OK;
- }
-#endif
-
-#if NOGC_WRITE_BARRIERS
- /* For WriteBarrier we can't use REG_WRITE_BARRIER */
- if (isWriteBarrier)
- needReg = Target::exclude_WriteBarrierReg(needReg);
-
- /* Also avoid using the previously computed addrReg(s) */
- bestReg = regSet.rsNarrowHint(needReg, ~addrReg);
-
- /* If we have a reg available to grab then use bestReg */
- if (bestReg & regSet.rsRegMaskCanGrab())
- needReg = bestReg;
-
- mustReg = RegSet::EXACT_REG;
-#endif
-
- /* Generate the RHS into a register */
- genComputeReg(op2, needReg, mustReg, RegSet::KEEP_REG);
- noway_assert(op2->InReg());
-
- /* Make sure the target is still addressable */
- addrReg = genKeepAddressable(op1, addrReg, genRegMask(op2->gtRegNum));
- noway_assert(op2->InReg());
-
- /* Does the write barrier helper do the assignment? */
-
- regGC = WriteBarrier(op1, op2, addrReg);
-
- if (regGC != 0)
- {
- // Yes, assignment done by the WriteBarrier
- noway_assert(isWriteBarrier);
- }
- else
- {
- assert(!isWriteBarrier);
-
-#ifdef _TARGET_ARM_
- if (volat)
- {
- // Emit a memory barrier instruction before the store
- instGen_MemoryBarrier();
- }
-#endif
-
- /* Move the value into the target */
-
- inst_TT_RV(ins_Store(op1->TypeGet()), op1, op2->gtRegNum);
- }
-
- /* The new value is no longer needed */
-
- genReleaseReg(op2);
-
-#ifdef DEBUG
- /* Update the current liveness info */
- if (compiler->opts.varNames)
- genUpdateLife(tree);
-#endif
-
- // This is done in WriteBarrier when (regGC != 0)
- if (regGC == 0)
- {
- /* Free up anything that was tied up by the LHS */
- genDoneAddressable(op1, addrReg, RegSet::KEEP_REG);
- }
- }
-
- addrReg = RBM_NONE;
- break;
- }
-
- noway_assert(addrReg != DUMMY_INIT(RBM_CORRUPT));
- genCodeForTreeSmpOpAsg_DONE_ASSG(tree, addrReg, REG_NA, ovfl);
-
-LExit:
- /* 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);
-}
-#ifdef _PREFAST_
-#pragma warning(pop)
-#endif
-
-/*****************************************************************************
- *
- * Generate code to complete the assignment operation
- */
-
-void CodeGen::genCodeForTreeSmpOpAsg_DONE_ASSG(GenTree* tree, regMaskTP addrReg, regNumber reg, bool ovfl)
-{
- const var_types treeType = tree->TypeGet();
- GenTree* op1 = tree->gtOp.gtOp1;
- GenTree* op2 = tree->gtOp.gtOp2;
- noway_assert(op2);
-
- if (op1->gtOper == GT_LCL_VAR || op1->gtOper == GT_REG_VAR)
- genUpdateLife(op1);
- genUpdateLife(tree);
-
-#if REDUNDANT_LOAD
-
- if (op1->gtOper == GT_LCL_VAR)
- regTracker.rsTrashLcl(op1->gtLclVarCommon.gtLclNum);
-
- /* Have we just assigned a value that is in a register? */
-
- if (op2->InReg() && tree->gtOper == GT_ASG)
- {
- regTracker.rsTrackRegAssign(op1, op2);
- }
-
-#endif
-
- noway_assert(addrReg != 0xDEADCAFE);
-
- gcInfo.gcMarkRegSetNpt(addrReg);
-
- if (ovfl)
- {
- noway_assert(tree->gtOper == GT_ASG_ADD || tree->gtOper == GT_ASG_SUB);
-
- /* If it is not in a register and it is a small type, then
- we must have loaded it up from memory, done the increment,
- checked for overflow, and then stored it back to memory */
-
- bool ovfCheckDone = (genTypeSize(op1->TypeGet()) < sizeof(int)) && !(op1->InReg());
-
- if (!ovfCheckDone)
- {
- // For small sizes, reg should be set as we sign/zero extend it.
-
- noway_assert(genIsValidReg(reg) || genTypeSize(treeType) == sizeof(int));
-
- /* Currently we don't morph x=x+y into x+=y in try blocks
- * if we need overflow check, as x+y may throw an exception.
- * We can do it if x is not live on entry to the catch block.
- */
- noway_assert(!compiler->compCurBB->hasTryIndex());
-
- genCheckOverflow(tree);
- }
- }
-}
-
-/*****************************************************************************
- *
- * Generate code for a special op tree
- */
-
-void CodeGen::genCodeForTreeSpecialOp(GenTree* tree, regMaskTP destReg, regMaskTP bestReg)
-{
- genTreeOps oper = tree->OperGet();
- regNumber reg = DUMMY_INIT(REG_CORRUPT);
- regMaskTP regs = regSet.rsMaskUsed;
-
- noway_assert((tree->OperKind() & (GTK_CONST | GTK_LEAF | GTK_SMPOP)) == 0);
-
- switch (oper)
- {
- case GT_CALL:
- regs = genCodeForCall(tree->AsCall(), true);
-
- /* If the result is in a register, make sure it ends up in the right place */
-
- if (regs != RBM_NONE)
- {
- genMarkTreeInReg(tree, genRegNumFromMask(regs));
- }
-
- genUpdateLife(tree);
- return;
-
- case GT_FIELD:
- NO_WAY("should not see this operator in this phase");
- break;
-
- case GT_ARR_BOUNDS_CHECK:
- {
-#ifdef FEATURE_ENABLE_NO_RANGE_CHECKS
- // MUST NEVER CHECK-IN WITH THIS ENABLED.
- // This is just for convenience in doing performance investigations and requires x86ret builds
- if (!JitConfig.JitNoRngChk())
-#endif
- genRangeCheck(tree);
- }
- return;
-
- case GT_ARR_ELEM:
- genCodeForTreeSmpOp_GT_ADDR(tree, destReg, bestReg);
- return;
-
- case GT_CMPXCHG:
- {
-#if defined(_TARGET_XARCH_)
- // cmpxchg does not have an [r/m32], imm32 encoding, so we need a register for the value operand
-
- // Since this is a "call", evaluate the operands from right to left. Don't worry about spilling
- // right now, just get the trees evaluated.
-
- // As a friendly reminder. IL args are evaluated left to right.
-
- GenTree* location = tree->gtCmpXchg.gtOpLocation; // arg1
- GenTree* value = tree->gtCmpXchg.gtOpValue; // arg2
- GenTree* comparand = tree->gtCmpXchg.gtOpComparand; // arg3
- regMaskTP addrReg;
-
- bool isAddr = genMakeIndAddrMode(location, tree, false, /* not for LEA */
- RBM_ALLINT, RegSet::KEEP_REG, &addrReg);
-
- if (!isAddr)
- {
- genCodeForTree(location, RBM_NONE, RBM_NONE);
- assert(location->InReg());
- addrReg = genRegMask(location->gtRegNum);
- regSet.rsMarkRegUsed(location);
- }
-
- // We must have a reg for the Value, but it doesn't really matter which register.
-
- // Try to avoid EAX and the address regsiter if possible.
- genComputeReg(value, regSet.rsNarrowHint(RBM_ALLINT, RBM_EAX | addrReg), RegSet::ANY_REG, RegSet::KEEP_REG);
-
-#ifdef DEBUG
- // cmpxchg uses EAX as an implicit operand to hold the comparand
- // We're going to destroy EAX in this operation, so we better not be keeping
- // anything important in it.
- if (RBM_EAX & regSet.rsMaskVars)
- {
- // We have a variable enregistered in EAX. Make sure it goes dead in this tree.
- for (unsigned varNum = 0; varNum < compiler->lvaCount; ++varNum)
- {
- const LclVarDsc& varDesc = compiler->lvaTable[varNum];
- if (!varDesc.lvIsRegCandidate())
- continue;
- if (!varDesc.lvRegister)
- continue;
- if (isFloatRegType(varDesc.lvType))
- continue;
- if (varDesc.lvRegNum != REG_EAX)
- continue;
- // We may need to check lvOtherReg.
-
- // If the variable isn't going dead during this tree, we've just trashed a local with
- // cmpxchg.
- noway_assert(genContainsVarDeath(value->gtNext, comparand->gtNext, varNum));
-
- break;
- }
- }
-#endif
- genComputeReg(comparand, RBM_EAX, RegSet::EXACT_REG, RegSet::KEEP_REG);
-
- // By this point we've evaluated everything. However the odds are that we've spilled something by
- // now. Let's recover all the registers and force them to stay.
-
- // Well, we just computed comparand, so it's still in EAX.
- noway_assert(comparand->gtRegNum == REG_EAX);
- regSet.rsLockUsedReg(RBM_EAX);
-
- // Stick it anywhere other than EAX.
- genRecoverReg(value, ~RBM_EAX, RegSet::KEEP_REG);
- reg = value->gtRegNum;
- noway_assert(reg != REG_EAX);
- regSet.rsLockUsedReg(genRegMask(reg));
-
- if (isAddr)
- {
- addrReg = genKeepAddressable(/*location*/ tree, addrReg, 0 /*avoidMask*/);
- }
- else
- {
- genRecoverReg(location, ~(RBM_EAX | genRegMask(reg)), RegSet::KEEP_REG);
- }
-
- regSet.rsUnlockUsedReg(genRegMask(reg));
- regSet.rsUnlockUsedReg(RBM_EAX);
-
- instGen(INS_lock);
- if (isAddr)
- {
- sched_AM(INS_cmpxchg, EA_4BYTE, reg, false, location, 0);
- genDoneAddressable(location, addrReg, RegSet::KEEP_REG);
- }
- else
- {
- instEmit_RM_RV(INS_cmpxchg, EA_4BYTE, location, reg, 0);
- genReleaseReg(location);
- }
-
- genReleaseReg(value);
- genReleaseReg(comparand);
-
- // EAX and the value register are both trashed at this point.
- regTracker.rsTrackRegTrash(REG_EAX);
- regTracker.rsTrackRegTrash(reg);
-
- reg = REG_EAX;
-
- genFlagsEqualToNone();
- break;
-#else // not defined(_TARGET_XARCH_)
- NYI("GT_CMPXCHG codegen");
- break;
-#endif
- }
-
- default:
-#ifdef DEBUG
- compiler->gtDispTree(tree);
-#endif
- noway_assert(!"unexpected operator");
- NO_WAY("unexpected operator");
- }
-
- noway_assert(reg != DUMMY_INIT(REG_CORRUPT));
- genCodeForTree_DONE(tree, reg);
-}
-
-/*****************************************************************************
- *
- * Generate code for the given tree. tree->gtRegNum will be set to the
- * register where the tree lives.
- *
- * If 'destReg' is non-zero, we'll do our best to compute the value into a
- * register that is in that register set.
- * Use genComputeReg() if you need the tree in a specific register.
- * Use genCompIntoFreeReg() if the register needs to be written to. Otherwise,
- * the register can only be used for read, but not for write.
- * Use genMakeAddressable() if you only need the tree to be accessible
- * using a complex addressing mode, and do not necessarily need the tree
- * materialized in a register.
- *
- * The GCness of the register will be properly set in gcInfo.gcRegGCrefSetCur/gcInfo.gcRegByrefSetCur.
- *
- * The register will not be marked as used. Use regSet.rsMarkRegUsed() if the
- * register will not be consumed right away and could possibly be spilled.
- */
-
-void CodeGen::genCodeForTree(GenTree* tree, regMaskTP destReg, regMaskTP bestReg)
-{
-#if 0
- if (compiler->verbose)
- {
- printf("Generating code for tree ");
- Compiler::printTreeID(tree);
- printf(" destReg = 0x%x bestReg = 0x%x\n", destReg, bestReg);
- }
- genStressRegs(tree);
-#endif
-
- noway_assert(tree);
- noway_assert(tree->gtOper != GT_STMT);
- assert(tree->IsNodeProperlySized());
-
- // When assigning to a enregistered local variable we receive
- // a hint that we should target the register that is used to
- // hold the enregistered local variable.
- // When receiving this hint both destReg and bestReg masks are set
- // to the register that is used by the enregistered local variable.
- //
- // However it is possible to us to have a different local variable
- // targeting the same register to become alive (and later die)
- // as we descend the expression tree.
- //
- // To handle such cases we will remove any registers that are alive from the
- // both the destReg and bestReg masks.
- //
- regMaskTP liveMask = genLiveMask(tree);
-
- // This removes any registers used to hold enregistered locals
- // from the destReg and bestReg masks.
- // After this either mask could become 0
- //
- destReg &= ~liveMask;
- bestReg &= ~liveMask;
-
- /* 'destReg' of 0 really means 'any' */
-
- destReg = regSet.rsUseIfZero(destReg, RBM_ALL(tree->TypeGet()));
-
- if (destReg != RBM_ALL(tree->TypeGet()))
- bestReg = regSet.rsUseIfZero(bestReg, destReg);
-
- // Long, float, and double have their own codegen functions
- switch (tree->TypeGet())
- {
-
- case TYP_LONG:
-#if !CPU_HAS_FP_SUPPORT
- case TYP_DOUBLE:
-#endif
- genCodeForTreeLng(tree, destReg, /*avoidReg*/ RBM_NONE);
- return;
-
-#if CPU_HAS_FP_SUPPORT
- case TYP_FLOAT:
- case TYP_DOUBLE:
-
- // For comma nodes, we'll get back here for the last node in the comma list.
- if (tree->gtOper != GT_COMMA)
- {
- genCodeForTreeFlt(tree, RBM_ALLFLOAT, RBM_ALLFLOAT & (destReg | bestReg));
- return;
- }
- break;
-#endif
-
-#ifdef DEBUG
- case TYP_UINT:
- case TYP_ULONG:
- noway_assert(!"These types are only used as markers in GT_CAST nodes");
- break;
-#endif
-
- default:
- break;
- }
-
- /* Is the value already in a register? */
-
- if (tree->InReg())
- {
- genCodeForTree_REG_VAR1(tree);
- return;
- }
-
- /* We better not have a spilled value here */
-
- noway_assert((tree->gtFlags & GTF_SPILLED) == 0);
-
- /* Figure out what kind of a node we have */
-
- unsigned kind = tree->OperKind();
-
- if (kind & GTK_CONST)
- {
- /* Handle constant nodes */
-
- genCodeForTreeConst(tree, destReg, bestReg);
- }
- else if (kind & GTK_LEAF)
- {
- /* Handle leaf nodes */
-
- genCodeForTreeLeaf(tree, destReg, bestReg);
- }
- else if (kind & GTK_SMPOP)
- {
- /* Handle 'simple' unary/binary operators */
-
- genCodeForTreeSmpOp(tree, destReg, bestReg);
- }
- else
- {
- /* Handle special operators */
-
- genCodeForTreeSpecialOp(tree, destReg, bestReg);
- }
-}
-
-/*****************************************************************************
- *
- * Generate code for all the basic blocks in the function.
- */
-
-#ifdef _PREFAST_
-#pragma warning(push)
-#pragma warning(disable : 21000) // Suppress PREFast warning about overly large function
-#endif
-void CodeGen::genCodeForBBlist()
-{
- unsigned varNum;
- LclVarDsc* varDsc;
-
- unsigned savedStkLvl;
-
-#ifdef DEBUG
- genInterruptibleUsed = true;
- unsigned stmtNum = 0;
- unsigned totalCostEx = 0;
- unsigned totalCostSz = 0;
-
- // You have to be careful if you create basic blocks from now on
- compiler->fgSafeBasicBlockCreation = false;
-
- // This stress mode is not comptible with fully interruptible GC
- if (genInterruptible && compiler->opts.compStackCheckOnCall)
- {
- compiler->opts.compStackCheckOnCall = false;
- }
-
- // This stress mode is not comptible with fully interruptible GC
- if (genInterruptible && compiler->opts.compStackCheckOnRet)
- {
- compiler->opts.compStackCheckOnRet = false;
- }
-#endif
-
- // Prepare the blocks for exception handling codegen: mark the blocks that needs labels.
- genPrepForEHCodegen();
-
- assert(!compiler->fgFirstBBScratch ||
- compiler->fgFirstBB == compiler->fgFirstBBScratch); // compiler->fgFirstBBScratch has to be first.
-
- /* Initialize the spill tracking logic */
-
- regSet.rsSpillBeg();
-
- /* Initialize the line# tracking logic */
-
- if (compiler->opts.compScopeInfo)
- {
- siInit();
- }
-
-#ifdef _TARGET_X86_
- if (compiler->compTailCallUsed)
- {
- noway_assert(isFramePointerUsed());
- regSet.rsSetRegsModified(RBM_INT_CALLEE_SAVED & ~RBM_FPBASE);
- }
-#endif
-
- if (compiler->opts.compDbgEnC)
- {
- noway_assert(isFramePointerUsed());
- regSet.rsSetRegsModified(RBM_INT_CALLEE_SAVED & ~RBM_FPBASE);
- }
-
- /* If we have any pinvoke calls, we might potentially trash everything */
-
- if (compiler->info.compCallUnmanaged)
- {
- noway_assert(isFramePointerUsed()); // Setup of Pinvoke frame currently requires an EBP style frame
- regSet.rsSetRegsModified(RBM_INT_CALLEE_SAVED & ~RBM_FPBASE);
- }
-
- /* Initialize the pointer tracking code */
-
- gcInfo.gcRegPtrSetInit();
- gcInfo.gcVarPtrSetInit();
-
- /* If any arguments live in registers, mark those regs as such */
-
- for (varNum = 0, varDsc = compiler->lvaTable; varNum < compiler->lvaCount; varNum++, varDsc++)
- {
- /* Is this variable a parameter assigned to a register? */
-
- if (!varDsc->lvIsParam || !varDsc->lvRegister)
- continue;
-
- /* Is the argument live on entry to the method? */
-
- if (!VarSetOps::IsMember(compiler, compiler->fgFirstBB->bbLiveIn, varDsc->lvVarIndex))
- continue;
-
-#if CPU_HAS_FP_SUPPORT
- /* Is this a floating-point argument? */
-
- if (varDsc->IsFloatRegType())
- continue;
-
- noway_assert(!varTypeIsFloating(varDsc->TypeGet()));
-#endif
-
- /* Mark the register as holding the variable */
-
- if (isRegPairType(varDsc->lvType))
- {
- regTracker.rsTrackRegLclVarLng(varDsc->lvRegNum, varNum, true);
-
- if (varDsc->lvOtherReg != REG_STK)
- regTracker.rsTrackRegLclVarLng(varDsc->lvOtherReg, varNum, false);
- }
- else
- {
- regTracker.rsTrackRegLclVar(varDsc->lvRegNum, varNum);
- }
- }
-
- unsigned finallyNesting = 0;
-
- // Make sure a set is allocated for compiler->compCurLife (in the long case), so we can set it to empty without
- // allocation at the start of each basic block.
- VarSetOps::AssignNoCopy(compiler, compiler->compCurLife, VarSetOps::MakeEmpty(compiler));
-
- /*-------------------------------------------------------------------------
- *
- * Walk the basic blocks and generate code for each one
- *
- */
-
- BasicBlock* block;
- BasicBlock* lblk; /* previous block */
-
- for (lblk = NULL, block = compiler->fgFirstBB; block != NULL; lblk = block, block = block->bbNext)
- {
-#ifdef DEBUG
- if (compiler->verbose)
- {
- printf("\n=============== Generating ");
- block->dspBlockHeader(compiler, true, true);
- compiler->fgDispBBLiveness(block);
- }
-#endif // DEBUG
-
- VARSET_TP liveSet(VarSetOps::UninitVal());
-
- regMaskTP gcrefRegs = 0;
- regMaskTP byrefRegs = 0;
-
- /* Does any other block jump to this point ? */
-
- if (block->bbFlags & BBF_JMP_TARGET)
- {
- /* Someone may jump here, so trash all regs */
-
- regTracker.rsTrackRegClr();
-
- genFlagsEqualToNone();
- }
- else
- {
- /* No jump, but pointers always need to get trashed for proper GC tracking */
-
- regTracker.rsTrackRegClrPtr();
- }
-
- /* No registers are used or locked on entry to a basic block */
-
- regSet.rsMaskUsed = RBM_NONE;
- regSet.rsMaskMult = RBM_NONE;
- regSet.rsMaskLock = RBM_NONE;
-
- // If we need to reserve registers such that they are not used
- // by CodeGen in this BasicBlock we do so here.
- // On the ARM when we have large frame offsets for locals we
- // will have RBM_R10 in the regSet.rsMaskResvd set,
- // additionally if a LocAlloc or alloca is used RBM_R9 is in
- // the regSet.rsMaskResvd set and we lock these registers here.
- //
- if (regSet.rsMaskResvd != RBM_NONE)
- {
- regSet.rsLockReg(regSet.rsMaskResvd);
- regSet.rsSetRegsModified(regSet.rsMaskResvd);
- }
-
- /* Figure out which registers hold variables on entry to this block */
-
- regMaskTP specialUseMask = regSet.rsMaskResvd;
-
- specialUseMask |= doubleAlignOrFramePointerUsed() ? RBM_SPBASE | RBM_FPBASE : RBM_SPBASE;
- regSet.ClearMaskVars();
- VarSetOps::ClearD(compiler, compiler->compCurLife);
- VarSetOps::Assign(compiler, liveSet, block->bbLiveIn);
-
-#if FEATURE_STACK_FP_X87
- VarSetOps::AssignNoCopy(compiler, genFPregVars,
- VarSetOps::Intersection(compiler, liveSet, compiler->optAllFPregVars));
- genFPregCnt = VarSetOps::Count(compiler, genFPregVars);
- genFPdeadRegCnt = 0;
-#endif
- gcInfo.gcResetForBB();
-
- genUpdateLife(liveSet); // This updates regSet.rsMaskVars with bits from any enregistered LclVars
-#if FEATURE_STACK_FP_X87
- VarSetOps::IntersectionD(compiler, liveSet, compiler->optAllNonFPvars);
-#endif
-
- // We should never enregister variables in any of the specialUseMask registers
- noway_assert((specialUseMask & regSet.rsMaskVars) == 0);
-
- VarSetOps::Iter iter(compiler, liveSet);
- unsigned varIndex = 0;
- while (iter.NextElem(&varIndex))
- {
- varNum = compiler->lvaTrackedToVarNum[varIndex];
- varDsc = compiler->lvaTable + varNum;
- assert(varDsc->lvTracked);
- /* Ignore the variable if it's not not in a reg */
-
- if (!varDsc->lvRegister)
- continue;
- if (isFloatRegType(varDsc->lvType))
- continue;
-
- /* Get hold of the index and the bitmask for the variable */
- regNumber regNum = varDsc->lvRegNum;
- regMaskTP regMask = genRegMask(regNum);
-
- regSet.AddMaskVars(regMask);
-
- if (varDsc->lvType == TYP_REF)
- gcrefRegs |= regMask;
- else if (varDsc->lvType == TYP_BYREF)
- byrefRegs |= regMask;
-
- /* Mark the register holding the variable as such */
-
- if (varTypeIsMultiReg(varDsc))
- {
- regTracker.rsTrackRegLclVarLng(regNum, varNum, true);
- if (varDsc->lvOtherReg != REG_STK)
- {
- regTracker.rsTrackRegLclVarLng(varDsc->lvOtherReg, varNum, false);
- regMask |= genRegMask(varDsc->lvOtherReg);
- }
- }
- else
- {
- regTracker.rsTrackRegLclVar(regNum, varNum);
- }
- }
-
- gcInfo.gcPtrArgCnt = 0;
-
-#if FEATURE_STACK_FP_X87
-
- regSet.rsMaskUsedFloat = regSet.rsMaskRegVarFloat = regSet.rsMaskLockedFloat = RBM_NONE;
-
- memset(regSet.genUsedRegsFloat, 0, sizeof(regSet.genUsedRegsFloat));
- memset(regSet.genRegVarsFloat, 0, sizeof(regSet.genRegVarsFloat));
-
- // Setup fp state on block entry
- genSetupStateStackFP(block);
-
-#ifdef DEBUG
- if (compiler->verbose)
- {
- JitDumpFPState();
- }
-#endif // DEBUG
-#endif // FEATURE_STACK_FP_X87
-
- /* Make sure we keep track of what pointers are live */
-
- noway_assert((gcrefRegs & byrefRegs) == 0); // Something can't be both a gcref and a byref
- gcInfo.gcRegGCrefSetCur = gcrefRegs;
- gcInfo.gcRegByrefSetCur = byrefRegs;
-
- /* Blocks with handlerGetsXcptnObj()==true use GT_CATCH_ARG to
- represent the exception object (TYP_REF).
- We mark REG_EXCEPTION_OBJECT as holding a GC object on entry
- to the block, it will be the first thing evaluated
- (thanks to GTF_ORDER_SIDEEFF).
- */
-
- if (handlerGetsXcptnObj(block->bbCatchTyp))
- {
- GenTree* firstStmt = block->FirstNonPhiDef();
- if (firstStmt != NULL)
- {
- GenTree* firstTree = firstStmt->gtStmt.gtStmtExpr;
- if (compiler->gtHasCatchArg(firstTree))
- {
- gcInfo.gcRegGCrefSetCur |= RBM_EXCEPTION_OBJECT;
- }
- }
- }
-
- /* Start a new code output block */
- CLANG_FORMAT_COMMENT_ANCHOR;
-
-#if FEATURE_EH_FUNCLETS
-#if defined(_TARGET_ARM_)
- genInsertNopForUnwinder(block);
-#endif // defined(_TARGET_ARM_)
-
- genUpdateCurrentFunclet(block);
-#endif // FEATURE_EH_FUNCLETS
-
-#ifdef _TARGET_XARCH_
- if (genAlignLoops && block->bbFlags & BBF_LOOP_HEAD)
- {
- getEmitter()->emitLoopAlign();
- }
-#endif
-
-#ifdef DEBUG
- if (compiler->opts.dspCode)
- printf("\n L_M%03u_BB%02u:\n", Compiler::s_compMethodsCount, block->bbNum);
-#endif
-
- block->bbEmitCookie = NULL;
-
- if (block->bbFlags & (BBF_JMP_TARGET | BBF_HAS_LABEL))
- {
- /* Mark a label and update the current set of live GC refs */
-
- block->bbEmitCookie =
- getEmitter()->emitAddLabel(gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur,
-#if FEATURE_EH_FUNCLETS && defined(_TARGET_ARM_)
- /*isFinally*/ block->bbFlags & BBF_FINALLY_TARGET
-#else
- FALSE
-#endif
- );
- }
-
- if (block == compiler->fgFirstColdBlock)
- {
-#ifdef DEBUG
- if (compiler->verbose)
- {
- printf("\nThis is the start of the cold region of the method\n");
- }
-#endif
- // We should never have a block that falls through into the Cold section
- noway_assert(!lblk->bbFallsThrough());
-
- // We require the block that starts the Cold section to have a label
- noway_assert(block->bbEmitCookie);
- getEmitter()->emitSetFirstColdIGCookie(block->bbEmitCookie);
- }
-
- /* Both stacks are always empty on entry to a basic block */
-
- SetStackLevel(0);
-#if FEATURE_STACK_FP_X87
- genResetFPstkLevel();
-#endif // FEATURE_STACK_FP_X87
-
- genAdjustStackLevel(block);
-
- savedStkLvl = genStackLevel;
-
- /* Tell everyone which basic block we're working on */
-
- compiler->compCurBB = block;
-
- siBeginBlock(block);
-
- // BBF_INTERNAL blocks don't correspond to any single IL instruction.
- if (compiler->opts.compDbgInfo && (block->bbFlags & BBF_INTERNAL) && block != compiler->fgFirstBB)
- genIPmappingAdd((IL_OFFSETX)ICorDebugInfo::NO_MAPPING, true);
-
- bool firstMapping = true;
-
- /*---------------------------------------------------------------------
- *
- * Generate code for each statement-tree in the block
- *
- */
- CLANG_FORMAT_COMMENT_ANCHOR;
-
-#if FEATURE_EH_FUNCLETS
- if (block->bbFlags & BBF_FUNCLET_BEG)
- {
- genReserveFuncletProlog(block);
- }
-#endif // FEATURE_EH_FUNCLETS
-
- for (GenTree* stmt = block->FirstNonPhiDef(); stmt; stmt = stmt->gtNext)
- {
- noway_assert(stmt->gtOper == GT_STMT);
-
- /* Do we have a new IL-offset ? */
-
- if (stmt->gtStmt.gtStmtILoffsx != BAD_IL_OFFSET)
- {
- /* Create and append a new IP-mapping entry */
- genIPmappingAdd(stmt->gtStmt.gtStmt.gtStmtILoffsx, firstMapping);
- firstMapping = false;
- }
-
-#ifdef DEBUG
- if (stmt->gtStmt.gtStmtLastILoffs != BAD_IL_OFFSET)
- {
- noway_assert(stmt->gtStmt.gtStmtLastILoffs <= compiler->info.compILCodeSize);
- if (compiler->opts.dspCode && compiler->opts.dspInstrs)
- {
- while (genCurDispOffset <= stmt->gtStmt.gtStmtLastILoffs)
- {
- genCurDispOffset += dumpSingleInstr(compiler->info.compCode, genCurDispOffset, "> ");
- }
- }
- }
-#endif // DEBUG
-
- /* Get hold of the statement tree */
- GenTree* tree = stmt->gtStmt.gtStmtExpr;
-
-#ifdef DEBUG
- stmtNum++;
- if (compiler->verbose)
- {
- printf("\nGenerating BB%02u, stmt %u\t\t", block->bbNum, stmtNum);
- printf("Holding variables: ");
- dspRegMask(regSet.rsMaskVars);
- printf("\n\n");
- compiler->gtDispTree(compiler->opts.compDbgInfo ? stmt : tree);
- printf("\n");
-#if FEATURE_STACK_FP_X87
- JitDumpFPState();
-#endif
-
- printf("Execution Order:\n");
- for (GenTree* treeNode = stmt->gtStmt.gtStmtList; treeNode != NULL; treeNode = treeNode->gtNext)
- {
- compiler->gtDispTree(treeNode, 0, NULL, true);
- }
- printf("\n");
- }
- totalCostEx += (stmt->gtCostEx * block->getBBWeight(compiler));
- totalCostSz += stmt->gtCostSz;
-#endif // DEBUG
-
- compiler->compCurStmt = stmt;
-
- compiler->compCurLifeTree = NULL;
- switch (tree->gtOper)
- {
- case GT_CALL:
- // Managed Retval under managed debugger - we need to make sure that the returned ref-type is
- // reported as alive even though not used within the caller for managed debugger sake. So
- // consider the return value of the method as used if generating debuggable code.
- genCodeForCall(tree->AsCall(), compiler->opts.MinOpts() || compiler->opts.compDbgCode);
- genUpdateLife(tree);
- gcInfo.gcMarkRegSetNpt(RBM_INTRET);
- break;
-
- case GT_IND:
- case GT_NULLCHECK:
-
- // Just do the side effects
- genEvalSideEffects(tree);
- break;
-
- default:
- /* Generate code for the tree */
-
- genCodeForTree(tree, 0);
- break;
- }
-
- regSet.rsSpillChk();
-
- /* The value of the tree isn't used, unless it's a return stmt */
-
- if (tree->gtOper != GT_RETURN)
- gcInfo.gcMarkRegPtrVal(tree);
-
-#if FEATURE_STACK_FP_X87
- genEndOfStatement();
-#endif
-
-#ifdef DEBUG
- /* Make sure we didn't bungle pointer register tracking */
-
- regMaskTP ptrRegs = (gcInfo.gcRegGCrefSetCur | gcInfo.gcRegByrefSetCur);
- regMaskTP nonVarPtrRegs = ptrRegs & ~regSet.rsMaskVars;
-
- // If return is a GC-type, clear it. Note that if a common
- // epilog is generated (compiler->genReturnBB) it has a void return
- // even though we might return a ref. We can't use the compRetType
- // as the determiner because something we are tracking as a byref
- // might be used as a return value of a int function (which is legal)
- if (tree->gtOper == GT_RETURN && (varTypeIsGC(compiler->info.compRetType) ||
- (tree->gtOp.gtOp1 != 0 && varTypeIsGC(tree->gtOp.gtOp1->TypeGet()))))
- {
- nonVarPtrRegs &= ~RBM_INTRET;
- }
-
- // When profiling, the first statement in a catch block will be the
- // harmless "inc" instruction (does not interfere with the exception
- // object).
-
- if (compiler->opts.jitFlags->IsSet(JitFlags::JIT_FLAG_BBINSTR) && (stmt == block->bbTreeList) &&
- (block->bbCatchTyp && handlerGetsXcptnObj(block->bbCatchTyp)))
- {
- nonVarPtrRegs &= ~RBM_EXCEPTION_OBJECT;
- }
-
- if (nonVarPtrRegs)
- {
- printf("Regset after tree=");
- Compiler::printTreeID(tree);
- printf(" BB%02u gcr=", block->bbNum);
- printRegMaskInt(gcInfo.gcRegGCrefSetCur & ~regSet.rsMaskVars);
- compiler->getEmitter()->emitDispRegSet(gcInfo.gcRegGCrefSetCur & ~regSet.rsMaskVars);
- printf(", byr=");
- printRegMaskInt(gcInfo.gcRegByrefSetCur & ~regSet.rsMaskVars);
- compiler->getEmitter()->emitDispRegSet(gcInfo.gcRegByrefSetCur & ~regSet.rsMaskVars);
- printf(", regVars=");
- printRegMaskInt(regSet.rsMaskVars);
- compiler->getEmitter()->emitDispRegSet(regSet.rsMaskVars);
- printf("\n");
- }
-
- noway_assert(nonVarPtrRegs == 0);
-#endif // DEBUG
-
- noway_assert(stmt->gtOper == GT_STMT);
-
- genEnsureCodeEmitted(stmt->gtStmt.gtStmtILoffsx);
-
- } //-------- END-FOR each statement-tree of the current block ---------
-
- if (compiler->opts.compScopeInfo && (compiler->info.compVarScopesCount > 0))
- {
- siEndBlock(block);
-
- /* Is this the last block, and are there any open scopes left ? */
-
- bool isLastBlockProcessed = (block->bbNext == NULL);
- if (block->isBBCallAlwaysPair())
- {
- isLastBlockProcessed = (block->bbNext->bbNext == NULL);
- }
-
- if (isLastBlockProcessed && siOpenScopeList.scNext)
- {
- /* This assert no longer holds, because we may insert a throw
- block to demarcate the end of a try or finally region when they
- are at the end of the method. It would be nice if we could fix
- our code so that this throw block will no longer be necessary. */
-
- // noway_assert(block->bbCodeOffsEnd != compiler->info.compILCodeSize);
-
- siCloseAllOpenScopes();
- }
- }
-
- SubtractStackLevel(savedStkLvl);
-
- gcInfo.gcMarkRegSetNpt(gcrefRegs | byrefRegs);
-
- if (!VarSetOps::Equal(compiler, compiler->compCurLife, block->bbLiveOut))
- compiler->genChangeLife(block->bbLiveOut);
-
- /* Both stacks should always be empty on exit from a basic block */
-
- noway_assert(genStackLevel == 0);
-#if FEATURE_STACK_FP_X87
- noway_assert(genGetFPstkLevel() == 0);
-
- // Do the FPState matching that may have to be done
- genCodeForEndBlockTransitionStackFP(block);
-#endif
-
- noway_assert(genFullPtrRegMap == false || gcInfo.gcPtrArgCnt == 0);
-
- /* Do we need to generate a jump or return? */
-
- switch (block->bbJumpKind)
- {
- case BBJ_ALWAYS:
- inst_JMP(EJ_jmp, block->bbJumpDest);
- break;
-
- case BBJ_RETURN:
- genExitCode(block);
- break;
-
- case BBJ_THROW:
- // If we have a throw at the end of a function or funclet, we need to emit another instruction
- // afterwards to help the OS unwinder determine the correct context during unwind.
- // We insert an unexecuted breakpoint instruction in several situations
- // following a throw instruction:
- // 1. If the throw is the last instruction of the function or funclet. This helps
- // the OS unwinder determine the correct context during an unwind from the
- // thrown exception.
- // 2. If this is this is the last block of the hot section.
- // 3. If the subsequent block is a special throw block.
- if ((block->bbNext == NULL)
-#if FEATURE_EH_FUNCLETS
- || (block->bbNext->bbFlags & BBF_FUNCLET_BEG)
-#endif // FEATURE_EH_FUNCLETS
- || (!isFramePointerUsed() && compiler->fgIsThrowHlpBlk(block->bbNext)) ||
- block->bbNext == compiler->fgFirstColdBlock)
- {
- instGen(INS_BREAKPOINT); // This should never get executed
- }
-
- break;
-
- case BBJ_CALLFINALLY:
-
-#if defined(_TARGET_X86_)
-
- /* If we are about to invoke a finally locally from a try block,
- we have to set the hidden slot corresponding to the finally's
- nesting level. When invoked in response to an exception, the
- EE usually does it.
-
- We must have : BBJ_CALLFINALLY followed by a BBJ_ALWAYS.
-
- This code depends on this order not being messed up.
- We will emit :
- mov [ebp-(n+1)],0
- mov [ebp- n ],0xFC
- push &step
- jmp finallyBlock
-
- step: mov [ebp- n ],0
- jmp leaveTarget
- leaveTarget:
- */
-
- noway_assert(isFramePointerUsed());
-
- // Get the nesting level which contains the finally
- compiler->fgGetNestingLevel(block, &finallyNesting);
-
- // The last slot is reserved for ICodeManager::FixContext(ppEndRegion)
- unsigned filterEndOffsetSlotOffs;
- filterEndOffsetSlotOffs =
- (unsigned)(compiler->lvaLclSize(compiler->lvaShadowSPslotsVar) - TARGET_POINTER_SIZE);
-
- unsigned curNestingSlotOffs;
- curNestingSlotOffs = (unsigned)(filterEndOffsetSlotOffs - ((finallyNesting + 1) * TARGET_POINTER_SIZE));
-
- // Zero out the slot for the next nesting level
- instGen_Store_Imm_Into_Lcl(TYP_I_IMPL, EA_PTRSIZE, 0, compiler->lvaShadowSPslotsVar,
- curNestingSlotOffs - TARGET_POINTER_SIZE);
-
- instGen_Store_Imm_Into_Lcl(TYP_I_IMPL, EA_PTRSIZE, LCL_FINALLY_MARK, compiler->lvaShadowSPslotsVar,
- curNestingSlotOffs);
-
- // Now push the address of where the finally funclet should
- // return to directly.
- if (!(block->bbFlags & BBF_RETLESS_CALL))
- {
- assert(block->isBBCallAlwaysPair());
- getEmitter()->emitIns_J(INS_push_hide, block->bbNext->bbJumpDest);
- }
- else
- {
- // EE expects a DWORD, so we give him 0
- inst_IV(INS_push_hide, 0);
- }
-
- // Jump to the finally BB
- inst_JMP(EJ_jmp, block->bbJumpDest);
-
-#elif defined(_TARGET_ARM_)
-
- // Now set REG_LR to the address of where the finally funclet should
- // return to directly.
-
- BasicBlock* bbFinallyRet;
- bbFinallyRet = NULL;
-
- // We don't have retless calls, since we use the BBJ_ALWAYS to point at a NOP pad where
- // we would have otherwise created retless calls.
- assert(block->isBBCallAlwaysPair());
-
- assert(block->bbNext != NULL);
- assert(block->bbNext->bbJumpKind == BBJ_ALWAYS);
- assert(block->bbNext->bbJumpDest != NULL);
- assert(block->bbNext->bbJumpDest->bbFlags & BBF_FINALLY_TARGET);
-
- bbFinallyRet = block->bbNext->bbJumpDest;
- bbFinallyRet->bbFlags |= BBF_JMP_TARGET;
-
- // Load the address where the finally funclet should return into LR.
- // The funclet prolog/epilog will do "push {lr}" / "pop {pc}" to do
- // the return.
- genMov32RelocatableDisplacement(bbFinallyRet, REG_LR);
- regTracker.rsTrackRegTrash(REG_LR);
-
- // Jump to the finally BB
- inst_JMP(EJ_jmp, block->bbJumpDest);
-#else
- NYI("TARGET");
-#endif
-
- // The BBJ_ALWAYS is used because the BBJ_CALLFINALLY can't point to the
- // jump target using bbJumpDest - that is already used to point
- // to the finally block. So just skip past the BBJ_ALWAYS unless the
- // block is RETLESS.
- if (!(block->bbFlags & BBF_RETLESS_CALL))
- {
- assert(block->isBBCallAlwaysPair());
-
- lblk = block;
- block = block->bbNext;
- }
- break;
-
-#ifdef _TARGET_ARM_
-
- case BBJ_EHCATCHRET:
- // set r0 to the address the VM should return to after the catch
- genMov32RelocatableDisplacement(block->bbJumpDest, REG_R0);
- regTracker.rsTrackRegTrash(REG_R0);
-
- __fallthrough;
-
- case BBJ_EHFINALLYRET:
- case BBJ_EHFILTERRET:
- genReserveFuncletEpilog(block);
- break;
-
-#else // _TARGET_ARM_
-
- case BBJ_EHFINALLYRET:
- case BBJ_EHFILTERRET:
- case BBJ_EHCATCHRET:
- break;
-
-#endif // _TARGET_ARM_
-
- case BBJ_NONE:
- case BBJ_COND:
- case BBJ_SWITCH:
- break;
-
- default:
- noway_assert(!"Unexpected bbJumpKind");
- break;
- }
-
-#ifdef DEBUG
- compiler->compCurBB = 0;
-#endif
-
- } //------------------ END-FOR each block of the method -------------------
-
- /* Nothing is live at this point */
- genUpdateLife(VarSetOps::MakeEmpty(compiler));
-
- /* Finalize the spill tracking logic */
-
- regSet.rsSpillEnd();
-
- /* Finalize the temp tracking logic */
-
- compiler->tmpEnd();
-
-#ifdef DEBUG
- if (compiler->verbose)
- {
- printf("\n# ");
- printf("totalCostEx = %6d, totalCostSz = %5d ", totalCostEx, totalCostSz);
- printf("%s\n", compiler->info.compFullName);
- }
-#endif
-}
-#ifdef _PREFAST_
-#pragma warning(pop)
-#endif
-
-/*****************************************************************************
- *
- * Generate code for a long operation.
- * needReg is a recommendation of which registers to use for the tree.
- * For partially enregistered longs, the tree will be marked as in a register
- * without loading the stack part into a register. Note that only leaf
- * nodes (or if gtEffectiveVal() == leaf node) may be marked as partially
- * enregistered so that we can know the memory location of the other half.
- */
-
-#ifdef _PREFAST_
-#pragma warning(push)
-#pragma warning(disable : 21000) // Suppress PREFast warning about overly large function
-#endif
-void CodeGen::genCodeForTreeLng(GenTree* tree, regMaskTP needReg, regMaskTP avoidReg)
-{
- genTreeOps oper;
- unsigned kind;
-
- regPairNo regPair = DUMMY_INIT(REG_PAIR_CORRUPT);
- regMaskTP addrReg;
- regNumber regLo;
- regNumber regHi;
-
- noway_assert(tree);
- noway_assert(tree->gtOper != GT_STMT);
- noway_assert(genActualType(tree->gtType) == TYP_LONG);
-
- /* Figure out what kind of a node we have */
-
- oper = tree->OperGet();
- kind = tree->OperKind();
-
- if (tree->InReg())
- {
- REG_VAR_LONG:
- regPair = tree->gtRegPair;
-
- gcInfo.gcMarkRegSetNpt(genRegPairMask(regPair));
-
- goto DONE;
- }
-
- /* Is this a constant node? */
-
- if (kind & GTK_CONST)
- {
- __int64 lval;
-
- /* Pick a register pair for the value */
-
- regPair = regSet.rsPickRegPair(needReg);
-
- /* Load the value into the registers */
- CLANG_FORMAT_COMMENT_ANCHOR;
-
-#if !CPU_HAS_FP_SUPPORT
- if (oper == GT_CNS_DBL)
- {
- noway_assert(sizeof(__int64) == sizeof(double));
-
- noway_assert(sizeof(tree->gtLngCon.gtLconVal) == sizeof(tree->gtDblCon.gtDconVal));
-
- lval = *(__int64*)(&tree->gtDblCon.gtDconVal);
- }
- else
-#endif
- {
- noway_assert(oper == GT_CNS_LNG);
-
- lval = tree->gtLngCon.gtLconVal;
- }
-
- genSetRegToIcon(genRegPairLo(regPair), int(lval));
- genSetRegToIcon(genRegPairHi(regPair), int(lval >> 32));
- goto DONE;
- }
-
- /* Is this a leaf node? */
-
- if (kind & GTK_LEAF)
- {
- switch (oper)
- {
- case GT_LCL_VAR:
-
-#if REDUNDANT_LOAD
-
- /* This case has to consider the case in which an int64 LCL_VAR
- * may both be enregistered and also have a cached copy of itself
- * in a different set of registers.
- * We want to return the registers that have the most in common
- * with the needReg mask
- */
-
- /* Does the var have a copy of itself in the cached registers?
- * And are these cached registers both free?
- * If so use these registers if they match any needReg.
- */
-
- regPair = regTracker.rsLclIsInRegPair(tree->gtLclVarCommon.gtLclNum);
-
- if ((regPair != REG_PAIR_NONE) && ((regSet.rsRegMaskFree() & needReg) == needReg) &&
- ((genRegPairMask(regPair) & needReg) != RBM_NONE))
- {
- goto DONE;
- }
-
- /* Does the variable live in a register?
- * If so use these registers.
- */
- if (genMarkLclVar(tree))
- goto REG_VAR_LONG;
-
- /* If tree is not an enregistered variable then
- * be sure to use any cached register that contain
- * a copy of this local variable
- */
- if (regPair != REG_PAIR_NONE)
- {
- goto DONE;
- }
-#endif
- goto MEM_LEAF;
-
- case GT_LCL_FLD:
-
- // We only use GT_LCL_FLD for lvDoNotEnregister vars, so we don't have
- // to worry about it being enregistered.
- noway_assert(compiler->lvaTable[tree->gtLclFld.gtLclNum].lvRegister == 0);
- goto MEM_LEAF;
-
- case GT_CLS_VAR:
- MEM_LEAF:
-
- /* Pick a register pair for the value */
-
- regPair = regSet.rsPickRegPair(needReg);
-
- /* Load the value into the registers */
-
- instruction loadIns;
-
- loadIns = ins_Load(TYP_INT); // INS_ldr
- regLo = genRegPairLo(regPair);
- regHi = genRegPairHi(regPair);
-
-#if CPU_LOAD_STORE_ARCH
- {
- regNumber regAddr = regSet.rsGrabReg(RBM_ALLINT);
- inst_RV_TT(INS_lea, regAddr, tree, 0);
- regTracker.rsTrackRegTrash(regAddr);
-
- if (regLo != regAddr)
- {
- // assert(regLo != regAddr); // forced by if statement
- getEmitter()->emitIns_R_R_I(loadIns, EA_4BYTE, regLo, regAddr, 0);
- getEmitter()->emitIns_R_R_I(loadIns, EA_4BYTE, regHi, regAddr, 4);
- }
- else
- {
- // assert(regHi != regAddr); // implied by regpair property and the if statement
- getEmitter()->emitIns_R_R_I(loadIns, EA_4BYTE, regHi, regAddr, 4);
- getEmitter()->emitIns_R_R_I(loadIns, EA_4BYTE, regLo, regAddr, 0);
- }
- }
-#else
- inst_RV_TT(loadIns, regLo, tree, 0);
- inst_RV_TT(loadIns, regHi, tree, 4);
-#endif
-
-#ifdef _TARGET_ARM_
- if ((oper == GT_CLS_VAR) && (tree->gtFlags & GTF_IND_VOLATILE))
- {
- // Emit a memory barrier instruction after the load
- instGen_MemoryBarrier();
- }
-#endif
-
- regTracker.rsTrackRegTrash(regLo);
- regTracker.rsTrackRegTrash(regHi);
-
- goto DONE;
-
- default:
-#ifdef DEBUG
- compiler->gtDispTree(tree);
-#endif
- noway_assert(!"unexpected leaf");
- }
- }
-
- /* Is it a 'simple' unary/binary operator? */
-
- if (kind & GTK_SMPOP)
- {
- instruction insLo;
- instruction insHi;
- bool doLo;
- bool doHi;
- bool setCarry = false;
- int helper;
-
- GenTree* op1 = tree->gtOp.gtOp1;
- GenTree* op2 = tree->gtGetOp2IfPresent();
-
- switch (oper)
- {
- case GT_ASG:
- {
- unsigned lclVarNum = compiler->lvaCount;
- unsigned lclVarILoffs = DUMMY_INIT(0);
-
- /* Is the target a local ? */
-
- if (op1->gtOper == GT_LCL_VAR)
- {
- unsigned varNum = op1->gtLclVarCommon.gtLclNum;
- LclVarDsc* varDsc;
-
- noway_assert(varNum < compiler->lvaCount);
- varDsc = compiler->lvaTable + varNum;
-
- // No dead stores, (with min opts we may have dead stores)
- noway_assert(!varDsc->lvTracked || compiler->opts.MinOpts() || !(op1->gtFlags & GTF_VAR_DEATH));
-
- /* 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 codegen of the assignment.
- */
- if (compiler->opts.compScopeInfo && !compiler->opts.compDbgCode &&
- (compiler->info.compVarScopesCount > 0))
- {
- lclVarNum = varNum;
- lclVarILoffs = op1->gtLclVar.gtLclILoffs;
- }
-
- /* Has the variable been assigned to a register (pair) ? */
-
- if (genMarkLclVar(op1))
- {
- noway_assert(op1->InReg());
- regPair = op1->gtRegPair;
- regLo = genRegPairLo(regPair);
- regHi = genRegPairHi(regPair);
- noway_assert(regLo != regHi);
-
- /* Is the value being assigned a constant? */
-
- if (op2->gtOper == GT_CNS_LNG)
- {
- /* Move the value into the target */
-
- genMakeRegPairAvailable(regPair);
-
- instruction ins;
- if (regLo == REG_STK)
- {
- ins = ins_Store(TYP_INT);
- }
- else
- {
- // Always do the stack first (in case it grabs a register it can't
- // clobber regLo this way)
- if (regHi == REG_STK)
- {
- inst_TT_IV(ins_Store(TYP_INT), op1, (int)(op2->gtLngCon.gtLconVal >> 32), 4);
- }
- ins = INS_mov;
- }
- inst_TT_IV(ins, op1, (int)(op2->gtLngCon.gtLconVal), 0);
-
- // The REG_STK case has already been handled
- if (regHi != REG_STK)
- {
- ins = INS_mov;
- inst_TT_IV(ins, op1, (int)(op2->gtLngCon.gtLconVal >> 32), 4);
- }
-
- goto DONE_ASSG_REGS;
- }
-
- /* Compute the RHS into desired register pair */
-
- if (regHi != REG_STK)
- {
- genComputeRegPair(op2, regPair, avoidReg, RegSet::KEEP_REG);
- noway_assert(op2->InReg());
- noway_assert(op2->gtRegPair == regPair);
- }
- else
- {
- regPairNo curPair;
- regNumber curLo;
- regNumber curHi;
-
- genComputeRegPair(op2, REG_PAIR_NONE, avoidReg, RegSet::KEEP_REG);
-
- noway_assert(op2->InReg());
-
- curPair = op2->gtRegPair;
- curLo = genRegPairLo(curPair);
- curHi = genRegPairHi(curPair);
-
- /* move high first, target is on stack */
- inst_TT_RV(ins_Store(TYP_INT), op1, curHi, 4);
-
- if (regLo != curLo)
- {
- if ((regSet.rsMaskUsed & genRegMask(regLo)) && (regLo != curHi))
- regSet.rsSpillReg(regLo);
- inst_RV_RV(INS_mov, regLo, curLo, TYP_LONG);
- regTracker.rsTrackRegCopy(regLo, curLo);
- }
- }
-
- genReleaseRegPair(op2);
- goto DONE_ASSG_REGS;
- }
- }
-
- /* Is the value being assigned a constant? */
-
- if (op2->gtOper == GT_CNS_LNG)
- {
- /* Make the target addressable */
-
- addrReg = genMakeAddressable(op1, needReg, RegSet::KEEP_REG);
-
- /* Move the value into the target */
-
- inst_TT_IV(ins_Store(TYP_INT), op1, (int)(op2->gtLngCon.gtLconVal), 0);
- inst_TT_IV(ins_Store(TYP_INT), op1, (int)(op2->gtLngCon.gtLconVal >> 32), 4);
-
- genDoneAddressable(op1, addrReg, RegSet::KEEP_REG);
-
- goto LAsgExit;
- }
-
-#if 0
- /* Catch a case where can avoid generating op reg, mem. Better pairing
- * from
- * mov regHi, mem
- * op regHi, reg
- *
- * To avoid problems with order of evaluation, only do this if op2 is
- * a non-enregistered local variable
- */
-
- if (GenTree::OperIsCommutative(oper) &&
- op1->gtOper == GT_LCL_VAR &&
- op2->gtOper == GT_LCL_VAR)
- {
- regPair = regTracker.rsLclIsInRegPair(op2->gtLclVarCommon.gtLclNum);
-
- /* Is op2 a non-enregistered local variable? */
- if (regPair == REG_PAIR_NONE)
- {
- regPair = regTracker.rsLclIsInRegPair(op1->gtLclVarCommon.gtLclNum);
-
- /* Is op1 an enregistered local variable? */
- if (regPair != REG_PAIR_NONE)
- {
- /* Swap the operands */
- GenTree* op = op1;
- op1 = op2;
- op2 = op;
- }
- }
- }
-#endif
-
- /* Eliminate worthless assignment "lcl = lcl" */
-
- if (op2->gtOper == GT_LCL_VAR && op1->gtOper == GT_LCL_VAR &&
- op2->gtLclVarCommon.gtLclNum == op1->gtLclVarCommon.gtLclNum)
- {
- genUpdateLife(op2);
- goto LAsgExit;
- }
-
- if (op2->gtOper == GT_CAST && TYP_ULONG == op2->CastToType() && op2->CastFromType() <= TYP_INT &&
- // op1,op2 need to be materialized in the correct order.
- (tree->gtFlags & GTF_REVERSE_OPS))
- {
- /* Generate the small RHS into a register pair */
-
- GenTree* smallOpr = op2->gtOp.gtOp1;
-
- genComputeReg(smallOpr, 0, RegSet::ANY_REG, RegSet::KEEP_REG);
-
- /* Make the target addressable */
-
- addrReg = genMakeAddressable(op1, 0, RegSet::KEEP_REG, true);
-
- /* Make sure everything is still addressable */
-
- genRecoverReg(smallOpr, 0, RegSet::KEEP_REG);
- noway_assert(smallOpr->InReg());
- regHi = smallOpr->gtRegNum;
- addrReg = genKeepAddressable(op1, addrReg, genRegMask(regHi));
-
- // conv.ovf.u8 could overflow if the original number was negative
- if (op2->gtOverflow())
- {
- noway_assert((op2->gtFlags & GTF_UNSIGNED) ==
- 0); // conv.ovf.u8.un should be bashed to conv.u8.un
- instGen_Compare_Reg_To_Zero(EA_4BYTE, regHi); // set flags
- emitJumpKind jmpLTS = genJumpKindForOper(GT_LT, CK_SIGNED);
- genJumpToThrowHlpBlk(jmpLTS, SCK_OVERFLOW);
- }
-
- /* Move the value into the target */
-
- inst_TT_RV(ins_Store(TYP_INT), op1, regHi, 0);
- inst_TT_IV(ins_Store(TYP_INT), op1, 0, 4); // Store 0 in hi-word
-
- /* Free up anything that was tied up by either side */
-
- genDoneAddressable(op1, addrReg, RegSet::KEEP_REG);
- genReleaseReg(smallOpr);
-
-#if REDUNDANT_LOAD
- if (op1->gtOper == GT_LCL_VAR)
- {
- /* clear this local from reg table */
- regTracker.rsTrashLclLong(op1->gtLclVarCommon.gtLclNum);
-
- /* mark RHS registers as containing the local var */
- regTracker.rsTrackRegLclVarLng(regHi, op1->gtLclVarCommon.gtLclNum, true);
- }
-#endif
- goto LAsgExit;
- }
-
- /* Is the LHS more complex than the RHS? */
-
- if (tree->gtFlags & GTF_REVERSE_OPS)
- {
- /* Generate the RHS into a register pair */
-
- genComputeRegPair(op2, REG_PAIR_NONE, avoidReg | op1->gtUsedRegs, RegSet::KEEP_REG);
- noway_assert(op2->InReg());
-
- /* Make the target addressable */
- op1 = genCodeForCommaTree(op1);
- addrReg = genMakeAddressable(op1, 0, RegSet::KEEP_REG);
-
- /* Make sure the RHS register hasn't been spilled */
-
- genRecoverRegPair(op2, REG_PAIR_NONE, RegSet::KEEP_REG);
- }
- else
- {
- /* Make the target addressable */
-
- op1 = genCodeForCommaTree(op1);
- addrReg = genMakeAddressable(op1, RBM_ALLINT & ~op2->gtRsvdRegs, RegSet::KEEP_REG, true);
-
- /* Generate the RHS into a register pair */
-
- genComputeRegPair(op2, REG_PAIR_NONE, avoidReg, RegSet::KEEP_REG, false);
- }
-
- /* Lock 'op2' and make sure 'op1' is still addressable */
-
- noway_assert(op2->InReg());
- regPair = op2->gtRegPair;
-
- addrReg = genKeepAddressable(op1, addrReg, genRegPairMask(regPair));
-
- /* Move the value into the target */
-
- inst_TT_RV(ins_Store(TYP_INT), op1, genRegPairLo(regPair), 0);
- inst_TT_RV(ins_Store(TYP_INT), op1, genRegPairHi(regPair), 4);
-
- /* Free up anything that was tied up by either side */
-
- genDoneAddressable(op1, addrReg, RegSet::KEEP_REG);
- genReleaseRegPair(op2);
-
- DONE_ASSG_REGS:
-
-#if REDUNDANT_LOAD
-
- if (op1->gtOper == GT_LCL_VAR)
- {
- /* Clear this local from reg table */
-
- regTracker.rsTrashLclLong(op1->gtLclVarCommon.gtLclNum);
-
- if ((op2->InReg()) &&
- /* constant has precedence over local */
- // rsRegValues[op2->gtRegNum].rvdKind != RV_INT_CNS &&
- tree->gtOper == GT_ASG)
- {
- regNumber regNo;
-
- /* mark RHS registers as containing the local var */
-
- regNo = genRegPairLo(op2->gtRegPair);
- if (regNo != REG_STK)
- regTracker.rsTrackRegLclVarLng(regNo, op1->gtLclVarCommon.gtLclNum, true);
-
- regNo = genRegPairHi(op2->gtRegPair);
- if (regNo != REG_STK)
- {
- /* For partially enregistered longs, we might have
- stomped on op2's hiReg */
- if (!(op1->InReg()) || regNo != genRegPairLo(op1->gtRegPair))
- {
- regTracker.rsTrackRegLclVarLng(regNo, op1->gtLclVarCommon.gtLclNum, false);
- }
- }
- }
- }
-#endif
-
- LAsgExit:
-
- genUpdateLife(op1);
- 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, lclVarILoffs);
- }
- return;
-
- case GT_SUB:
- insLo = INS_sub;
- insHi = INS_SUBC;
- setCarry = true;
- goto BINOP_OVF;
- case GT_ADD:
- insLo = INS_add;
- insHi = INS_ADDC;
- setCarry = true;
- goto BINOP_OVF;
-
- bool ovfl;
-
- BINOP_OVF:
- ovfl = tree->gtOverflow();
- goto _BINOP;
-
- case GT_AND:
- insLo = insHi = INS_AND;
- goto BINOP;
- case GT_OR:
- insLo = insHi = INS_OR;
- goto BINOP;
- case GT_XOR:
- insLo = insHi = INS_XOR;
- goto BINOP;
-
- BINOP:
- ovfl = false;
- goto _BINOP;
-
- _BINOP:
-
- /* The following makes an assumption about gtSetEvalOrder(this) */
-
- noway_assert((tree->gtFlags & GTF_REVERSE_OPS) == 0);
-
- /* Special case: check for "(long(intval) << 32) | longval" */
-
- if (oper == GT_OR && op1->gtOper == GT_LSH)
- {
- GenTree* lshLHS = op1->gtOp.gtOp1;
- GenTree* lshRHS = op1->gtOp.gtOp2;
-
- if (lshLHS->gtOper == GT_CAST && lshRHS->gtOper == GT_CNS_INT && lshRHS->gtIntCon.gtIconVal == 32 &&
- genTypeSize(TYP_INT) == genTypeSize(lshLHS->CastFromType()))
- {
-
- /* Throw away the cast of the shift operand. */
-
- op1 = lshLHS->gtCast.CastOp();
-
- /* Special case: check op2 for "ulong(intval)" */
- if ((op2->gtOper == GT_CAST) && (op2->CastToType() == TYP_ULONG) &&
- genTypeSize(TYP_INT) == genTypeSize(op2->CastFromType()))
- {
- /* Throw away the cast of the second operand. */
-
- op2 = op2->gtCast.CastOp();
- goto SIMPLE_OR_LONG;
- }
- /* Special case: check op2 for "long(intval) & 0xFFFFFFFF" */
- else if (op2->gtOper == GT_AND)
- {
- GenTree* andLHS;
- andLHS = op2->gtOp.gtOp1;
- GenTree* andRHS;
- andRHS = op2->gtOp.gtOp2;
-
- if (andLHS->gtOper == GT_CAST && andRHS->gtOper == GT_CNS_LNG &&
- andRHS->gtLngCon.gtLconVal == 0x00000000FFFFFFFF &&
- genTypeSize(TYP_INT) == genTypeSize(andLHS->CastFromType()))
- {
- /* Throw away the cast of the second operand. */
-
- op2 = andLHS->gtCast.CastOp();
-
- SIMPLE_OR_LONG:
- // Load the high DWORD, ie. op1
-
- genCodeForTree(op1, needReg & ~op2->gtRsvdRegs);
-
- noway_assert(op1->InReg());
- regHi = op1->gtRegNum;
- regSet.rsMarkRegUsed(op1);
-
- // Load the low DWORD, ie. op2
-
- genCodeForTree(op2, needReg & ~genRegMask(regHi));
-
- noway_assert(op2->InReg());
- regLo = op2->gtRegNum;
-
- /* Make sure regHi is still around. Also, force
- regLo to be excluded in case regLo==regHi */
-
- genRecoverReg(op1, ~genRegMask(regLo), RegSet::FREE_REG);
- regHi = op1->gtRegNum;
-
- regPair = gen2regs2pair(regLo, regHi);
- goto DONE;
- }
- }
-
- /* Generate the following sequence:
- Prepare op1 (discarding shift)
- Compute op2 into some regpair
- OR regpairhi, op1
- */
-
- /* First, make op1 addressable */
-
- /* tempReg must avoid both needReg, op2->RsvdRegs and regSet.rsMaskResvd.
-
- It appears incorrect to exclude needReg as we are not ensuring that the reg pair into
- which the long value is computed is from needReg. But at this point the safest fix is
- to exclude regSet.rsMaskResvd.
-
- Note that needReg could be the set of free registers (excluding reserved ones). If we don't
- exclude regSet.rsMaskResvd, the expression below will have the effect of trying to choose a
- reg from
- reserved set which is bound to fail. To prevent that we avoid regSet.rsMaskResvd.
- */
- regMaskTP tempReg = RBM_ALLINT & ~needReg & ~op2->gtRsvdRegs & ~avoidReg & ~regSet.rsMaskResvd;
-
- addrReg = genMakeAddressable(op1, tempReg, RegSet::KEEP_REG);
-
- genCompIntoFreeRegPair(op2, avoidReg, RegSet::KEEP_REG);
-
- noway_assert(op2->InReg());
- regPair = op2->gtRegPair;
- regHi = genRegPairHi(regPair);
-
- /* The operand might have interfered with the address */
-
- addrReg = genKeepAddressable(op1, addrReg, genRegPairMask(regPair));
-
- /* Now compute the result */
-
- inst_RV_TT(insHi, regHi, op1, 0);
-
- regTracker.rsTrackRegTrash(regHi);
-
- /* Free up anything that was tied up by the LHS */
-
- genDoneAddressable(op1, addrReg, RegSet::KEEP_REG);
-
- /* The result is where the second operand is sitting */
-
- genRecoverRegPair(op2, REG_PAIR_NONE, RegSet::FREE_REG);
-
- regPair = op2->gtRegPair;
- goto DONE;
- }
- }
-
- /* Special case: check for "longval | (long(intval) << 32)" */
-
- if (oper == GT_OR && op2->gtOper == GT_LSH)
- {
- GenTree* lshLHS = op2->gtOp.gtOp1;
- GenTree* lshRHS = op2->gtOp.gtOp2;
-
- if (lshLHS->gtOper == GT_CAST && lshRHS->gtOper == GT_CNS_INT && lshRHS->gtIntCon.gtIconVal == 32 &&
- genTypeSize(TYP_INT) == genTypeSize(lshLHS->CastFromType()))
-
- {
- /* We throw away the cast of the shift operand. */
-
- op2 = lshLHS->gtCast.CastOp();
-
- /* Special case: check op1 for "long(intval) & 0xFFFFFFFF" */
-
- if (op1->gtOper == GT_AND)
- {
- GenTree* andLHS = op1->gtOp.gtOp1;
- GenTree* andRHS = op1->gtOp.gtOp2;
-
- if (andLHS->gtOper == GT_CAST && andRHS->gtOper == GT_CNS_LNG &&
- andRHS->gtLngCon.gtLconVal == 0x00000000FFFFFFFF &&
- genTypeSize(TYP_INT) == genTypeSize(andLHS->CastFromType()))
- {
- /* Throw away the cast of the first operand. */
-
- op1 = andLHS->gtCast.CastOp();
-
- // Load the low DWORD, ie. op1
-
- genCodeForTree(op1, needReg & ~op2->gtRsvdRegs);
-
- noway_assert(op1->InReg());
- regLo = op1->gtRegNum;
- regSet.rsMarkRegUsed(op1);
-
- // Load the high DWORD, ie. op2
-
- genCodeForTree(op2, needReg & ~genRegMask(regLo));
-
- noway_assert(op2->InReg());
- regHi = op2->gtRegNum;
-
- /* Make sure regLo is still around. Also, force
- regHi to be excluded in case regLo==regHi */
-
- genRecoverReg(op1, ~genRegMask(regHi), RegSet::FREE_REG);
- regLo = op1->gtRegNum;
-
- regPair = gen2regs2pair(regLo, regHi);
- goto DONE;
- }
- }
-
- /* Generate the following sequence:
- Compute op1 into some regpair
- Make op2 (ignoring shift) addressable
- OR regPairHi, op2
- */
-
- // First, generate the first operand into some register
-
- genCompIntoFreeRegPair(op1, avoidReg | op2->gtRsvdRegs, RegSet::KEEP_REG);
- noway_assert(op1->InReg());
-
- /* Make the second operand addressable */
-
- addrReg = genMakeAddressable(op2, needReg, RegSet::KEEP_REG);
-
- /* Make sure the result is in a free register pair */
-
- genRecoverRegPair(op1, REG_PAIR_NONE, RegSet::KEEP_REG);
- regPair = op1->gtRegPair;
- regHi = genRegPairHi(regPair);
-
- /* The operand might have interfered with the address */
-
- addrReg = genKeepAddressable(op2, addrReg, genRegPairMask(regPair));
-
- /* Compute the new value */
-
- inst_RV_TT(insHi, regHi, op2, 0);
-
- /* The value in the high register has been trashed */
-
- regTracker.rsTrackRegTrash(regHi);
-
- goto DONE_OR;
- }
- }
-
- /* Generate the first operand into registers */
-
- if ((genCountBits(needReg) == 2) && ((regSet.rsRegMaskFree() & needReg) == needReg) &&
- ((op2->gtRsvdRegs & needReg) == RBM_NONE) && (!(tree->gtFlags & GTF_ASG)))
- {
- regPair = regSet.rsPickRegPair(needReg);
- genComputeRegPair(op1, regPair, avoidReg | op2->gtRsvdRegs, RegSet::KEEP_REG);
- }
- else
- {
- genCompIntoFreeRegPair(op1, avoidReg | op2->gtRsvdRegs, RegSet::KEEP_REG);
- }
- noway_assert(op1->InReg());
- regMaskTP op1Mask;
- regPair = op1->gtRegPair;
- op1Mask = genRegPairMask(regPair);
-
- /* Make the second operand addressable */
- regMaskTP needReg2;
- needReg2 = regSet.rsNarrowHint(needReg, ~op1Mask);
- addrReg = genMakeAddressable(op2, needReg2, RegSet::KEEP_REG);
-
- // TODO: If 'op1' got spilled and 'op2' happens to be
- // TODO: in a register, and we have add/mul/and/or/xor,
- // TODO: reverse the operands since we can perform the
- // TODO: operation directly with the spill temp, e.g.
- // TODO: 'add regHi, [temp]'.
-
- /* Make sure the result is in a free register pair */
-
- genRecoverRegPair(op1, REG_PAIR_NONE, RegSet::KEEP_REG);
- regPair = op1->gtRegPair;
- op1Mask = genRegPairMask(regPair);
-
- regLo = genRegPairLo(regPair);
- regHi = genRegPairHi(regPair);
-
- /* Make sure that we don't spill regLo/regHi below */
- regSet.rsLockUsedReg(op1Mask);
-
- /* The operand might have interfered with the address */
-
- addrReg = genKeepAddressable(op2, addrReg);
-
- /* The value in the register pair is about to be trashed */
-
- regTracker.rsTrackRegTrash(regLo);
- regTracker.rsTrackRegTrash(regHi);
-
- /* Compute the new value */
-
- doLo = true;
- doHi = true;
-
- if (op2->gtOper == GT_CNS_LNG)
- {
- __int64 icon = op2->gtLngCon.gtLconVal;
-
- /* Check for "(op1 AND -1)" and "(op1 [X]OR 0)" */
-
- switch (oper)
- {
- case GT_AND:
- if ((int)(icon) == -1)
- doLo = false;
- if ((int)(icon >> 32) == -1)
- doHi = false;
-
- if (!(icon & I64(0x00000000FFFFFFFF)))
- {
- genSetRegToIcon(regLo, 0);
- doLo = false;
- }
-
- if (!(icon & I64(0xFFFFFFFF00000000)))
- {
- /* Just to always set low first*/
-
- if (doLo)
- {
- inst_RV_TT(insLo, regLo, op2, 0);
- doLo = false;
- }
- genSetRegToIcon(regHi, 0);
- doHi = false;
- }
-
- break;
-
- case GT_OR:
- case GT_XOR:
- if (!(icon & I64(0x00000000FFFFFFFF)))
- doLo = false;
- if (!(icon & I64(0xFFFFFFFF00000000)))
- doHi = false;
- break;
- default:
- break;
- }
- }
-
- // Fix 383813 X86/ARM ILGEN
- // Fix 383793 ARM ILGEN
- // Fix 383911 ARM ILGEN
- regMaskTP newMask;
- newMask = addrReg & ~op1Mask;
- regSet.rsLockUsedReg(newMask);
-
- if (doLo)
- {
- insFlags flagsLo = setCarry ? INS_FLAGS_SET : INS_FLAGS_DONT_CARE;
- inst_RV_TT(insLo, regLo, op2, 0, EA_4BYTE, flagsLo);
- }
- if (doHi)
- {
- insFlags flagsHi = ovfl ? INS_FLAGS_SET : INS_FLAGS_DONT_CARE;
- inst_RV_TT(insHi, regHi, op2, 4, EA_4BYTE, flagsHi);
- }
-
- regSet.rsUnlockUsedReg(newMask);
- regSet.rsUnlockUsedReg(op1Mask);
-
- DONE_OR:
-
- /* Free up anything that was tied up by the LHS */
-
- genDoneAddressable(op2, addrReg, RegSet::KEEP_REG);
-
- /* The result is where the first operand is sitting */
-
- genRecoverRegPair(op1, REG_PAIR_NONE, RegSet::FREE_REG);
-
- regPair = op1->gtRegPair;
-
- if (ovfl)
- genCheckOverflow(tree);
-
- goto DONE;
-
- case GT_UMOD:
-
- regPair = genCodeForLongModInt(tree, needReg);
- goto DONE;
-
- case GT_MUL:
-
- /* Special case: both operands promoted from int */
-
- assert(tree->gtIsValid64RsltMul());
-
- /* Change to an integer multiply temporarily */
-
- tree->gtType = TYP_INT;
-
- noway_assert(op1->gtOper == GT_CAST && op2->gtOper == GT_CAST);
- tree->gtOp.gtOp1 = op1->gtCast.CastOp();
- tree->gtOp.gtOp2 = op2->gtCast.CastOp();
-
- assert(tree->gtFlags & GTF_MUL_64RSLT);
-
-#if defined(_TARGET_X86_)
- // imul on x86 requires EDX:EAX
- genComputeReg(tree, (RBM_EAX | RBM_EDX), RegSet::EXACT_REG, RegSet::FREE_REG);
- noway_assert(tree->InReg());
- noway_assert(tree->gtRegNum == REG_EAX); // Also REG_EDX is setup with hi 32-bits
-#elif defined(_TARGET_ARM_)
- genComputeReg(tree, needReg, RegSet::ANY_REG, RegSet::FREE_REG);
- noway_assert(tree->InReg());
-#else
- assert(!"Unsupported target for 64-bit multiply codegen");
-#endif
-
- /* Restore gtType, op1 and op2 from the change above */
-
- tree->gtType = TYP_LONG;
- tree->gtOp.gtOp1 = op1;
- tree->gtOp.gtOp2 = op2;
-
-#if defined(_TARGET_X86_)
- /* The result is now in EDX:EAX */
- regPair = REG_PAIR_EAXEDX;
-#elif defined(_TARGET_ARM_)
- regPair = tree->gtRegPair;
-#endif
- goto DONE;
-
- case GT_LSH:
- helper = CORINFO_HELP_LLSH;
- goto SHIFT;
- case GT_RSH:
- helper = CORINFO_HELP_LRSH;
- goto SHIFT;
- case GT_RSZ:
- helper = CORINFO_HELP_LRSZ;
- goto SHIFT;
-
- SHIFT:
-
- noway_assert(op1->gtType == TYP_LONG);
- noway_assert(genActualType(op2->gtType) == TYP_INT);
-
- /* Is the second operand a constant? */
-
- if (op2->gtOper == GT_CNS_INT)
- {
- unsigned int count = op2->gtIntCon.gtIconVal;
-
- /* Compute the left operand into a free register pair */
-
- genCompIntoFreeRegPair(op1, avoidReg | op2->gtRsvdRegs, RegSet::FREE_REG);
- noway_assert(op1->InReg());
-
- regPair = op1->gtRegPair;
- regLo = genRegPairLo(regPair);
- regHi = genRegPairHi(regPair);
-
- /* Assume the value in the register pair is trashed. In some cases, though,
- a register might be set to zero, and we can use that information to improve
- some code generation.
- */
-
- regTracker.rsTrackRegTrash(regLo);
- regTracker.rsTrackRegTrash(regHi);
-
- /* Generate the appropriate shift instructions */
-
- switch (oper)
- {
- case GT_LSH:
- if (count == 0)
- {
- // regHi, regLo are correct
- }
- else if (count < 32)
- {
-#if defined(_TARGET_XARCH_)
- inst_RV_RV_IV(INS_shld, EA_4BYTE, regHi, regLo, count);
-#elif defined(_TARGET_ARM_)
- inst_RV_SH(INS_SHIFT_LEFT_LOGICAL, EA_4BYTE, regHi, count);
- getEmitter()->emitIns_R_R_R_I(INS_OR, EA_4BYTE, regHi, regHi, regLo, 32 - count,
- INS_FLAGS_DONT_CARE, INS_OPTS_LSR);
-#else // _TARGET_*
- NYI("INS_shld");
-#endif // _TARGET_*
- inst_RV_SH(INS_SHIFT_LEFT_LOGICAL, EA_4BYTE, regLo, count);
- }
- else // count >= 32
- {
- assert(count >= 32);
- if (count < 64)
- {
-#if defined(_TARGET_ARM_)
- if (count == 32)
- {
- // mov low dword into high dword (i.e. shift left by 32-bits)
- inst_RV_RV(INS_mov, regHi, regLo);
- }
- else
- {
- assert(count > 32 && count < 64);
- getEmitter()->emitIns_R_R_I(INS_SHIFT_LEFT_LOGICAL, EA_4BYTE, regHi, regLo,
- count - 32);
- }
-#else // _TARGET_*
- // mov low dword into high dword (i.e. shift left by 32-bits)
- inst_RV_RV(INS_mov, regHi, regLo);
- if (count > 32)
- {
- // Shift high dword left by count - 32
- inst_RV_SH(INS_SHIFT_LEFT_LOGICAL, EA_4BYTE, regHi, count - 32);
- }
-#endif // _TARGET_*
- }
- else // count >= 64
- {
- assert(count >= 64);
- genSetRegToIcon(regHi, 0);
- }
- genSetRegToIcon(regLo, 0);
- }
- break;
-
- case GT_RSH:
- if (count == 0)
- {
- // regHi, regLo are correct
- }
- else if (count < 32)
- {
-#if defined(_TARGET_XARCH_)
- inst_RV_RV_IV(INS_shrd, EA_4BYTE, regLo, regHi, count);
-#elif defined(_TARGET_ARM_)
- inst_RV_SH(INS_SHIFT_RIGHT_LOGICAL, EA_4BYTE, regLo, count);
- getEmitter()->emitIns_R_R_R_I(INS_OR, EA_4BYTE, regLo, regLo, regHi, 32 - count,
- INS_FLAGS_DONT_CARE, INS_OPTS_LSL);
-#else // _TARGET_*
- NYI("INS_shrd");
-#endif // _TARGET_*
- inst_RV_SH(INS_SHIFT_RIGHT_ARITHM, EA_4BYTE, regHi, count);
- }
- else // count >= 32
- {
- assert(count >= 32);
- if (count < 64)
- {
-#if defined(_TARGET_ARM_)
- if (count == 32)
- {
- // mov high dword into low dword (i.e. shift right by 32-bits)
- inst_RV_RV(INS_mov, regLo, regHi);
- }
- else
- {
- assert(count > 32 && count < 64);
- getEmitter()->emitIns_R_R_I(INS_SHIFT_RIGHT_ARITHM, EA_4BYTE, regLo, regHi,
- count - 32);
- }
-#else // _TARGET_*
- // mov high dword into low dword (i.e. shift right by 32-bits)
- inst_RV_RV(INS_mov, regLo, regHi);
- if (count > 32)
- {
- // Shift low dword right by count - 32
- inst_RV_SH(INS_SHIFT_RIGHT_ARITHM, EA_4BYTE, regLo, count - 32);
- }
-#endif // _TARGET_*
- }
-
- // Propagate sign bit in high dword
- inst_RV_SH(INS_SHIFT_RIGHT_ARITHM, EA_4BYTE, regHi, 31);
-
- if (count >= 64)
- {
- // Propagate the sign from the high dword
- inst_RV_RV(INS_mov, regLo, regHi, TYP_INT);
- }
- }
- break;
-
- case GT_RSZ:
- if (count == 0)
- {
- // regHi, regLo are correct
- }
- else if (count < 32)
- {
-#if defined(_TARGET_XARCH_)
- inst_RV_RV_IV(INS_shrd, EA_4BYTE, regLo, regHi, count);
-#elif defined(_TARGET_ARM_)
- inst_RV_SH(INS_SHIFT_RIGHT_LOGICAL, EA_4BYTE, regLo, count);
- getEmitter()->emitIns_R_R_R_I(INS_OR, EA_4BYTE, regLo, regLo, regHi, 32 - count,
- INS_FLAGS_DONT_CARE, INS_OPTS_LSL);
-#else // _TARGET_*
- NYI("INS_shrd");
-#endif // _TARGET_*
- inst_RV_SH(INS_SHIFT_RIGHT_LOGICAL, EA_4BYTE, regHi, count);
- }
- else // count >= 32
- {
- assert(count >= 32);
- if (count < 64)
- {
-#if defined(_TARGET_ARM_)
- if (count == 32)
- {
- // mov high dword into low dword (i.e. shift right by 32-bits)
- inst_RV_RV(INS_mov, regLo, regHi);
- }
- else
- {
- assert(count > 32 && count < 64);
- getEmitter()->emitIns_R_R_I(INS_SHIFT_RIGHT_LOGICAL, EA_4BYTE, regLo, regHi,
- count - 32);
- }
-#else // _TARGET_*
- // mov high dword into low dword (i.e. shift right by 32-bits)
- inst_RV_RV(INS_mov, regLo, regHi);
- if (count > 32)
- {
- // Shift low dword right by count - 32
- inst_RV_SH(INS_SHIFT_RIGHT_LOGICAL, EA_4BYTE, regLo, count - 32);
- }
-#endif // _TARGET_*
- }
- else // count >= 64
- {
- assert(count >= 64);
- genSetRegToIcon(regLo, 0);
- }
- genSetRegToIcon(regHi, 0);
- }
- break;
-
- default:
- noway_assert(!"Illegal oper for long shift");
- break;
- }
-
- goto DONE_SHF;
- }
-
- /* Which operand are we supposed to compute first? */
-
- assert((RBM_SHIFT_LNG & RBM_LNGARG_0) == 0);
-
- if (tree->gtFlags & GTF_REVERSE_OPS)
- {
- /* The second operand can't be a constant */
-
- noway_assert(op2->gtOper != GT_CNS_INT);
-
- /* Load the shift count, hopefully into RBM_SHIFT */
- RegSet::ExactReg exactReg;
- if ((RBM_SHIFT_LNG & op1->gtRsvdRegs) == 0)
- exactReg = RegSet::EXACT_REG;
- else
- exactReg = RegSet::ANY_REG;
- genComputeReg(op2, RBM_SHIFT_LNG, exactReg, RegSet::KEEP_REG);
-
- /* Compute the left operand into REG_LNGARG_0 */
-
- genComputeRegPair(op1, REG_LNGARG_0, avoidReg, RegSet::KEEP_REG, false);
- noway_assert(op1->InReg());
-
- /* Lock op1 so that it doesn't get trashed */
-
- regSet.rsLockUsedReg(RBM_LNGARG_0);
-
- /* Make sure the shift count wasn't displaced */
-
- genRecoverReg(op2, RBM_SHIFT_LNG, RegSet::KEEP_REG);
-
- /* Lock op2 */
-
- regSet.rsLockUsedReg(RBM_SHIFT_LNG);
- }
- else
- {
- /* Compute the left operand into REG_LNGARG_0 */
-
- genComputeRegPair(op1, REG_LNGARG_0, avoidReg, RegSet::KEEP_REG, false);
- noway_assert(op1->InReg());
-
- /* Compute the shift count into RBM_SHIFT */
-
- genComputeReg(op2, RBM_SHIFT_LNG, RegSet::EXACT_REG, RegSet::KEEP_REG);
-
- /* Lock op2 */
-
- regSet.rsLockUsedReg(RBM_SHIFT_LNG);
-
- /* Make sure the value hasn't been displaced */
-
- genRecoverRegPair(op1, REG_LNGARG_0, RegSet::KEEP_REG);
-
- /* Lock op1 so that it doesn't get trashed */
-
- regSet.rsLockUsedReg(RBM_LNGARG_0);
- }
-
-#ifndef _TARGET_X86_
- /* The generic helper is a C-routine and so it follows the full ABI */
- {
- /* Spill any callee-saved registers which are being used */
- regMaskTP spillRegs = RBM_CALLEE_TRASH & regSet.rsMaskUsed;
-
- /* But do not spill our argument registers. */
- spillRegs &= ~(RBM_LNGARG_0 | RBM_SHIFT_LNG);
-
- if (spillRegs)
- {
- regSet.rsSpillRegs(spillRegs);
- }
- }
-#endif // !_TARGET_X86_
-
- /* Perform the shift by calling a helper function */
-
- noway_assert(op1->gtRegPair == REG_LNGARG_0);
- noway_assert(op2->gtRegNum == REG_SHIFT_LNG);
- noway_assert((regSet.rsMaskLock & (RBM_LNGARG_0 | RBM_SHIFT_LNG)) == (RBM_LNGARG_0 | RBM_SHIFT_LNG));
-
- genEmitHelperCall(helper,
- 0, // argSize
- EA_8BYTE); // retSize
-
-#ifdef _TARGET_X86_
- /* The value in the register pair is trashed */
-
- regTracker.rsTrackRegTrash(genRegPairLo(REG_LNGARG_0));
- regTracker.rsTrackRegTrash(genRegPairHi(REG_LNGARG_0));
-#else // _TARGET_X86_
- /* The generic helper is a C-routine and so it follows the full ABI */
- regTracker.rsTrackRegMaskTrash(RBM_CALLEE_TRASH);
-#endif // _TARGET_X86_
-
- /* Release both operands */
-
- regSet.rsUnlockUsedReg(RBM_LNGARG_0 | RBM_SHIFT_LNG);
- genReleaseRegPair(op1);
- genReleaseReg(op2);
-
- DONE_SHF:
-
- noway_assert(op1->InReg());
- regPair = op1->gtRegPair;
- goto DONE;
-
- case GT_NEG:
- case GT_NOT:
-
- /* Generate the operand into some register pair */
-
- genCompIntoFreeRegPair(op1, avoidReg, RegSet::FREE_REG);
- noway_assert(op1->InReg());
-
- regPair = op1->gtRegPair;
-
- /* 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);
-
- /* Unary "neg": negate the value in the register pair */
- if (oper == GT_NEG)
- {
-#ifdef _TARGET_ARM_
-
- // ARM doesn't have an opcode that sets the carry bit like
- // x86, so we can't use neg/addc/neg. Instead we use subtract
- // with carry. Too bad this uses an extra register.
-
- // Lock regLo and regHi so we don't pick them, and then pick
- // a third register to be our 0.
- regMaskTP regPairMask = genRegMask(regLo) | genRegMask(regHi);
- regSet.rsLockReg(regPairMask);
- regMaskTP regBest = RBM_ALLINT & ~avoidReg;
- regNumber regZero = genGetRegSetToIcon(0, regBest);
- regSet.rsUnlockReg(regPairMask);
-
- inst_RV_IV(INS_rsb, regLo, 0, EA_4BYTE, INS_FLAGS_SET);
- getEmitter()->emitIns_R_R_R_I(INS_sbc, EA_4BYTE, regHi, regZero, regHi, 0);
-
-#elif defined(_TARGET_XARCH_)
-
- inst_RV(INS_NEG, regLo, TYP_LONG);
- inst_RV_IV(INS_ADDC, regHi, 0, emitActualTypeSize(TYP_LONG));
- inst_RV(INS_NEG, regHi, TYP_LONG);
-#else
- NYI("GT_NEG on TYP_LONG");
-#endif
- }
- else
- {
- /* Unary "not": flip all the bits in the register pair */
-
- inst_RV(INS_NOT, regLo, TYP_LONG);
- inst_RV(INS_NOT, regHi, TYP_LONG);
- }
-
- goto DONE;
-
- case GT_IND:
- case GT_NULLCHECK:
- {
- regMaskTP tmpMask;
- int hiFirst;
-
- regMaskTP availMask = RBM_ALLINT & ~needReg;
-
- /* Make sure the operand is addressable */
-
- addrReg = genMakeAddressable(tree, availMask, RegSet::FREE_REG);
-
- GenTree* addr = oper == GT_IND ? op1 : tree;
-
- /* Pick a register for the value */
-
- regPair = regSet.rsPickRegPair(needReg);
- tmpMask = genRegPairMask(regPair);
-
- /* Is there any overlap between the register pair and the address? */
-
- hiFirst = FALSE;
-
- if (tmpMask & addrReg)
- {
- /* Does one or both of the target registers overlap? */
-
- if ((tmpMask & addrReg) != tmpMask)
- {
- /* Only one register overlaps */
-
- noway_assert(genMaxOneBit(tmpMask & addrReg) == TRUE);
-
- /* If the low register overlaps, load the upper half first */
-
- if (addrReg & genRegMask(genRegPairLo(regPair)))
- hiFirst = TRUE;
- }
- else
- {
- regMaskTP regFree;
-
- /* The register completely overlaps with the address */
-
- noway_assert(genMaxOneBit(tmpMask & addrReg) == FALSE);
-
- /* Can we pick another pair easily? */
-
- regFree = regSet.rsRegMaskFree() & ~addrReg;
- if (needReg)
- regFree &= needReg;
-
- /* More than one free register available? */
-
- if (regFree && !genMaxOneBit(regFree))
- {
- regPair = regSet.rsPickRegPair(regFree);
- tmpMask = genRegPairMask(regPair);
- }
- else
- {
- // printf("Overlap: needReg = %08X\n", needReg);
-
- // Reg-prediction won't allow this
- noway_assert((regSet.rsMaskVars & addrReg) == 0);
-
- // Grab one fresh reg, and use any one of addrReg
-
- if (regFree) // Try to follow 'needReg'
- regLo = regSet.rsGrabReg(regFree);
- else // Pick any reg besides addrReg
- regLo = regSet.rsGrabReg(RBM_ALLINT & ~addrReg);
-
- unsigned regBit = 0x1;
- regNumber regNo;
-
- for (regNo = REG_INT_FIRST; regNo <= REG_INT_LAST; regNo = REG_NEXT(regNo), regBit <<= 1)
- {
- // Found one of addrReg. Use it.
- if (regBit & addrReg)
- break;
- }
- noway_assert(genIsValidReg(regNo)); // Should have found regNo
-
- regPair = gen2regs2pair(regLo, regNo);
- tmpMask = genRegPairMask(regPair);
- }
- }
- }
-
- /* Make sure the value is still addressable */
-
- noway_assert(genStillAddressable(tree));
-
- /* 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 target registers from where the value is */
-
- if (hiFirst)
- {
- inst_RV_AT(ins_Load(TYP_INT), EA_4BYTE, TYP_INT, regHi, addr, 4);
- regSet.rsLockReg(genRegMask(regHi));
- inst_RV_AT(ins_Load(TYP_INT), EA_4BYTE, TYP_INT, regLo, addr, 0);
- regSet.rsUnlockReg(genRegMask(regHi));
- }
- else
- {
- inst_RV_AT(ins_Load(TYP_INT), EA_4BYTE, TYP_INT, regLo, addr, 0);
- regSet.rsLockReg(genRegMask(regLo));
- inst_RV_AT(ins_Load(TYP_INT), EA_4BYTE, TYP_INT, regHi, addr, 4);
- regSet.rsUnlockReg(genRegMask(regLo));
- }
-
-#ifdef _TARGET_ARM_
- if (tree->gtFlags & GTF_IND_VOLATILE)
- {
- // Emit a memory barrier instruction after the load
- instGen_MemoryBarrier();
- }
-#endif
-
- genUpdateLife(tree);
- genDoneAddressable(tree, addrReg, RegSet::FREE_REG);
- }
- goto DONE;
-
- case GT_CAST:
-
- /* What are we casting from? */
-
- switch (op1->gtType)
- {
- case TYP_BOOL:
- case TYP_BYTE:
- case TYP_USHORT:
- case TYP_SHORT:
- case TYP_INT:
- case TYP_UBYTE:
- case TYP_BYREF:
- {
- regMaskTP hiRegMask;
- regMaskTP loRegMask;
-
- // For an unsigned cast we don't need to sign-extend the 32 bit value
- if (tree->gtFlags & GTF_UNSIGNED)
- {
- // 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_UNSIGNED;
- loRegMask = genRegMask(genRegPairLo(regPair));
- if ((loRegMask & regSet.rsRegMaskCanGrab()) == 0)
- goto ANY_FREE_REG_UNSIGNED;
- hiRegMask = genRegMask(genRegPairHi(regPair));
- }
- else
- {
- ANY_FREE_REG_UNSIGNED:
- loRegMask = needReg;
- hiRegMask = needReg;
- }
-
- genComputeReg(op1, loRegMask, RegSet::ANY_REG, RegSet::KEEP_REG);
- noway_assert(op1->InReg());
-
- regLo = op1->gtRegNum;
- loRegMask = genRegMask(regLo);
- regSet.rsLockUsedReg(loRegMask);
- regHi = regSet.rsPickReg(hiRegMask);
- regSet.rsUnlockUsedReg(loRegMask);
-
- regPair = gen2regs2pair(regLo, regHi);
-
- // Move 0 to the higher word of the ULong
- genSetRegToIcon(regHi, 0, TYP_INT);
-
- /* We can now free up the operand */
- genReleaseReg(op1);
-
- goto DONE;
- }
-#ifdef _TARGET_XARCH_
- /* Cast of 'int' to 'long' --> 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, &regTmp);
- 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, &regTmp);
- 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), &regSrcUsed);
-
- 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*/ &regArg,
- /*argOffset*/ INT32_MAX,
- /*fieldOffsetOfFirstStackSlot*/ INT32_MAX,
- /*argOffsetOfFirstStackSlot*/ INT32_MAX,
- &deadFieldVarRegs, &regTmp);
- 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(), &regsUsed);
- }
-
-#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,
- &regsUsed);
-#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_<cond> 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_<cond> 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 <bool ForCodeGen>
-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 <op>=
- // ..." 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(<promoted struct local>)),
- 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<true>(GenTree* tree, VARSET_TP* pLastUseVars);
-template void Compiler::compUpdateLifeVar<false>(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 <bool ForCodeGen>
-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<ForCodeGen>(tree);
-}
-
-template void Compiler::compUpdateLife<false>(GenTree* tree);
-template void Compiler::compUpdateLife<true>(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<GenTree*>(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;
@@ -2012,9 +1983,6 @@ void Compiler::compInit(ArenaAllocator* pAlloc, InlineInfo* inlineInfo)
#if CPU_USES_BLOCK_MOVE
compBlkOpUsed = false;
#endif
-#if FEATURE_STACK_FP_X87
- compMayHaveTransitionBlocks = false;
-#endif
compNeedsGSSecurityCookie = false;
compGSReorderStackLayout = false;
#if STACK_PROBES
@@ -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</*ForCodeGen*/ true>(newLife);
}
-#ifdef LEGACY_BACKEND
-
- template <bool ForCodeGen>
- 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 <bool ForCodeGen>
- void compUpdateLifeVar(GenTree* tree, VARSET_TP* pLastUseVars = nullptr);
-
-#endif // LEGACY_BACKEND
-
template <bool ForCodeGen>
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<GenTree*>* 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 @@
<OutputName>jit_crossgen</OutputName>
<FeatureMergeJitAndEngine>true</FeatureMergeJitAndEngine>
<TargetType>LIBRARY</TargetType>
-
- <ClDefines Condition="'$(BuildArchitecture)' == 'i386'">$(ClDefines);LEGACY_BACKEND</ClDefines>
- <ClDefines Condition="'$(BuildArchitecture)' == 'arm'">$(ClDefines);LEGACY_BACKEND</ClDefines>
</PropertyGroup>
<Import Project="..\jit.settings.targets" />
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
/*
<BUGNUM> VSW441487 </BUGNUM>
@@ -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,12 +298,10 @@ 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;
@@ -315,27 +311,6 @@ bool GCInfo::gcIsWriteBarrierAsgNode(GenTree* op)
/*****************************************************************************/
/*****************************************************************************
*
- * 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
-
-/*****************************************************************************/
-/*****************************************************************************
- *
* Initialize the non-register pointer variable tracking logic.
*/
@@ -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<GenTree*>(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<GenTree*>(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<GenTree*>(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<GenTree*>(this);
- GenTreeCopyOrReload* copyOrReload = tree->AsCopyOrReload();
- GenTreeCall* call = copyOrReload->gtGetOp1()->AsCall();
- unsigned regCount = call->GetReturnTypeDesc()->GetReturnRegCount();
+ GenTree* tree = const_cast<GenTree*>(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<GenTree*>(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<GenTree*>(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);
}
@@ -3387,13 +3290,6 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree)
}
}
#endif
-#if CPU_LONG_USES_REGPAIR
- if (varTypeIsLong(tree->TypeGet()))
- {
- costEx *= 2; // Longs are twice as expensive
- costSz *= 2;
- }
-#endif
break;
case GT_CLS_VAR:
@@ -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<unsigned char>(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_<relop> -- Is branch taken if ops are NaN?
#define GTF_RELOP_JMP_USED 0x40000000 // GT_<relop> -- result of compare used for jump or ?:
#define GTF_RELOP_QMARK 0x20000000 // GT_<relop> -- 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<T>. 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
@@ -19221,25 +19136,12 @@ bool Compiler::IsTargetIntrinsic(CorInfoIntrinsics intrinsicId)
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;
- }
#else
// TODO: This portion of logic is not implemented for other arch.
// 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,845 +537,6 @@ 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.
- */
-
-void CodeGen::instGetAddrMode(GenTree* addr, regNumber* baseReg, unsigned* indScale, regNumber* indReg, unsigned* cns)
-{
- 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;
-
- *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
- {
- /* 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())
- {
- 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());
- }
- // If we did both, we might have spilled rv1.
- if (rv1 && ((rv1->gtFlags & GTF_SPILLED) != 0))
- {
- regSet.rsLockUsedReg(genRegMask(rv2->gtRegNum));
- genRecoverReg(rv1, ~genRegMask(rv2->gtRegNum), RegSet::KEEP_REG);
- regSet.rsUnlockReg(genRegMask(rv2->gtRegNum));
- }
-
- *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<<shift
- * movw regOffs, offsLo
- * movt regOffs, offsHi
- * ldr regVal, [regT + regOffs]
- * movw regImm, consLo
- * movt regImm, consHi
- * "ins" regVal, regImm
- * str regVal, [regT + regOffs]
- *
- */
-
-void CodeGen::sched_AM(instruction ins,
- emitAttr size,
- regNumber ireg,
- bool rdst,
- GenTree* addr,
- unsigned offs,
- bool cons,
- int imm,
- insFlags flags)
-{
- assert(addr);
- assert(size != EA_UNKNOWN);
-
- enum INS_TYPE
- {
- eIT_Lea,
- eIT_Load,
- eIT_Store,
- eIT_Other
- };
- INS_TYPE insType = eIT_Other;
-
- if (ins == INS_lea)
- {
- insType = eIT_Lea;
- ins = INS_add;
- }
- else if (getEmitter()->emitInsIsLoad(ins))
- {
- insType = eIT_Load;
- }
- else if (getEmitter()->emitInsIsStore(ins))
- {
- insType = eIT_Store;
- }
-
- regNumber baseReg = REG_NA;
- regNumber indReg = REG_NA;
- unsigned indScale = 0;
-
- regMaskTP avoidMask = RBM_NONE;
-
- if (addr->InReg())
- {
- /* The address is "[reg+offs]" */
- baseReg = addr->gtRegNum;
- }
- 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
- {
- 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
-
- // 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;
- }
-
- // 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);
- }
-
-#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<<shift.
- getEmitter()->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);
- }
- }
-
- // 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);
-
- 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);
-
- // We cannot use [regT + regOffs] to load/store a floating register
- if (emitter::isFloatReg(ireg))
- {
- 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);
- }
- 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);
- }
- }
- }
- 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;
- }
-
- // 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);
- }
-
- if (cons)
- {
- if (arm_Valid_Imm_For_Instr(ins, imm, flags))
- {
- getEmitter()->emitIns_R_I(ins, size, regVal, imm, flags);
- }
- 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);
- }
- }
- else if (rdst)
- {
- getEmitter()->emitIns_R_R(ins, size, ireg, regVal, flags);
- }
- else
- {
- getEmitter()->emitIns_R_R(ins, size, regVal, ireg, flags);
- }
-
- // 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);
- }
- else
- {
- // strX regVal, [regT + regOffs]
- getEmitter()->emitIns_R_R_R(ins_Store(load_store_type), size, regVal, regT, regOffs);
- }
- }
- }
-}
-
-#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.
- */
-
-void CodeGen::sched_AM(instruction ins,
- emitAttr size,
- regNumber ireg,
- bool rdst,
- GenTree* addr,
- unsigned offs,
- bool cons,
- int imm,
- insFlags flags)
-{
-#ifdef _TARGET_XARCH_
- /* Don't use this method for issuing calls. Use instEmit_xxxCall() */
- assert(ins != INS_call);
-#endif
-
- assert(addr);
- assert(size != EA_UNKNOWN);
-
- regNumber reg;
-
- /* Has the address been conveniently loaded into a register,
- or is it an absolute value ? */
-
- if ((addr->InReg()) || (addr->IsCnsIntOrI()))
- {
- if (addr->InReg())
- {
- /* 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);
- }
- 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
@@ -1486,80 +562,6 @@ void CodeGen::inst_TT(instruction ins, GenTree* tree, unsigned offs, int shfv, e
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)
@@ -1573,18 +575,6 @@ AGAIN:
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;
@@ -1629,15 +619,7 @@ AGAIN:
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
+ assert(!"inst_TT not supported for GT_IND, GT_NULLCHECK or GT_ARR_ELEM");
}
break;
@@ -1675,67 +657,6 @@ void CodeGen::inst_TT_RV(instruction ins, GenTree* tree, regNumber reg, unsigned
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)
@@ -1761,16 +682,6 @@ AGAIN:
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;
@@ -1787,14 +698,10 @@ AGAIN:
#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);
@@ -1825,20 +732,7 @@ AGAIN:
#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
+ NYI("Store of GT_CLS_VAR not supported for ARM");
}
else
#endif // CPU_LOAD_STORE_ARCH
@@ -1851,12 +745,7 @@ AGAIN:
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
+ assert(!"inst_TT_RV not supported for GT_IND, GT_NULLCHECK or GT_ARR_ELEM");
}
break;
@@ -1870,421 +759,6 @@ AGAIN:
}
}
-#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;
-
- 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;
-
- 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);
-
- 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);
- }
-
- LCL:
-
- /* Integer instructions never operate on more than EA_PTRSIZE */
-
- assert(instIsFP(ins) == false);
-
-#if CPU_LOAD_STORE_ARCH
- if (!getEmitter()->emitInsIsStore(ins))
- {
- regNumber regTmp = regSet.rsPickFreeReg(RBM_ALLINT);
- 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_S_R(ins_Store(tree->TypeGet()), size, regTmp, varNum, offs);
- }
- else
-#endif
- {
- getEmitter()->emitIns_S_I(ins, size, varNum, offs, ival);
- }
- 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.
- //
- 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);
- }
- else
- {
- 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);
- }
-#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);
- }
- return;
-
- 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 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;
@@ -2636,32 +941,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 */
+ /* Generate the "movsx/movzx" opcode */
- inst_RV_TT(INS_mov, reg, tree, 0, EA_1BYTE);
- }
- else
-#endif // CPU_HAS_BYTE_REGS && defined(LEGACY_BACKEND)
- {
- /* 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 @@
<CppCompile Include="..\objectalloc.cpp" />
<CppCompile Include="..\sideeffects.cpp" />
<CppCompile Include="..\stacklevelsetter.cpp" />
- <CppCompile Condition="'$(ClDefines.Contains(`LEGACY_BACKEND`))'=='True'" Include="..\CodeGenLegacy.cpp" />
- <CppCompile Condition="'$(ClDefines.Contains(`LEGACY_BACKEND`))'=='False'" Include="..\Lower.cpp" />
- <CppCompile Condition="'$(ClDefines.Contains(`LEGACY_BACKEND`))'=='False'" Include="..\LSRA.cpp" />
- <CppCompile Condition="'$(ClDefines.Contains(`LEGACY_BACKEND`))'=='False'" Include="..\lsrabuild.cpp" />
- <CppCompile Condition="'$(ClDefines.Contains(`LEGACY_BACKEND`))'=='False'" Include="..\codegenlinear.cpp" />
+ <CppCompile Include="..\Lower.cpp" />
+ <CppCompile Include="..\LSRA.cpp" />
+ <CppCompile Include="..\lsrabuild.cpp" />
+ <CppCompile Include="..\codegenlinear.cpp" />
</ItemGroup>
<ItemGroup Condition="'$(TargetArch)'=='i386'">
<CppCompile Include="..\emitXArch.cpp" />
<CppCompile Include="..\TargetX86.cpp" />
<CppCompile Include="..\unwindx86.cpp" />
- <CppCompile Condition="'$(ClDefines.Contains(`LEGACY_BACKEND`))'=='True'" Include="..\stackfp.cpp" />
- <CppCompile Condition="'$(ClDefines.Contains(`LEGACY_BACKEND`))'=='False'" Include="..\DecomposeLongs.cpp" />
- <CppCompile Condition="'$(ClDefines.Contains(`LEGACY_BACKEND`))'=='False'" Include="..\LowerXArch.cpp" />
- <CppCompile Condition="'$(ClDefines.Contains(`LEGACY_BACKEND`))'=='False'" Include="..\lsraxarch.cpp" />
- <CppCompile Condition="'$(ClDefines.Contains(`LEGACY_BACKEND`))'=='False'" Include="..\CodeGenXArch.cpp" />
- <CppCompile Condition="'$(ClDefines.Contains(`LEGACY_BACKEND`))'=='False'" Include="..\SIMD.cpp" />
- <CppCompile Condition="'$(ClDefines.Contains(`LEGACY_BACKEND`))'=='False'" Include="..\SIMDCodeGenXArch.cpp" />
- <CppCompile Condition="'$(ClDefines.Contains(`LEGACY_BACKEND`))'=='False'" Include="..\hwintrinsicxarch.cpp" />
- <CppCompile Condition="'$(ClDefines.Contains(`LEGACY_BACKEND`))'=='False'" Include="..\hwintrinsiccodegenxarch.cpp" />
+ <CppCompile Include="..\DecomposeLongs.cpp" />
+ <CppCompile Include="..\LowerXArch.cpp" />
+ <CppCompile Include="..\lsraxarch.cpp" />
+ <CppCompile Include="..\CodeGenXArch.cpp" />
+ <CppCompile Include="..\SIMD.cpp" />
+ <CppCompile Include="..\SIMDCodeGenXArch.cpp" />
+ <CppCompile Include="..\hwintrinsicxarch.cpp" />
+ <CppCompile Include="..\hwintrinsiccodegenxarch.cpp" />
</ItemGroup>
<ItemGroup Condition="'$(TargetArch)'=='amd64'">
- <!-- AMD64 target is always RyuJIT backend -->
<CppCompile Include="..\emitXArch.cpp" />
<CppCompile Include="..\TargetAmd64.cpp" />
<CppCompile Include="..\LowerXArch.cpp" />
@@ -124,18 +121,16 @@
<ItemGroup Condition="'$(TargetArch)'=='arm'">
<CppCompile Include="..\emitarm.cpp" />
<CppCompile Include="..\TargetArm.cpp" />
- <CppCompile Condition="'$(ClDefines.Contains(`LEGACY_BACKEND`))'=='True'" Include="..\registerfp.cpp" />
- <CppCompile Condition="'$(ClDefines.Contains(`LEGACY_BACKEND`))'=='False'" Include="..\DecomposeLongs.cpp" />
- <CppCompile Condition="'$(ClDefines.Contains(`LEGACY_BACKEND`))'=='False'" Include="..\LowerArmArch.cpp" />
- <CppCompile Condition="'$(ClDefines.Contains(`LEGACY_BACKEND`))'=='False'" Include="..\lsraarmarch.cpp" />
- <CppCompile Condition="'$(ClDefines.Contains(`LEGACY_BACKEND`))'=='False'" Include="..\CodeGenArmArch.cpp" />
- <CppCompile Condition="'$(ClDefines.Contains(`LEGACY_BACKEND`))'=='False'" Include="..\LowerArm.cpp" />
- <CppCompile Condition="'$(ClDefines.Contains(`LEGACY_BACKEND`))'=='False'" Include="..\lsraarm.cpp" />
- <CppCompile Condition="'$(ClDefines.Contains(`LEGACY_BACKEND`))'=='False'" Include="..\CodeGenArm.cpp" />
+ <CppCompile Include="..\DecomposeLongs.cpp" />
+ <CppCompile Include="..\LowerArmArch.cpp" />
+ <CppCompile Include="..\lsraarmarch.cpp" />
+ <CppCompile Include="..\CodeGenArmArch.cpp" />
+ <CppCompile Include="..\LowerArm.cpp" />
+ <CppCompile Include="..\lsraarm.cpp" />
+ <CppCompile Include="..\CodeGenArm.cpp" />
<CppCompile Include="..\unwindArm.cpp" />
</ItemGroup>
<ItemGroup Condition="'$(TargetArch)'=='arm64'">
- <!-- ARM64 target is always RyuJIT backend -->
<CppCompile Include="..\emitarm64.cpp" />
<CppCompile Include="..\TargetArm64.cpp" />
<CppCompile Include="..\LowerArmArch.cpp" />
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_<cond>->gtLiveSet and GT_COLON->gtLiveSet are
- the union of the liveness on entry to thenTree and elseTree.
-
- +--------------------+
- | GT_QMARK 11|
- +----------+---------+
- |
- *
- / \
- / \
- / \
- +---------------------+ +--------------------+
- | GT_<cond> 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 <op_ovf>= (with GTF_OVERFLOW). In that case, we need to keep the <op_ovf>
-
- // Dead <OpOvf>= 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 <asgop> ovf to <op> 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))
{
@@ -123,20 +119,6 @@ GenTree* Compiler::fgMorphIntoHelperCall(GenTree* tree, int helper, GenTreeArgLi
/*****************************************************************************
*
- * 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 <op> x" into "a <op>= x" */
- /* and also "a = x <op> a" into "a <op>= 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 "<op>=" if the result can be
- visible to anyone as <op> 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 <op> a" to "a <op>= 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<UINT64> si1(i1);
- if ((si1 + ClrSafeInt<UINT64>(i2)).IsOverflow())
- {
- goto NOT_CAFFE;
- }
- }
- else
- {
- ClrSafeInt<INT64> si1(i1);
- if ((si1 + ClrSafeInt<INT64>(i2)).IsOverflow())
- {
- goto NOT_CAFFE;
- }
- }
- }
- else if (next->gtFlags & GTF_UNSIGNED)
- {
- ClrSafeInt<UINT32> si1(i1);
- if ((si1 + ClrSafeInt<UINT32>(i2)).IsOverflow())
- {
- goto NOT_CAFFE;
- }
- }
- else
- {
- ClrSafeInt<INT32> si1(i1);
- if ((si1 + ClrSafeInt<INT32>(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 <op> y where x could be any arbitrary expression into
- * x <op>= 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 <op> x and we want to fold that into
- * x <op>= 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 <op> 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()
{
@@ -141,558 +107,6 @@ bool Compiler::shouldDoubleAlign(
}
#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<regNumber>(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)
@@ -779,4452 +193,6 @@ regNumber Compiler::raUpdateRegStateForArg(RegState* regState, LclVarDsc* argDsc
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<DWORD>(regmask));
- BitScanForward(&reg, (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<<sh + cns]
- // when op1 is an enregistered variable, thus the op1Mask is RBM_NONE
- //
- if (hasTwoAddConst && (sh != 0) && (op1Mask == RBM_NONE))
- {
- op1Mask |= rpPredictRegPick(TYP_INT, PREDICT_REG, (lockedRegs | op1Mask | op2Mask));
- }
-
- //
- // On the ARM we will need at least one scratch register for trees that have this form:
- // [op1 + op2 + cns] or [op1 + op2<<sh + cns]
- // or for a float/double or long when we have both op1 and op2
- // or when we have an 'cns' that is too large for the ld/st instruction
- //
- if (hasTwoAddConst || varTypeIsFloating(type) || (type == TYP_LONG) || !codeGen->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, <lo32 bits>
- // movw/movt rT2, <hi32 bits>
- // 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</*ForCodeGen*/ false>(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 <op>= 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 <op>= 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 <op>= 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</*ForCodeGen*/ false>(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 <cond> 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 <then> subtree
-
- VarSetOps::Assign(this, compCurLife, tree->gtQmark.gtThenLiveSet);
-
- if (type == TYP_VOID)
- {
- /* Evaluate the <then> 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 <then> 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 <else> 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 = <already set as we want it to be>
-
- 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</*ForCodeGen*/ false>(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</*ForCodeGen*/ false>(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</*ForCodeGen*/ false>(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
@@ -5299,1313 +267,6 @@ bool Compiler::rpMustCreateEBPFrame(INDEBUG(const char** wbReason))
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));
- }
-
- raNewBlocks = false;
- rpPredictAssignAgain = false;
- rpPasses = 0;
-
- bool mustPredict = true;
- unsigned stmtNum = 0;
- unsigned oldStkPredict = DUMMY_INIT(~0);
- VARSET_TP oldLclRegIntf[REG_COUNT];
-
- for (unsigned i = 0; i < REG_COUNT; i++)
- {
- VarSetOps::AssignNoCopy(this, oldLclRegIntf[i], VarSetOps::MakeEmpty(this));
- }
-
- while (true)
- {
- /* 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)
- {
- /* 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 */;
- }
-
-#ifdef DEBUG
- if (JitConfig.JitAssertOnMaxRAPasses())
- {
- noway_assert(rpPasses < rpPassesMax &&
- "This may not a bug, but dev team should look and see what is happening");
- }
-#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");
- }
-
-#ifdef DEBUG
- if (verbose)
- {
- 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);
- }
-#endif
-
- /* Zero the variable/register interference graph */
- for (unsigned i = 0; i < REG_COUNT; i++)
- {
- VarSetOps::ClearD(this, raLclRegIntf[i]);
- }
-
- // 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)
- {
- 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)
- {
- noway_assert(genReturnBB);
- LclVarDsc* localTmp = &lvaTable[genReturnLocal];
- if (localTmp->lvTracked)
- {
- rpRecordRegIntf(RBM_PINVOKE_TCB | RBM_PINVOKE_FRAME,
- VarSetOps::MakeSingleton(this, localTmp->lvVarIndex) DEBUGARG("genReturnLocal"));
- }
- }
- }
-
-#ifdef _TARGET_ARM_
- if (compFloatingPointUsed)
- {
- 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++)
- {
- if (varDsc->lvMustInit && varTypeIsFloating(varDsc->TypeGet()))
- {
- hasMustInitFloat = true;
- break;
- }
- }
-
- 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
- }
-
- if (rpPasses == 1)
- {
- if ((opts.compFlags & CLFLG_REGVAR) == 0)
- goto ALL_DONE;
-
- if (rpPredictAssignAgain)
- mustPredict = true;
-#ifdef DEBUG
- if (fJitNoRegLoc)
- goto ALL_DONE;
-#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)
- {
- 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
- }
-
- /* Record the set of registers that we need */
- codeGen->regSet.rsClearRegsModified();
- if (regUsed != RBM_NONE)
- {
- codeGen->regSet.rsSetRegsModified(regUsed);
- }
-
- /* We need genFullPtrRegMap if :
- * The method is fully interruptible, or
- * We are generating an EBP-less frame (for stack-pointer deltas)
- */
-
- genFullPtrRegMap = (genInterruptible || !codeGen->isFramePointerUsed());
-
- raMarkStkVars();
-#ifdef DEBUG
- if (verbose)
- {
- printf("# rpPasses was %u for %s\n", rpPasses, info.compFullName);
- printf(" rpStkPredict was %u\n", rpStkPredict);
- }
-#endif
- rpRegAllocDone = true;
-}
-
-#endif // LEGACY_BACKEND
-
/*****************************************************************************
*
* Mark all variables as to whether they live on the stack frame
@@ -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<VarRegPrediction*>(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<regNumber>(rpBestRecordedPrediction[k].m_regNum));
- lvaTable[k].SetOtherReg(static_cast<regNumber>(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,262 +153,6 @@ 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
- */
-
-regMaskTP RegSet::rsUseIfZero(regMaskTP regs, regMaskTP includeHint)
-{
- return regs ? regs : includeHint;
-}
-
-/*****************************************************************************
- * Excludes 'excludeHint' if it results in a non-empty mask
- */
-
-regMaskTP RegSet::rsExcludeHint(regMaskTP regs, regMaskTP excludeHint)
-{
- regMaskTP OKmask = regs & ~excludeHint;
- return OKmask ? OKmask : regs;
-}
-
-/*****************************************************************************
- * Narrows choice by 'narrowHint' if it results in a non-empty mask
- */
-
-regMaskTP RegSet::rsNarrowHint(regMaskTP regs, regMaskTP narrowHint)
-{
- regMaskTP narrowed = regs & narrowHint;
- return narrowed ? narrowed : regs;
-}
-
-/*****************************************************************************
- * Excludes 'exclude' from regs if non-zero, or from RBM_ALLINT
- */
-
-regMaskTP RegSet::rsMustExclude(regMaskTP regs, regMaskTP exclude)
-{
- // 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);
-
- return OKmask;
-}
-
-/*****************************************************************************
- *
- * The following returns a mask that yields all free registers.
- */
-
-// inline
-regMaskTP RegSet::rsRegMaskFree()
-{
- /* Any register that is locked must also be marked as 'used' */
-
- assert((rsMaskUsed & rsMaskLock) == rsMaskLock);
-
- /* Any register that isn't used and doesn't hold a variable is free */
-
- return RBM_ALLINT & ~(rsMaskUsed | rsMaskVars | rsMaskResvd);
-}
-
-/*****************************************************************************
- *
- * The following returns a mask of registers that may be grabbed.
- */
-
-// inline
-regMaskTP RegSet::rsRegMaskCanGrab()
-{
- /* 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_
-
- // 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))
- {
- GenTree* regHolds = rsUsedTree[reg];
- if ((regHolds != NULL) && (regHolds->TypeGet() == TYP_STRUCT))
- {
- structArgMask |= genRegMask(reg);
- }
- }
- result &= ~structArgMask;
-#endif
-
- return result;
-}
-
-/*****************************************************************************
- *
- * Pick a free register. It is guaranteed that a register is available.
- * Note that rsPickReg() can spill a register, whereas rsPickFreeReg() will not.
- */
-
-// inline
-regNumber RegSet::rsPickFreeReg(regMaskTP regMaskHint)
-{
- regMaskTP freeRegs = rsRegMaskFree();
- assert(freeRegs != RBM_NONE);
-
- regMaskTP regs = rsNarrowHint(freeRegs, regMaskHint);
-
- return rsGrabReg(regs);
-}
-
-/*****************************************************************************
- *
- * Mark the given set of registers as used and locked.
- */
-
-// inline
-void RegSet::rsLockReg(regMaskTP regMask)
-{
- /* Must not be already marked as either used or locked */
-
- assert((rsMaskUsed & regMask) == 0);
- rsMaskUsed |= regMask;
- assert((rsMaskLock & regMask) == 0);
- rsMaskLock |= regMask;
-}
-
-/*****************************************************************************
- *
- * Mark an already used set of registers as locked.
- */
-
-// inline
-void RegSet::rsLockUsedReg(regMaskTP regMask)
-{
- /* Must not be already marked as locked. Must be already marked as used. */
-
- assert((rsMaskLock & regMask) == 0);
- assert((rsMaskUsed & regMask) == regMask);
-
- rsMaskLock |= regMask;
-}
-
-/*****************************************************************************
- *
- * Mark the given set of registers as no longer used/locked.
- */
-
-// inline
-void RegSet::rsUnlockReg(regMaskTP regMask)
-{
- /* Must be currently marked as both used and locked */
-
- assert((rsMaskUsed & regMask) == regMask);
- rsMaskUsed -= regMask;
- assert((rsMaskLock & regMask) == regMask);
- rsMaskLock -= regMask;
-}
-
-/*****************************************************************************
- *
- * Mark the given set of registers as no longer locked.
- */
-
-// inline
-void RegSet::rsUnlockUsedReg(regMaskTP regMask)
-{
- /* Must be currently marked as both used and locked */
-
- assert((rsMaskUsed & regMask) == regMask);
- assert((rsMaskLock & regMask) == regMask);
- rsMaskLock -= regMask;
-}
-
-/*****************************************************************************
- *
- * Mark the given set of registers as used and locked. It may already have
- * been marked as used.
- */
-
-// inline
-void RegSet::rsLockReg(regMaskTP regMask, regMaskTP* usedMask)
-{
- /* Is it already marked as used? */
-
- regMaskTP used = (rsMaskUsed & regMask);
- regMaskTP unused = (regMask & ~used);
-
- if (used)
- rsLockUsedReg(used);
-
- if (unused)
- rsLockReg(unused);
-
- *usedMask = used;
-}
-
-/*****************************************************************************
- *
- * Mark the given set of registers as no longer
- */
-
-// inline
-void RegSet::rsUnlockReg(regMaskTP regMask, regMaskTP usedMask)
-{
- regMaskTP unused = (regMask & ~usedMask);
-
- if (usedMask)
- rsUnlockUsedReg(usedMask);
-
- 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
@@ -433,39 +164,7 @@ 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
/*****************************************************************************/
@@ -477,325 +176,8 @@ void RegTracker::rsTrackRegIntCns(regNumber reg, ssize_t val)
/* 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)
@@ -812,12 +194,6 @@ RegSet::RegSet(Compiler* compiler, GCInfo& gcInfo) : m_rsCompiler(compiler), m_r
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_
@@ -832,298 +208,21 @@ RegSet::RegSet(Compiler* compiler, GCInfo& gcInfo) : m_rsCompiler(compiler), m_r
#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
- )
+RegSet::SpillDsc* RegSet::rsGetSpillInfo(GenTree* tree, regNumber reg, SpillDsc** pPrevDsc)
{
/* 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;
@@ -1134,235 +233,10 @@ RegSet::SpillDsc* RegSet::rsGetSpillInfo(GenTree* tree,
{
*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
@@ -1378,11 +252,6 @@ void RegTracker::rsTrackRegLclVar(regNumber reg, unsigned var)
#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)
{
@@ -1392,68 +261,10 @@ void RegTracker::rsTrackRegLclVar(regNumber reg, unsigned var)
/* 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 */
@@ -1462,62 +273,8 @@ void RegTracker::rsTrackRegCopy(regNumber reg1, regNumber reg2)
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'.
//
@@ -1543,12 +300,11 @@ void RegSet::rsSpillTree(regNumber reg, GenTree* tree, unsigned regIdx /* =0 */)
GenTreeCall* call = nullptr;
var_types treeType;
-#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_)
+#if defined(_TARGET_ARM_)
GenTreePutArgSplit* splitArg = nullptr;
GenTreeMultiRegOp* multiReg = nullptr;
#endif
-#ifndef LEGACY_BACKEND
if (tree->IsMultiRegCall())
{
call = tree->AsCall();
@@ -1568,7 +324,6 @@ void RegSet::rsSpillTree(regNumber reg, GenTree* tree, unsigned regIdx /* =0 */)
}
#endif // _TARGET_ARM_
else
-#endif // !LEGACY_BACKEND
{
treeType = tree->TypeGet();
}
@@ -1589,16 +344,6 @@ void RegSet::rsSpillTree(regNumber reg, GenTree* tree, unsigned regIdx /* =0 */)
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.
@@ -1635,27 +380,14 @@ void RegSet::rsSpillTree(regNumber reg, GenTree* tree, unsigned regIdx /* =0 */)
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_)
+#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 // !CPU_LONG_USES_REGPAIR && !_TARGET_ARM_
+#endif // !_TARGET_ARM_
// Are any registers free for spillage?
SpillDsc* spill = SpillDsc::alloc(m_rsCompiler, this, tempType);
@@ -1667,87 +399,19 @@ void RegSet::rsSpillTree(regNumber reg, GenTree* tree, unsigned regIdx /* =0 */)
// 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;
@@ -1767,10 +431,6 @@ void RegSet::rsSpillTree(regNumber reg, GenTree* tree, unsigned regIdx /* =0 */)
// 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)
@@ -1790,10 +450,9 @@ void RegSet::rsSpillTree(regNumber reg, GenTree* tree, unsigned regIdx /* =0 */)
multiReg->SetRegSpillFlagByIdx(regFlags, regIdx);
}
#endif // _TARGET_ARM_
-#endif //! LEGACY_BACKEND
}
-#if defined(_TARGET_X86_) && !FEATURE_STACK_FP_X87
+#if defined(_TARGET_X86_)
/*****************************************************************************
*
* Spill the top of the FP x87 stack.
@@ -1823,342 +482,14 @@ void RegSet::rsSpillFPStack(GenTreeCall* call)
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
+#endif // defined(_TARGET_X86_)
/*****************************************************************************
*
@@ -2170,13 +501,6 @@ TempDsc* RegSet::rsGetSpillTempWord(regNumber reg, SpillDsc* dsc, SpillDsc* prev
{
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;
@@ -2192,200 +516,6 @@ TempDsc* RegSet::rsGetSpillTempWord(regNumber reg, SpillDsc* dsc, SpillDsc* prev
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.
@@ -2407,8 +537,6 @@ regNumber RegSet::rsUnspillOneReg(GenTree* tree, regNumber oldReg, KeepReg willK
//
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);
@@ -2425,7 +553,7 @@ TempDsc* RegSet::rsUnspillInPlace(GenTree* tree, regNumber oldReg, unsigned regI
flags &= ~GTF_SPILLED;
call->SetRegSpillFlagByIdx(flags, regIdx);
}
-#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_)
+#if defined(_TARGET_ARM_)
else if (tree->OperIsPutArgSplit())
{
GenTreePutArgSplit* splitArg = tree->AsPutArgSplit();
@@ -2440,7 +568,7 @@ TempDsc* RegSet::rsUnspillInPlace(GenTree* tree, regNumber oldReg, unsigned regI
flags &= ~GTF_SPILLED;
multiReg->SetRegSpillFlagByIdx(flags, regIdx);
}
-#endif // !LEGACY_BACKEND && _TARGET_ARM_
+#endif // _TARGET_ARM_
else
{
tree->gtFlags &= ~GTF_SPILLED;
@@ -2458,734 +586,11 @@ TempDsc* RegSet::rsUnspillInPlace(GenTree* tree, regNumber oldReg, unsigned regI
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
-
- rsAddrUnspillOper(unspillAddr);
-
-#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, &regLoUsed);
- }
-
- /* 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);
-
- /* Is only one use of the register left? */
-
- if (!dsc->spillMoreMultis)
- {
- rsMaskMult -= regMask;
- }
-
-#ifdef DEBUG
- if (m_rsCompiler->verbose)
- {
- 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);
- }
-#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)
- {
- return REG_NA;
- }
-
- for (regNumber reg = REG_INT_FIRST; reg <= REG_INT_LAST; reg = REG_NEXT(reg))
- {
- 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
- }
- }
- }
-
- /* There was not an exact match */
-
- return closeReg; /* will always be REG_NA when closeDelta is NULL */
-}
-
-/*****************************************************************************
- *
- * 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()
-{
- 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;
- }
- }
-
- /* Preserve variables known to not be pointers */
-
- if (rsRegValues[reg].rvdKind == RV_LCL_VAR)
- {
- if (!varTypeIsGC(compiler->lvaTable[rsRegValues[reg].rvdLclVarNum].TypeGet()))
- {
- continue;
- }
- }
-
- rsRegValues[reg].rvdKind = RV_TRASH;
- }
-}
-
-/*****************************************************************************
- *
- * 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.
- *
- */
-
-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];
-
- if (!varTypeIsGC(varDsc->TypeGet()))
- {
- continue;
- }
-
- // Only stack locals got tracked.
- assert(!varDsc->lvRegister);
-
- rsRegValues[reg].rvdKind = RV_TRASH;
-
- result |= genRegMask(reg);
- }
- }
-
- return result;
-}
-
-/*****************************************************************************
- *
- * 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.
- */
-
-regNumber RegTracker::rsLclIsInReg(unsigned var)
-{
- assert(var < compiler->lvaCount);
-
- if (compiler->opts.MinOpts() || compiler->opts.compDbgCode)
- {
- return REG_NA;
- }
-
- /* return false if register var so genMarkLclVar can do its job */
-
- if (compiler->lvaTable[var].lvRegister)
- {
- return REG_NA;
- }
-
- 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;
- }
- }
-
- return REG_NA;
-}
-
-/*****************************************************************************/
-
-regPairNo RegTracker::rsLclIsInRegPair(unsigned var)
-{
- assert(var < compiler->lvaCount);
-
- if (compiler->opts.MinOpts() || compiler->opts.compDbgCode)
- {
- return REG_PAIR_NONE;
- }
-
- regValKind rvKind = RV_TRASH;
- regNumber regNo = DUMMY_INIT(REG_NA);
-
- for (regNumber reg = REG_FIRST; reg < REG_COUNT; reg = REG_NEXT(reg))
- {
- 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);
- }
- }
- }
-
- return REG_PAIR_NONE;
-}
-
-/*****************************************************************************/
-
-void RegTracker::rsTrashLclLong(unsigned var)
-{
- if (compiler->opts.MinOpts() || compiler->opts.compDbgCode)
- {
- return;
- }
-
- for (regNumber reg = REG_FIRST; reg < REG_COUNT; reg = REG_NEXT(reg))
- {
- if (rsTrackIsLclVarLng(rsRegValues[reg].rvdKind) && rsRegValues[reg].rvdLclVarNum == var)
- {
- rsRegValues[reg].rvdKind = RV_TRASH;
- }
- }
-}
-
-/*****************************************************************************
- *
- * 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)
- {
- return;
- }
-
- for (regNumber reg = REG_FIRST; reg < REG_COUNT; reg = REG_NEXT(reg))
- {
- if (rsRegValues[reg].rvdKind == RV_LCL_VAR && rsRegValues[reg].rvdLclVarNum == var)
- {
- rsRegValues[reg].rvdKind = RV_TRASH;
- }
- }
-}
-#endif // LEGACY_BACKEND
-
/*****************************************************************************
*
* A little helper to trash the given set of registers.
@@ -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.
@@ -3628,38 +951,6 @@ 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
* returns the next 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 = &regSet.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(&regSet, 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, &regSet, 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);
@@ -2301,65 +1992,6 @@ 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"
* of this type, else REG_NA if there are no more.
*/
@@ -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<ForCodeGen>::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<ForCodeGen>::UpdateLifeVar(GenTree* tree)
// if it's "x <op>=..." 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<ForCodeGen>::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<ForCodeGen>::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<ForCodeGen>::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<ForCodeGen>::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<ForCodeGen>::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