summaryrefslogtreecommitdiff
path: root/src/jit
diff options
context:
space:
mode:
Diffstat (limited to 'src/jit')
-rw-r--r--src/jit/compiler.cpp2
-rw-r--r--src/jit/compiler.h22
-rw-r--r--src/jit/compiler.hpp11
-rw-r--r--src/jit/importer.cpp13
-rw-r--r--src/jit/lclvars.cpp36
-rw-r--r--src/jit/morph.cpp59
-rw-r--r--src/jit/scopeinfo.cpp9
7 files changed, 80 insertions, 72 deletions
diff --git a/src/jit/compiler.cpp b/src/jit/compiler.cpp
index 15ed19c62d..378d83bd1f 100644
--- a/src/jit/compiler.cpp
+++ b/src/jit/compiler.cpp
@@ -704,7 +704,7 @@ var_types Compiler::getPrimitiveTypeForStruct(unsigned structSize, CORINFO_CLASS
// getArgTypeForStruct:
// Get the type that is used to pass values of the given struct type.
// If you have already retrieved the struct size then it should be
-// passed as the optional third argument, as this allows us to avoid
+// passed as the optional fourth argument, as this allows us to avoid
// an extra call to getClassSize(clsHnd)
//
// Arguments:
diff --git a/src/jit/compiler.h b/src/jit/compiler.h
index dc1f844a31..dec2343356 100644
--- a/src/jit/compiler.h
+++ b/src/jit/compiler.h
@@ -610,8 +610,12 @@ public:
unsigned char lvHasILStoreOp : 1; // there is at least one STLOC or STARG on this local
unsigned char lvHasMultipleILStoreOp : 1; // there is more than one STLOC on this local
- unsigned char lvIsTemp : 1; // Short-lifetime compiler temp (if lvIsParam is false), or implicit byref parameter
- // (if lvIsParam is true)
+ unsigned char lvIsTemp : 1; // Short-lifetime compiler temp
+
+#if defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)
+ unsigned char lvIsImplicitByRef : 1; // Set if the argument is an implicit byref.
+#endif // defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)
+
#if OPT_BOOL_OPS
unsigned char lvIsBoolean : 1; // set if variable is boolean
#endif
@@ -957,7 +961,7 @@ public:
private:
unsigned short m_lvRefCnt; // unweighted (real) reference count. For implicit by reference
- // parameters, this gets hijacked from fgMarkImplicitByRefArgs
+ // parameters, this gets hijacked from fgResetImplicitByRefRefCount
// through fgMarkDemotedImplicitByRefArgs, to provide a static
// appearance count (computed during address-exposed analysis)
// that fgMakeOutgoingStructArgCopy consults during global morph
@@ -3346,16 +3350,16 @@ public:
BOOL lvaIsOriginalThisReadOnly(); // return TRUE if there is no place in the code
// that writes to arg0
- // Struct parameters that are passed by reference are marked as both lvIsParam and lvIsTemp
- // (this is an overload of lvIsTemp because there are no temp parameters).
// For x64 this is 3, 5, 6, 7, >8 byte structs that are passed by reference.
// For ARM64, this is structs larger than 16 bytes that are passed by reference.
bool lvaIsImplicitByRefLocal(unsigned varNum)
{
#if defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)
- LclVarDsc* varDsc = &(lvaTable[varNum]);
- if (varDsc->lvIsParam && varDsc->lvIsTemp)
+ LclVarDsc* varDsc = lvaGetDesc(varNum);
+ if (varDsc->lvIsImplicitByRef)
{
+ assert(varDsc->lvIsParam);
+
assert(varTypeIsStruct(varDsc) || (varDsc->lvType == TYP_BYREF));
return true;
}
@@ -5667,8 +5671,8 @@ private:
void fgMorphStructField(GenTree* tree, GenTree* parent);
void fgMorphLocalField(GenTree* tree, GenTree* parent);
- // Identify which parameters are implicit byrefs, and flag their LclVarDscs.
- void fgMarkImplicitByRefArgs();
+ // Reset the refCount for implicit byrefs.
+ void fgResetImplicitByRefRefCount();
// Change implicit byrefs' types from struct to pointer, and for any that were
// promoted, create new promoted struct temps.
diff --git a/src/jit/compiler.hpp b/src/jit/compiler.hpp
index 0b30114f67..9b10542722 100644
--- a/src/jit/compiler.hpp
+++ b/src/jit/compiler.hpp
@@ -1756,8 +1756,15 @@ inline void LclVarDsc::incRefCnts(BasicBlock::weight_t weight, Compiler* comp, R
if (weight != 0)
{
// We double the weight of internal temps
- //
- if (lvIsTemp && (weight * 2 > weight))
+
+ bool doubleWeight = lvIsTemp;
+
+#if defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)
+ // and, for the time being, implict byref params
+ doubleWeight |= lvIsImplicitByRef;
+#endif // defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)
+
+ if (doubleWeight && (weight * 2 > weight))
{
weight *= 2;
}
diff --git a/src/jit/importer.cpp b/src/jit/importer.cpp
index 88638092fe..78c0b4fde0 100644
--- a/src/jit/importer.cpp
+++ b/src/jit/importer.cpp
@@ -9022,13 +9022,16 @@ GenTree* Compiler::impFixupStructReturnType(GenTree* op, CORINFO_CLASS_HANDLE re
// This LCL_VAR stays as a TYP_STRUCT
unsigned lclNum = op->gtLclVarCommon.gtLclNum;
- // Make sure this struct type is not struct promoted
- lvaTable[lclNum].lvIsMultiRegRet = true;
+ if (!lvaIsImplicitByRefLocal(lclNum))
+ {
+ // Make sure this struct type is not struct promoted
+ lvaTable[lclNum].lvIsMultiRegRet = true;
- // TODO-1stClassStructs: Handle constant propagation and CSE-ing of multireg returns.
- op->gtFlags |= GTF_DONT_CSE;
+ // TODO-1stClassStructs: Handle constant propagation and CSE-ing of multireg returns.
+ op->gtFlags |= GTF_DONT_CSE;
- return op;
+ return op;
+ }
}
if (op->gtOper == GT_CALL)
diff --git a/src/jit/lclvars.cpp b/src/jit/lclvars.cpp
index d7d0f11134..515e9348c0 100644
--- a/src/jit/lclvars.cpp
+++ b/src/jit/lclvars.cpp
@@ -1280,6 +1280,10 @@ void Compiler::lvaInitVarDsc(LclVarDsc* varDsc,
varDsc->lvStructGcCount = 1;
}
+#if defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)
+ varDsc->lvIsImplicitByRef = 0;
+#endif // defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)
+
// Set the lvType (before this point it is TYP_UNDEF).
#ifdef FEATURE_HFA
@@ -2163,6 +2167,10 @@ void Compiler::StructPromotionHelper::PromoteStructVar(unsigned lclNum)
fieldVarDsc->lvParentLcl = lclNum;
fieldVarDsc->lvIsParam = varDsc->lvIsParam;
#if defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)
+
+ // Reset the implicitByRef flag.
+ fieldVarDsc->lvIsImplicitByRef = 0;
+
// Do we have a parameter that can be enregistered?
//
if (varDsc->lvIsRegArg)
@@ -2490,11 +2498,12 @@ void Compiler::lvaSetStruct(unsigned varNum, CORINFO_CLASS_HANDLE typeHnd, bool
varDsc->lvExactSize = info.compCompHnd->getHeapClassSize(typeHnd);
}
- size_t lvSize = varDsc->lvSize();
- assert((lvSize % TARGET_POINTER_SIZE) ==
- 0); // The struct needs to be a multiple of TARGET_POINTER_SIZE bytes for getClassGClayout() to be valid.
- varDsc->lvGcLayout = getAllocator(CMK_LvaTable).allocate<BYTE>(lvSize / TARGET_POINTER_SIZE);
- unsigned numGCVars;
+ // Normalize struct types, and fill in GC info for all types
+ unsigned lvSize = varDsc->lvSize();
+ // The struct needs to be a multiple of TARGET_POINTER_SIZE bytes for getClassGClayout() to be valid.
+ assert((lvSize % TARGET_POINTER_SIZE) == 0);
+ varDsc->lvGcLayout = getAllocator(CMK_LvaTable).allocate<BYTE>(lvSize / TARGET_POINTER_SIZE);
+ unsigned numGCVars = 0;
var_types simdBaseType = TYP_UNKNOWN;
if (isValueClass)
{
@@ -2510,10 +2519,27 @@ void Compiler::lvaSetStruct(unsigned varNum, CORINFO_CLASS_HANDLE typeHnd, bool
{
numGCVars = 7;
}
+
varDsc->lvStructGcCount = numGCVars;
if (isValueClass)
{
+
+#if defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)
+ // Mark implicit byref struct parameters
+ if (varDsc->lvIsParam && !varDsc->lvIsStructField)
+ {
+ structPassingKind howToReturnStruct;
+ getArgTypeForStruct(typeHnd, &howToReturnStruct, this->info.compIsVarArgs, varDsc->lvExactSize);
+
+ if (howToReturnStruct == SPK_ByReference)
+ {
+ JITDUMP("Marking V%02i as a byref parameter\n", varNum);
+ varDsc->lvIsImplicitByRef = 1;
+ }
+ }
+#endif // defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)
+
#if FEATURE_SIMD
if (simdBaseType != TYP_UNKNOWN)
{
diff --git a/src/jit/morph.cpp b/src/jit/morph.cpp
index fbfa173311..83e069cebf 100644
--- a/src/jit/morph.cpp
+++ b/src/jit/morph.cpp
@@ -17061,9 +17061,8 @@ void Compiler::fgMorph()
fgUpdateFinallyTargetFlags();
/* For x64 and ARM64 we need to mark irregular parameters */
-
lvaRefCountState = RCS_EARLY;
- fgMarkImplicitByRefArgs();
+ fgResetImplicitByRefRefCount();
/* Promote struct locals if necessary */
fgPromoteStructs();
@@ -17491,63 +17490,29 @@ void Compiler::fgMorphLocalField(GenTree* tree, GenTree* parent)
}
//------------------------------------------------------------------------
-// fgMarkImplicitByRefArgs: Identify any by-value struct parameters which are "implicit by-reference";
-// i.e. which the ABI requires to be passed by making a copy in the caller and
-// passing its address to the callee. Mark their `LclVarDsc`s such that
-// `lvaIsImplicitByRefLocal` will return true for them.
+// fgResetImplicitByRefRefCount: Clear the ref count field of all implicit byrefs
-void Compiler::fgMarkImplicitByRefArgs()
+void Compiler::fgResetImplicitByRefRefCount()
{
#if (defined(_TARGET_AMD64_) && !defined(UNIX_AMD64_ABI)) || defined(_TARGET_ARM64_)
#ifdef DEBUG
if (verbose)
{
- printf("\n*************** In fgMarkImplicitByRefs()\n");
+ printf("\n*************** In fgResetImplicitByRefRefCount()\n");
}
#endif // DEBUG
- for (unsigned lclNum = 0; lclNum < info.compArgsCount; lclNum++)
+ for (unsigned lclNum = 0; lclNum < info.compArgsCount; ++lclNum)
{
- LclVarDsc* varDsc = &lvaTable[lclNum];
+ LclVarDsc* varDsc = lvaGetDesc(lclNum);
- if (varDsc->lvIsParam && varTypeIsStruct(varDsc))
+ if (varDsc->lvIsImplicitByRef)
{
- size_t size = varDsc->lvExactSize;
- assert(size == info.compCompHnd->getClassSize(varDsc->lvVerTypeInfo.GetClassHandle()));
-
- bool isPassedByReference;
-#if defined(_TARGET_AMD64_)
- isPassedByReference = (size > REGSIZE_BYTES || (size & (size - 1)) != 0);
-#elif defined(_TARGET_ARM64_)
- if (size > TARGET_POINTER_SIZE)
- {
- CORINFO_CLASS_HANDLE clsHnd = varDsc->lvVerTypeInfo.GetClassHandleForValueClass();
- structPassingKind howToPassStruct;
- var_types type =
- getArgTypeForStruct(clsHnd, &howToPassStruct, this->info.compIsVarArgs, varDsc->lvExactSize);
- isPassedByReference = (howToPassStruct == SPK_ByReference);
- }
- else
- {
- isPassedByReference = false;
- }
-#endif
-
- if (isPassedByReference)
- {
- // Previously nobody was ever setting lvIsParam and lvIsTemp on the same local
- // So I am now using it to indicate that this is one of the weird implicit
- // by ref locals.
- // The address taken cleanup will look for references to locals marked like
- // this, and transform them appropriately.
- varDsc->lvIsTemp = 1;
-
- // Clear the ref count field; fgMarkAddressTakenLocals will increment it per
- // appearance of implicit-by-ref param so that call arg morphing can do an
- // optimization for single-use implicit-by-ref params whose single use is as
- // an outgoing call argument.
- varDsc->setLvRefCnt(0, RCS_EARLY);
- }
+ // Clear the ref count field; fgMarkAddressTakenLocals will increment it per
+ // appearance of implicit-by-ref param so that call arg morphing can do an
+ // optimization for single-use implicit-by-ref params whose single use is as
+ // an outgoing call argument.
+ varDsc->setLvRefCnt(0, RCS_EARLY);
}
}
diff --git a/src/jit/scopeinfo.cpp b/src/jit/scopeinfo.cpp
index be6fb8ce6c..d396b333a4 100644
--- a/src/jit/scopeinfo.cpp
+++ b/src/jit/scopeinfo.cpp
@@ -301,14 +301,17 @@ void CodeGenInterface::siVarLoc::siFillStackVarLoc(
// size is not 1, 2, 4 or 8 bytes in size. During fgMorph, the compiler modifies
// the IR to comply with the ABI and therefore changes the type of the lclVar
// that holds the struct from TYP_STRUCT to TYP_BYREF but it gives us a hint that
- // this is still a struct by setting the lvIsTemp flag.
+ // this is still a struct by setting the lvIsImplicitByref flag.
// The same is true for ARM64 and structs > 16 bytes.
- // (See Compiler::fgMarkImplicitByRefArgs in Morph.cpp for further detail)
+ //
+ // See lvaSetStruct for further detail.
+ //
// Now, the VM expects a special enum for these type of local vars: VLT_STK_BYREF
// to accomodate for this situation.
- if (varDsc->lvType == TYP_BYREF && varDsc->lvIsTemp)
+ if (varDsc->lvIsImplicitByRef)
{
assert(varDsc->lvIsParam);
+ assert(varDsc->lvType == TYP_BYREF);
this->vlType = VLT_STK_BYREF;
}
else