summaryrefslogtreecommitdiff
path: root/src/jit
diff options
context:
space:
mode:
authorLubomir Litchev <LLITCHEV@users.noreply.github.com>2016-02-29 21:29:51 -0800
committerLubomir Litchev <LLITCHEV@users.noreply.github.com>2016-02-29 21:29:51 -0800
commit444ca211c12c1f424acc1fa275272f284dcccb75 (patch)
treefac989dea90d6eef282b20670237c81c6b841bc0 /src/jit
parent6f6d0f0d797cd6b04ec8678245f73af5b64e89e3 (diff)
parent31cb7975335b33bd04268216e3409a4cd6e9dc29 (diff)
downloadcoreclr-444ca211c12c1f424acc1fa275272f284dcccb75.tar.gz
coreclr-444ca211c12c1f424acc1fa275272f284dcccb75.tar.bz2
coreclr-444ca211c12c1f424acc1fa275272f284dcccb75.zip
Merge pull request #3351 from LLITCHEV/Issue2757
Add support for emitting GC-ness of the second return register for 16…
Diffstat (limited to 'src/jit')
-rw-r--r--src/jit/codegen.h29
-rw-r--r--src/jit/codegencommon.cpp19
-rw-r--r--src/jit/codegenxarch.cpp134
-rw-r--r--src/jit/emit.cpp125
-rw-r--r--src/jit/emit.h29
-rw-r--r--src/jit/emitxarch.cpp73
-rw-r--r--src/jit/emitxarch.h84
-rw-r--r--src/jit/instr.cpp71
-rw-r--r--src/jit/instr.h35
9 files changed, 378 insertions, 221 deletions
diff --git a/src/jit/codegen.h b/src/jit/codegen.h
index 574cf1cf9e..a0423e8972 100644
--- a/src/jit/codegen.h
+++ b/src/jit/codegen.h
@@ -467,23 +467,25 @@ protected:
void genPrologPadForReJit();
- void genEmitCall(int callType,
- CORINFO_METHOD_HANDLE methHnd,
- INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO* sigInfo)
- void* addr
- X86_ARG(ssize_t argSize),
- emitAttr retSize,
- IL_OFFSETX ilOffset,
- regNumber base = REG_NA,
- bool isJump = false,
- bool isNoGC = false);
-
+ void genEmitCall(int callType,
+ CORINFO_METHOD_HANDLE methHnd,
+ INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO* sigInfo)
+ void* addr
+ X86_ARG(ssize_t argSize),
+ emitAttr retSize
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(emitAttr secondRetSize),
+ IL_OFFSETX ilOffset,
+ regNumber base = REG_NA,
+ bool isJump = false,
+ bool isNoGC = false);
+
void genEmitCall(int callType,
CORINFO_METHOD_HANDLE methHnd,
INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO* sigInfo)
GenTreeIndir* indir
X86_ARG(ssize_t argSize),
- emitAttr retSize,
+ emitAttr retSize
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(emitAttr secondRetSize),
IL_OFFSETX ilOffset);
@@ -937,7 +939,8 @@ public :
void instEmit_indCall(GenTreePtr call,
size_t argSize,
- emitAttr retSize);
+ emitAttr retSize
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(emitAttr secondRetSize));
void instEmit_RM (instruction ins,
GenTreePtr tree,
diff --git a/src/jit/codegencommon.cpp b/src/jit/codegencommon.cpp
index fa81516022..caa0a21b57 100644
--- a/src/jit/codegencommon.cpp
+++ b/src/jit/codegencommon.cpp
@@ -9322,15 +9322,17 @@ void CodeGen::genFnEpilog(BasicBlock* block)
methHnd,
INDEBUG_LDISASM_COMMA(nullptr)
addr,
- 0, /* argSize */
- EA_UNKNOWN, /* retSize */
+ 0, // argSize
+ EA_UNKNOWN, // retSize
gcInfo.gcVarPtrSetCur,
gcInfo.gcRegGCrefSetCur,
gcInfo.gcRegByrefSetCur,
- BAD_IL_OFFSET, /* IL offset*/
- indCallReg, /* ireg */
- REG_NA, 0, 0, /* xreg, xmul, disp */
- true); /* isJump */
+ BAD_IL_OFFSET, // IL offset
+ indCallReg, // ireg
+ REG_NA, // xreg
+ 0, // xmul
+ 0, // disp
+ true); // isJump
}
else
{
@@ -9673,8 +9675,9 @@ void CodeGen::genFnEpilog(BasicBlock* block)
methHnd,
INDEBUG_LDISASM_COMMA(nullptr)
addrInfo.addr,
- 0, /* argSize */
- EA_UNKNOWN, /* retSize */
+ 0, // argSize
+ EA_UNKNOWN // retSize
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(EA_UNKNOWN), // secondRetSize
gcInfo.gcVarPtrSetCur,
gcInfo.gcRegGCrefSetCur,
gcInfo.gcRegByrefSetCur,
diff --git a/src/jit/codegenxarch.cpp b/src/jit/codegenxarch.cpp
index 1dec8ba732..94c031423b 100644
--- a/src/jit/codegenxarch.cpp
+++ b/src/jit/codegenxarch.cpp
@@ -5163,16 +5163,17 @@ void CodeGen::genTransferRegGCState(regNumber dst, regNumber src)
// pass in 'addr' for a relative call or 'base' for a indirect register call
// methHnd - optional, only used for pretty printing
// retSize - emitter type of return for GC purposes, should be EA_BYREF, EA_GCREF, or EA_PTRSIZE(not GC)
-void CodeGen::genEmitCall(int callType,
- CORINFO_METHOD_HANDLE methHnd,
- INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO* sigInfo)
- void* addr
- X86_ARG(ssize_t argSize),
- emitAttr retSize,
- IL_OFFSETX ilOffset,
- regNumber base,
- bool isJump,
- bool isNoGC)
+void CodeGen::genEmitCall(int callType,
+ CORINFO_METHOD_HANDLE methHnd,
+ INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO* sigInfo)
+ void* addr
+ X86_ARG(ssize_t argSize),
+ emitAttr retSize
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(emitAttr secondRetSize),
+ IL_OFFSETX ilOffset,
+ regNumber base,
+ bool isJump,
+ bool isNoGC)
{
#if !defined(_TARGET_X86_)
ssize_t argSize = 0;
@@ -5182,7 +5183,8 @@ void CodeGen::genEmitCall(int callType,
INDEBUG_LDISASM_COMMA(sigInfo)
addr,
argSize,
- retSize,
+ retSize
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(secondRetSize),
gcInfo.gcVarPtrSetCur,
gcInfo.gcRegGCrefSetCur,
gcInfo.gcRegByrefSetCur,
@@ -5195,13 +5197,14 @@ void CodeGen::genEmitCall(int callType,
// generates an indirect call via addressing mode (call []) given an indir node
// methHnd - optional, only used for pretty printing
// retSize - emitter type of return for GC purposes, should be EA_BYREF, EA_GCREF, or EA_PTRSIZE(not GC)
-void CodeGen::genEmitCall(int callType,
- CORINFO_METHOD_HANDLE methHnd,
- INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO* sigInfo)
- GenTreeIndir* indir
- X86_ARG(ssize_t argSize),
- emitAttr retSize,
- IL_OFFSETX ilOffset)
+void CodeGen::genEmitCall(int callType,
+ CORINFO_METHOD_HANDLE methHnd,
+ INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO* sigInfo)
+ GenTreeIndir* indir
+ X86_ARG(ssize_t argSize),
+ emitAttr retSize
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(emitAttr secondRetSize),
+ IL_OFFSETX ilOffset)
{
#if !defined(_TARGET_X86_)
ssize_t argSize = 0;
@@ -5213,7 +5216,8 @@ void CodeGen::genEmitCall(int callType,
INDEBUG_LDISASM_COMMA(sigInfo)
nullptr,
argSize,
- retSize,
+ retSize
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(secondRetSize),
gcInfo.gcVarPtrSetCur,
gcInfo.gcRegGCrefSetCur,
gcInfo.gcRegByrefSetCur,
@@ -5486,7 +5490,6 @@ bool CodeGen::genEmitOptimizedGCWriteBarrier(GCInfo::WriteBarrierForm writeBarri
void CodeGen::genCallInstruction(GenTreePtr node)
{
GenTreeCall *call = node->AsCall();
-
assert(call->gtOper == GT_CALL);
gtCallTypes callType = (gtCallTypes)call->gtCallType;
@@ -5520,20 +5523,22 @@ void CodeGen::genCallInstruction(GenTreePtr node)
GenTreePtr putArgRegNode = argListPtr->gtOp.gtOp1;
assert(putArgRegNode->gtOper == GT_PUTARG_REG);
regNumber argReg = REG_NA;
+
if (iterationNum == 0)
{
argReg = curArgTabEntry->regNum;
}
- else if (iterationNum == 1)
- {
- argReg = curArgTabEntry->otherRegNum;
- }
else
{
- assert(false); // Illegal state.
+ assert(iterationNum == 1);
+ argReg = curArgTabEntry->otherRegNum;
}
genConsumeReg(putArgRegNode);
+
+ // Validate the putArgRegNode has the right type.
+ assert(putArgRegNode->TypeGet() == compiler->GetTypeFromClassificationAndSizes(curArgTabEntry->structDesc.eightByteClassifications[iterationNum],
+ curArgTabEntry->structDesc.eightByteSizes[iterationNum]));
if (putArgRegNode->gtRegNum != argReg)
{
inst_RV_RV(ins_Move_Extend(putArgRegNode->TypeGet(), putArgRegNode->InReg()), argReg, putArgRegNode->gtRegNum);
@@ -5659,16 +5664,34 @@ void CodeGen::genCallInstruction(GenTreePtr node)
genDefineTempLabel(genCreateTempLabel());
}
- // Determine return value size.
+ // Determine return value size(s).
emitAttr retSize = EA_PTRSIZE;
- if (call->gtType == TYP_REF ||
- call->gtType == TYP_ARRAY)
+
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+ emitAttr secondRetSize = EA_UNKNOWN;
+ if (varTypeIsStruct(call->gtType))
{
- retSize = EA_GCREF;
+ // Make sure it is a multi-register returned struct,
+ // otherwise, for a struct passed in a single register
+ // the call would have a normalized type that is not a struct type.
+ assert(call->structDesc.passedInRegisters &&
+ (call->structDesc.eightByteCount == CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_RETURN_IN_REGISTERS));
+
+ retSize = emitTypeSize(compiler->getEightByteType(call->structDesc, 0));
+ secondRetSize = emitTypeSize(compiler->getEightByteType(call->structDesc, 1));
}
- else if (call->gtType == TYP_BYREF)
+ else
+#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
{
- retSize = EA_BYREF;
+ if (call->gtType == TYP_REF ||
+ call->gtType == TYP_ARRAY)
+ {
+ retSize = EA_GCREF;
+ }
+ else if (call->gtType == TYP_BYREF)
+ {
+ retSize = EA_BYREF;
+ }
}
bool fPossibleSyncHelperCall = false;
@@ -5713,7 +5736,8 @@ void CodeGen::genCallInstruction(GenTreePtr node)
INDEBUG_LDISASM_COMMA(sigInfo)
(void*) target->AsIndir()->Base()->AsIntConCommon()->IconValue()
X86_ARG(argSizeForEmitter),
- retSize,
+ retSize
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(secondRetSize),
ilOffset);
}
else
@@ -5725,7 +5749,8 @@ void CodeGen::genCallInstruction(GenTreePtr node)
INDEBUG_LDISASM_COMMA(sigInfo)
target->AsIndir()
X86_ARG(argSizeForEmitter),
- retSize,
+ retSize
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(secondRetSize),
ilOffset);
}
}
@@ -5739,7 +5764,8 @@ void CodeGen::genCallInstruction(GenTreePtr node)
INDEBUG_LDISASM_COMMA(sigInfo)
nullptr //addr
X86_ARG(argSizeForEmitter),
- retSize,
+ retSize
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(secondRetSize),
ilOffset,
genConsumeReg(target));
}
@@ -5752,7 +5778,8 @@ void CodeGen::genCallInstruction(GenTreePtr node)
INDEBUG_LDISASM_COMMA(sigInfo)
(void*) call->gtEntryPoint.addr
X86_ARG(argSizeForEmitter),
- retSize,
+ retSize
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(secondRetSize),
ilOffset);
}
#endif
@@ -5808,7 +5835,8 @@ void CodeGen::genCallInstruction(GenTreePtr node)
INDEBUG_LDISASM_COMMA(sigInfo)
addr
X86_ARG(argSizeForEmitter),
- retSize,
+ retSize
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(secondRetSize),
ilOffset);
}
@@ -8450,11 +8478,8 @@ CodeGen::genCreateAndStoreGCInfoX64(unsigned codeSize, unsigned prologSize DEBUG
void CodeGen::genEmitHelperCall(unsigned helper,
int argSize,
- emitAttr retSize
-#ifndef LEGACY_BACKEND
- ,regNumber callTargetReg /*= REG_NA */
-#endif // !LEGACY_BACKEND
- )
+ emitAttr retSize,
+ regNumber callTargetReg)
{
void* addr = nullptr;
void* pAddr = nullptr;
@@ -8511,19 +8536,20 @@ void CodeGen::genEmitHelperCall(unsigned helper,
}
getEmitter()->emitIns_Call(callType,
- compiler->eeFindHelper(helper),
- INDEBUG_LDISASM_COMMA(nullptr)
- addr,
- argSize,
- retSize,
- gcInfo.gcVarPtrSetCur,
- gcInfo.gcRegGCrefSetCur,
- gcInfo.gcRegByrefSetCur,
- BAD_IL_OFFSET, /* IL offset */
- callTarget, /* ireg */
- REG_NA, 0, 0, /* xreg, xmul, disp */
- false, /* isJump */
- emitter::emitNoGChelper(helper));
+ compiler->eeFindHelper(helper),
+ INDEBUG_LDISASM_COMMA(nullptr)
+ addr,
+ argSize,
+ retSize
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(EA_UNKNOWN),
+ gcInfo.gcVarPtrSetCur,
+ gcInfo.gcRegGCrefSetCur,
+ gcInfo.gcRegByrefSetCur,
+ BAD_IL_OFFSET, // IL offset
+ callTarget, // ireg
+ REG_NA, 0, 0, // xreg, xmul, disp
+ false, // isJump
+ emitter::emitNoGChelper(helper));
regTracker.rsTrashRegSet(killMask);
diff --git a/src/jit/emit.cpp b/src/jit/emit.cpp
index 35b16b679a..493d13cde0 100644
--- a/src/jit/emit.cpp
+++ b/src/jit/emit.cpp
@@ -2953,6 +2953,36 @@ void emitter::emitDispVarSet()
/*****************************************************************************/
#endif//DEBUG
+
+#if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+//------------------------------------------------------------------------
+// emitSetSecondRetRegGCType: Sets the GC type of the second return register for instrDescCGCA struct.
+//
+// Arguments:
+// id - The large call instr descriptor to set the second GC return register type on.
+// secondRetSize - The EA_SIZE for second return register type.
+//
+// Return Value:
+// None
+//
+
+void emitter::emitSetSecondRetRegGCType(instrDescCGCA* id, emitAttr secondRetSize)
+{
+ if (EA_IS_GCREF(secondRetSize))
+ {
+ id->idSecondGCref(GCT_GCREF);
+ }
+ else if (EA_IS_BYREF(secondRetSize))
+ {
+ id->idSecondGCref(GCT_BYREF);
+ }
+ else
+ {
+ id->idSecondGCref(GCT_NONE);
+ }
+}
+#endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+
/*****************************************************************************
*
* Allocate an instruction descriptor for an indirect call.
@@ -2964,29 +2994,36 @@ void emitter::emitDispVarSet()
* address mode displacement.
*/
-emitter::instrDesc * emitter::emitNewInstrCallInd(int argCnt,
- ssize_t disp,
- VARSET_VALARG_TP GCvars,
- regMaskTP gcrefRegs,
- regMaskTP byrefRegs,
- emitAttr retSizeIn)
+emitter::instrDesc * emitter::emitNewInstrCallInd(int argCnt,
+ ssize_t disp,
+ VARSET_VALARG_TP GCvars,
+ regMaskTP gcrefRegs,
+ regMaskTP byrefRegs,
+ emitAttr retSizeIn
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(emitAttr secondRetSize))
{
- emitAttr retSize = retSizeIn ? EA_ATTR(retSizeIn) : EA_PTRSIZE;
+ emitAttr retSize = (retSizeIn != EA_UNKNOWN) ? retSizeIn : EA_PTRSIZE;
bool gcRefRegsInScratch = ((gcrefRegs & RBM_CALLEE_TRASH) != 0);
- /*
- Allocate a larger descriptor if any GC values need to be saved
- or if we have an absurd number of arguments or a large address
- mode displacement, or we have some byref registers
- */
-
- if (!VarSetOps::IsEmpty(emitComp, GCvars) || // any frame GCvars live
- (gcRefRegsInScratch) || // any register gc refs live in scratch regs
- (byrefRegs != 0) || // any register byrefs live
- (disp < AM_DISP_MIN) || // displacement too negative
- (disp > AM_DISP_MAX) || // displacement too positive
- (argCnt > ID_MAX_SMALL_CNS) || // too many args
- (argCnt < 0) ) // caller pops arguments
+
+ // Allocate a larger descriptor if any GC values need to be saved
+ // or if we have an absurd number of arguments or a large address
+ // mode displacement, or we have some byref registers
+ //
+ // On Amd64 System V OSs a larger descriptor is also needed if the
+ // call returns a two-register-returned struct and the second
+ // register (RDX) is a GCRef or ByRef pointer.
+
+
+ if (!VarSetOps::IsEmpty(emitComp, GCvars) || // any frame GCvars live
+ (gcRefRegsInScratch) || // any register gc refs live in scratch regs
+ (byrefRegs != 0) || // any register byrefs live
+ (disp < AM_DISP_MIN) || // displacement too negative
+ (disp > AM_DISP_MAX) || // displacement too positive
+ (argCnt > ID_MAX_SMALL_CNS) || // too many args
+ (argCnt < 0) // caller pops arguments
+ // There is a second ref/byref return register.
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY( || EA_IS_GCREF_OR_BYREF(secondRetSize)))
{
instrDescCGCA* id;
@@ -3000,6 +3037,10 @@ emitter::instrDesc * emitter::emitNewInstrCallInd(int argCnt,
id->idcArgCnt = argCnt;
id->idcDisp = disp;
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+ emitSetSecondRetRegGCType(id, secondRetSize);
+#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+
return id;
}
else
@@ -3013,7 +3054,7 @@ emitter::instrDesc * emitter::emitNewInstrCallInd(int argCnt,
/* Store the displacement and make sure the value fit */
id->idAddr()->iiaAddrMode.amDisp = disp;
- assert(id->idAddr()->iiaAddrMode.amDisp == disp);
+ assert(id->idAddr()->iiaAddrMode.amDisp == disp);
/* Save the the live GC registers in the unused 'idReg/idRg2' fields */
emitEncodeCallGCregs(gcrefRegs, id);
@@ -3033,26 +3074,32 @@ emitter::instrDesc * emitter::emitNewInstrCallInd(int argCnt,
* and an arbitrarily large argument count.
*/
-emitter::instrDesc *emitter::emitNewInstrCallDir(int argCnt,
- VARSET_VALARG_TP GCvars,
- regMaskTP gcrefRegs,
- regMaskTP byrefRegs,
- emitAttr retSizeIn)
+emitter::instrDesc *emitter::emitNewInstrCallDir(int argCnt,
+ VARSET_VALARG_TP GCvars,
+ regMaskTP gcrefRegs,
+ regMaskTP byrefRegs,
+ emitAttr retSizeIn
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(emitAttr secondRetSize))
{
- emitAttr retSize = retSizeIn ? EA_ATTR(retSizeIn) : EA_PTRSIZE;
+ emitAttr retSize = (retSizeIn != EA_UNKNOWN) ? retSizeIn : EA_PTRSIZE;
+
+ // Allocate a larger descriptor if new GC values need to be saved
+ // or if we have an absurd number of arguments or if we need to
+ // save the scope.
+ //
+ // On Amd64 System V OSs a larger descriptor is also needed if the
+ // call returns a two-register-returned struct and the second
+ // register (RDX) is a GCRef or ByRef pointer.
- /*
- Allocate a larger descriptor if new GC values need to be saved
- or if we have an absurd number of arguments or if we need to
- save the scope.
- */
bool gcRefRegsInScratch = ((gcrefRegs & RBM_CALLEE_TRASH) != 0);
- if (!VarSetOps::IsEmpty(emitComp, GCvars) || // any frame GCvars live
- gcRefRegsInScratch || // any register gc refs live in scratch regs
- (byrefRegs != 0) || // any register byrefs live
- (argCnt > ID_MAX_SMALL_CNS) || // too many args
- (argCnt < 0) ) // caller pops arguments
+ if (!VarSetOps::IsEmpty(emitComp, GCvars) || // any frame GCvars live
+ gcRefRegsInScratch || // any register gc refs live in scratch regs
+ (byrefRegs != 0) || // any register byrefs live
+ (argCnt > ID_MAX_SMALL_CNS) || // too many args
+ (argCnt < 0) // caller pops arguments
+ // There is a second ref/byref return register.
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY( || EA_IS_GCREF_OR_BYREF(secondRetSize)))
{
instrDescCGCA* id = emitAllocInstrCGCA(retSize);
@@ -3066,6 +3113,10 @@ emitter::instrDesc *emitter::emitNewInstrCallDir(int argCnt,
id->idcDisp = 0;
id->idcArgCnt = argCnt;
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+ emitSetSecondRetRegGCType(id, secondRetSize);
+#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+
return id;
}
else
diff --git a/src/jit/emit.h b/src/jit/emit.h
index df277360f4..c03e902afc 100644
--- a/src/jit/emit.h
+++ b/src/jit/emit.h
@@ -656,6 +656,13 @@ protected:
#endif // ARM or x86-LEGACY_BACKEND
// 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
+ // stores the GC-ness of the second register.
+ // It is added to the instrDescCGCA and not here (the base struct) since it is not needed by all the instructions.
+ // This struct (instrDesc) is very carefully kept to be no more than 128 bytes. There is no more space to add members
+ // for keeping GC-ness of the second return registers. It will also bloat the base struct unnecessarily
+ // since the GC-ness of the second register is only needed for call instructions.
+ // The instrDescCGCA struct's member keeping the GC-ness of the first return register is _idcSecondRetRegGCType.
GCtype _idGCref :2; // GCref operand? (value is a "GCtype")
// Note that we use the _idReg1 and _idReg2 fields to hold
@@ -1209,6 +1216,24 @@ protected:
regMaskTP idcGcrefRegs; // ... gcref registers
regMaskTP idcByrefRegs; // ... byref registers
unsigned idcArgCnt; // ... lots of args or (<0 ==> caller pops args)
+
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+ // This method handle the GC-ness of the second register in a 2 register returned struct on System V.
+ GCtype idSecondGCref() const { return (GCtype)_idcSecondRetRegGCType; }
+ void idSecondGCref(GCtype gctype) { _idcSecondRetRegGCType = gctype; }
+#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+
+ private:
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+ // This member stores the GC-ness of the second register in a 2 register returned struct on System V.
+ // It is added to the call struct since it is not needed by the base instrDesc struct, which keeps GC-ness
+ // of the first register for the instCall nodes.
+ // The base instrDesc is very carefully kept to be no more than 128 bytes. There is no more space to add members
+ // for keeping GC-ness of the second return registers. It will also bloat the base struct unnecessarily
+ // since the GC-ness of the second register is only needed for call instructions.
+ // The base struct's member keeping the GC-ness of the first return register is _idGCref.
+ GCtype _idcSecondRetRegGCType : 2; // ... GC type for the second return register.
+#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
};
struct instrDescArmFP : instrDesc
@@ -1589,6 +1614,10 @@ private:
regNumber emitSyncThisObjReg; // where is "this" enregistered for synchronized methods?
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+ void emitSetSecondRetRegGCType(instrDescCGCA* id, emitAttr secondRetSize);
+#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+
static void emitEncodeCallGCregs(regMaskTP regs, instrDesc *id);
static unsigned emitDecodeCallGCregs(instrDesc *id);
diff --git a/src/jit/emitxarch.cpp b/src/jit/emitxarch.cpp
index 63a2c6b795..4ca70e34ab 100644
--- a/src/jit/emitxarch.cpp
+++ b/src/jit/emitxarch.cpp
@@ -5316,20 +5316,21 @@ void emitter::emitIns_J(instruction ins,
void emitter::emitIns_Call(EmitCallType callType,
CORINFO_METHOD_HANDLE methHnd,
- INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO* sigInfo) // used to report call sites to the EE
- void* addr,
- ssize_t argSize,
- emitAttr retSize,
- VARSET_VALARG_TP ptrVars,
- regMaskTP gcrefRegs,
- regMaskTP byrefRegs,
- IL_OFFSETX ilOffset /* = BAD_IL_OFFSET */,
- regNumber ireg /* = REG_NA */,
- regNumber xreg /* = REG_NA */,
- unsigned xmul /* = 0 */,
- ssize_t disp /* = 0 */,
- bool isJump /* = false */,
- bool isNoGC /* = false */)
+ INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO* sigInfo) // used to report call sites to the EE
+ void* addr,
+ ssize_t argSize,
+ emitAttr retSize
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(emitAttr secondRetSize),
+ VARSET_VALARG_TP ptrVars,
+ regMaskTP gcrefRegs,
+ regMaskTP byrefRegs,
+ IL_OFFSETX ilOffset, // = BAD_IL_OFFSET
+ regNumber ireg, // = REG_NA
+ regNumber xreg, // = REG_NA
+ unsigned xmul, // = 0
+ ssize_t disp, // = 0
+ bool isJump, // = false
+ bool isNoGC) // = false
{
/* Sanity check the arguments depending on callType */
@@ -5513,17 +5514,29 @@ void emitter::emitIns_Call(EmitCallType callType,
callType == EC_INDIR_SR || callType == EC_INDIR_C ||
callType == EC_INDIR_ARD);
- id = emitNewInstrCallInd(argCnt, disp, ptrVars, gcrefRegs, byrefRegs, retSize);
+ id = emitNewInstrCallInd(argCnt,
+ disp,
+ ptrVars,
+ gcrefRegs,
+ byrefRegs,
+ retSize
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(secondRetSize));
}
else
{
- /* Helper/static/nonvirtual/function calls (direct or through handle),
- and calls to an absolute addr. */
+ // Helper/static/nonvirtual/function calls (direct or through handle),
+ // and calls to an absolute addr.
- assert(callType == EC_FUNC_TOKEN || callType == EC_FUNC_TOKEN_INDIR ||
+ assert(callType == EC_FUNC_TOKEN ||
+ callType == EC_FUNC_TOKEN_INDIR ||
callType == EC_FUNC_ADDR);
- id = emitNewInstrCallDir(argCnt, ptrVars, gcrefRegs, byrefRegs, retSize);
+ id = emitNewInstrCallDir(argCnt,
+ ptrVars,
+ gcrefRegs,
+ byrefRegs,
+ retSize
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(secondRetSize));
}
/* Update the emitter's live GC ref sets */
@@ -10532,9 +10545,29 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE**
// If the method returns a GC ref, mark EAX appropriately
if (id->idGCref() == GCT_GCREF)
+ {
gcrefRegs |= RBM_EAX;
- else if (id->idGCref() == GCT_BYREF)
+ }
+ else if (id->idGCref() == GCT_BYREF)
+ {
byrefRegs |= RBM_EAX;
+ }
+
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+ // If is a multi-register return method is called, mark RDX appropriately (for System V AMD64).
+ if (id->idIsLargeCall())
+ {
+ instrDescCGCA* idCall = (instrDescCGCA*)id;
+ if (idCall->idSecondGCref() == GCT_GCREF)
+ {
+ gcrefRegs |= RBM_RDX;
+ }
+ else if (idCall->idSecondGCref() == GCT_BYREF)
+ {
+ byrefRegs |= RBM_RDX;
+ }
+ }
+#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
// If the GC register set has changed, report the new set
if (gcrefRegs != emitThisGCrefRegs)
diff --git a/src/jit/emitxarch.h b/src/jit/emitxarch.h
index 1f8ead4afd..594d0d0996 100644
--- a/src/jit/emitxarch.h
+++ b/src/jit/emitxarch.h
@@ -163,18 +163,20 @@ private:
instrDesc *emitNewInstrAmd (emitAttr attr, ssize_t dsp);
instrDesc *emitNewInstrAmdCns (emitAttr attr, ssize_t dsp, int cns);
- instrDesc *emitNewInstrCallDir (int argCnt,
- VARSET_VALARG_TP GCvars,
- regMaskTP gcrefRegs,
- regMaskTP byrefRegs,
- emitAttr retSize);
-
- instrDesc *emitNewInstrCallInd( int argCnt,
- ssize_t disp,
- VARSET_VALARG_TP GCvars,
- regMaskTP gcrefRegs,
- regMaskTP byrefRegs,
- emitAttr retSize);
+ instrDesc *emitNewInstrCallDir (int argCnt,
+ VARSET_VALARG_TP GCvars,
+ regMaskTP gcrefRegs,
+ regMaskTP byrefRegs,
+ emitAttr retSize
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(emitAttr secondRegSize));
+
+ instrDesc *emitNewInstrCallInd( int argCnt,
+ ssize_t disp,
+ VARSET_VALARG_TP GCvars,
+ regMaskTP gcrefRegs,
+ regMaskTP byrefRegs,
+ emitAttr retSize
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(emitAttr secondRegSize));
void emitGetInsCns (instrDesc *id, CnsVal *cv);
ssize_t emitGetInsAmdCns(instrDesc *id, CnsVal *cv);
@@ -463,35 +465,37 @@ public:
EC_COUNT
};
- void emitIns_Call (EmitCallType callType,
- CORINFO_METHOD_HANDLE methHnd,
- CORINFO_SIG_INFO* sigInfo, // used to report call sites to the EE
- void* addr,
- ssize_t argSize,
- emitAttr retSize,
- VARSET_VALARG_TP ptrVars,
- regMaskTP gcrefRegs,
- regMaskTP byrefRegs,
- GenTreeIndir * indir,
- bool isJump = false,
- bool isNoGC = false);
-
- void emitIns_Call (EmitCallType callType,
- CORINFO_METHOD_HANDLE methHnd,
+ void emitIns_Call (EmitCallType callType,
+ CORINFO_METHOD_HANDLE methHnd,
+ CORINFO_SIG_INFO* sigInfo, // used to report call sites to the EE
+ void* addr,
+ ssize_t argSize,
+ emitAttr retSize
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(emitAttr secondRegSize),
+ VARSET_VALARG_TP ptrVars,
+ regMaskTP gcrefRegs,
+ regMaskTP byrefRegs,
+ GenTreeIndir * indir,
+ bool isJump = false,
+ bool isNoGC = false);
+
+ void emitIns_Call (EmitCallType callType,
+ CORINFO_METHOD_HANDLE methHnd,
INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO* sigInfo) // used to report call sites to the EE
- void* addr,
- ssize_t argSize,
- emitAttr retSize,
- VARSET_VALARG_TP ptrVars,
- regMaskTP gcrefRegs,
- regMaskTP byrefRegs,
- IL_OFFSETX ilOffset = BAD_IL_OFFSET,
- regNumber ireg = REG_NA,
- regNumber xreg = REG_NA,
- unsigned xmul = 0,
- ssize_t disp = 0,
- bool isJump = false,
- bool isNoGC = false);
+ void* addr,
+ ssize_t argSize,
+ emitAttr retSize
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(emitAttr secondRegSize),
+ VARSET_VALARG_TP ptrVars,
+ regMaskTP gcrefRegs,
+ regMaskTP byrefRegs,
+ IL_OFFSETX ilOffset = BAD_IL_OFFSET,
+ regNumber ireg = REG_NA,
+ regNumber xreg = REG_NA,
+ unsigned xmul = 0,
+ ssize_t disp = 0,
+ bool isJump = false,
+ bool isNoGC = false);
#ifdef _TARGET_AMD64_
// Is the last instruction emitted a call instruction?
diff --git a/src/jit/instr.cpp b/src/jit/instr.cpp
index c6ca9d35c5..1bd1ab6032 100644
--- a/src/jit/instr.cpp
+++ b/src/jit/instr.cpp
@@ -1205,9 +1205,10 @@ void CodeGen::sched_AM(instruction ins,
* Emit a "call [r/m]" instruction (the r/m operand given by a tree).
*/
-void CodeGen::instEmit_indCall(GenTreePtr call,
- size_t argSize,
- emitAttr retSize)
+void CodeGen::instEmit_indCall(GenTreePtr call,
+ size_t argSize,
+ emitAttr retSize
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(emitAttr secondRetSize))
{
GenTreePtr addr;
@@ -1244,15 +1245,16 @@ void CodeGen::instEmit_indCall(GenTreePtr call,
{
ssize_t funcPtr = addr->gtIntCon.gtIconVal;
- getEmitter()->emitIns_Call( emitter::EC_FUNC_ADDR,
- NULL, // methHnd
- INDEBUG_LDISASM_COMMA(sigInfo)
- (void*) funcPtr,
- argSize,
- retSize,
- gcInfo.gcVarPtrSetCur,
- gcInfo.gcRegGCrefSetCur,
- gcInfo.gcRegByrefSetCur);
+ getEmitter()->emitIns_Call(emitter::EC_FUNC_ADDR,
+ NULL, // methHnd
+ INDEBUG_LDISASM_COMMA(sigInfo)
+ (void*) funcPtr,
+ argSize,
+ retSize
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(secondRetSize),
+ gcInfo.gcVarPtrSetCur,
+ gcInfo.gcRegGCrefSetCur,
+ gcInfo.gcRegByrefSetCur);
return;
}
}
@@ -1305,15 +1307,16 @@ void CodeGen::instEmit_indCall(GenTreePtr call,
{
ssize_t funcPtr = addr->gtIntCon.gtIconVal;
- getEmitter()->emitIns_Call( emitter::EC_FUNC_ADDR,
- NULL, // methHnd
- INDEBUG_LDISASM_COMMA(sigInfo)
- (void*) funcPtr,
- argSize,
- retSize,
- gcInfo.gcVarPtrSetCur,
- gcInfo.gcRegGCrefSetCur,
- gcInfo.gcRegByrefSetCur);
+ getEmitter()->emitIns_Call(emitter::EC_FUNC_ADDR,
+ NULL, // methHnd
+ INDEBUG_LDISASM_COMMA(sigInfo)
+ (void*) funcPtr,
+ argSize,
+ retSize
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(secondRetSize),
+ gcInfo.gcVarPtrSetCur,
+ gcInfo.gcRegGCrefSetCur,
+ gcInfo.gcRegByrefSetCur);
return;
}
}
@@ -1369,17 +1372,21 @@ void CodeGen::instEmit_indCall(GenTreePtr call,
#endif // CPU_LOAD_STORE_ARCH
- getEmitter()->emitIns_Call( emitCallType,
- NULL, // methHnd
- INDEBUG_LDISASM_COMMA(sigInfo)
- NULL, // addr
- argSize,
- retSize,
- gcInfo.gcVarPtrSetCur,
- gcInfo.gcRegGCrefSetCur,
- gcInfo.gcRegByrefSetCur,
- BAD_IL_OFFSET, // ilOffset
- brg, xrg, mul, cns); // addressing mode values
+ getEmitter()->emitIns_Call(emitCallType,
+ NULL, // methHnd
+ INDEBUG_LDISASM_COMMA(sigInfo)
+ NULL, // addr
+ argSize,
+ retSize
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(secondRetSize),
+ gcInfo.gcVarPtrSetCur,
+ gcInfo.gcRegGCrefSetCur,
+ gcInfo.gcRegByrefSetCur,
+ BAD_IL_OFFSET, // ilOffset
+ brg,
+ xrg,
+ mul,
+ cns); // addressing mode values
}
#ifdef LEGACY_BACKEND
diff --git a/src/jit/instr.h b/src/jit/instr.h
index c1e34afac0..ff73dd4a80 100644
--- a/src/jit/instr.h
+++ b/src/jit/instr.h
@@ -259,23 +259,24 @@ DECLARE_TYPED_ENUM(emitAttr,unsigned)
}
END_DECLARE_TYPED_ENUM(emitAttr,unsigned)
-# define EA_ATTR(x) ((emitAttr) (x))
-# define EA_SIZE(x) ((emitAttr) ( ((unsigned) (x)) & EA_SIZE_MASK) )
-# define EA_SIZE_IN_BYTES(x) ((UNATIVE_OFFSET) (EA_SIZE(x)))
-# define EA_SET_SIZE(x,sz) ((emitAttr) ((((unsigned) (x)) & ~EA_SIZE_MASK) | sz))
-# define EA_SET_FLG(x,flg) ((emitAttr) ( ((unsigned) (x)) | flg ) )
-# define EA_4BYTE_DSP_RELOC (EA_SET_FLG(EA_4BYTE,EA_DSP_RELOC_FLG) )
-# define EA_PTR_DSP_RELOC (EA_SET_FLG(EA_PTRSIZE,EA_DSP_RELOC_FLG) )
-# define EA_HANDLE_CNS_RELOC (EA_SET_FLG(EA_PTRSIZE,EA_CNS_RELOC_FLG) )
-# define EA_IS_OFFSET(x) ((((unsigned) (x)) & ((unsigned) EA_OFFSET_FLG)) != 0)
-# define EA_IS_GCREF(x) ((((unsigned) (x)) & ((unsigned) EA_GCREF_FLG )) != 0)
-# define EA_IS_BYREF(x) ((((unsigned) (x)) & ((unsigned) EA_BYREF_FLG )) != 0)
-# define EA_IS_DSP_RELOC(x) ((((unsigned) (x)) & ((unsigned) EA_DSP_RELOC_FLG )) != 0)
-# define EA_IS_CNS_RELOC(x) ((((unsigned) (x)) & ((unsigned) EA_CNS_RELOC_FLG )) != 0)
-# define EA_IS_RELOC(x) (EA_IS_DSP_RELOC(x) || EA_IS_CNS_RELOC(x))
-# define EA_TYPE(x) ((emitAttr) ( ((unsigned) (x)) & ~(EA_OFFSET_FLG | EA_DSP_RELOC_FLG | EA_CNS_RELOC_FLG) ) )
-
-#define EmitSize(x) (EA_ATTR(genTypeSize(TypeGet(x))))
+#define EA_ATTR(x) ((emitAttr)(x))
+#define EA_SIZE(x) ((emitAttr)(((unsigned)(x)) & EA_SIZE_MASK))
+#define EA_SIZE_IN_BYTES(x) ((UNATIVE_OFFSET)(EA_SIZE(x)))
+#define EA_SET_SIZE(x, sz) ((emitAttr)((((unsigned)(x)) & ~EA_SIZE_MASK) | sz))
+#define EA_SET_FLG(x, flg) ((emitAttr)(((unsigned)(x)) | flg))
+#define EA_4BYTE_DSP_RELOC (EA_SET_FLG(EA_4BYTE, EA_DSP_RELOC_FLG))
+#define EA_PTR_DSP_RELOC (EA_SET_FLG(EA_PTRSIZE, EA_DSP_RELOC_FLG))
+#define EA_HANDLE_CNS_RELOC (EA_SET_FLG(EA_PTRSIZE, EA_CNS_RELOC_FLG))
+#define EA_IS_OFFSET(x) ((((unsigned)(x)) & ((unsigned)EA_OFFSET_FLG)) != 0)
+#define EA_IS_GCREF(x) ((((unsigned)(x)) & ((unsigned)EA_GCREF_FLG)) != 0)
+#define EA_IS_BYREF(x) ((((unsigned)(x)) & ((unsigned)EA_BYREF_FLG)) != 0)
+#define EA_IS_GCREF_OR_BYREF(x) ((((unsigned)(x)) & ((unsigned)(EA_BYREF_FLG | EA_GCREF_FLG))) != 0)
+#define EA_IS_DSP_RELOC(x) ((((unsigned)(x)) & ((unsigned)EA_DSP_RELOC_FLG)) != 0)
+#define EA_IS_CNS_RELOC(x) ((((unsigned)(x)) & ((unsigned)EA_CNS_RELOC_FLG)) != 0)
+#define EA_IS_RELOC(x) (EA_IS_DSP_RELOC(x) || EA_IS_CNS_RELOC(x))
+#define EA_TYPE(x) ((emitAttr)(((unsigned)(x)) & ~(EA_OFFSET_FLG | EA_DSP_RELOC_FLG | EA_CNS_RELOC_FLG)))
+
+#define EmitSize(x) (EA_ATTR(genTypeSize(TypeGet(x))))
// Enum specifying the instruction set for generating floating point or SIMD code.
enum InstructionSet