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);
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,
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
{
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,
// 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;
INDEBUG_LDISASM_COMMA(sigInfo)
addr,
argSize,
- retSize,
+ retSize
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(secondRetSize),
gcInfo.gcVarPtrSetCur,
gcInfo.gcRegGCrefSetCur,
gcInfo.gcRegByrefSetCur,
// 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;
INDEBUG_LDISASM_COMMA(sigInfo)
nullptr,
argSize,
- retSize,
+ retSize
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(secondRetSize),
gcInfo.gcVarPtrSetCur,
gcInfo.gcRegGCrefSetCur,
gcInfo.gcRegByrefSetCur,
void CodeGen::genCallInstruction(GenTreePtr node)
{
GenTreeCall *call = node->AsCall();
-
assert(call->gtOper == GT_CALL);
gtCallTypes callType = (gtCallTypes)call->gtCallType;
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);
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;
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
INDEBUG_LDISASM_COMMA(sigInfo)
target->AsIndir()
X86_ARG(argSizeForEmitter),
- retSize,
+ retSize
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(secondRetSize),
ilOffset);
}
}
INDEBUG_LDISASM_COMMA(sigInfo)
nullptr //addr
X86_ARG(argSizeForEmitter),
- retSize,
+ retSize
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(secondRetSize),
ilOffset,
genConsumeReg(target));
}
INDEBUG_LDISASM_COMMA(sigInfo)
(void*) call->gtEntryPoint.addr
X86_ARG(argSizeForEmitter),
- retSize,
+ retSize
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(secondRetSize),
ilOffset);
}
#endif
INDEBUG_LDISASM_COMMA(sigInfo)
addr
X86_ARG(argSizeForEmitter),
- retSize,
+ retSize
+ FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(secondRetSize),
ilOffset);
}
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;
}
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);
/*****************************************************************************/
#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.
* 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;
id->idcArgCnt = argCnt;
id->idcDisp = disp;
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+ emitSetSecondRetRegGCType(id, secondRetSize);
+#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+
return id;
}
else
/* 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);
* 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);
id->idcDisp = 0;
id->idcArgCnt = argCnt;
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+ emitSetSecondRetRegGCType(id, secondRetSize);
+#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+
return id;
}
else
#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
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
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);
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 */
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 */
// 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)
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);
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?
* 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;
{
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;
}
}
{
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;
}
}
#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
}
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
public float f1;
}
+ struct Test35
+ {
+ public Foo3 foo1;
+ public Foo3 foo2;
+ }
class Program0
{
[MethodImplAttribute(MethodImplOptions.NoInlining)]
Test30 test1()
{
+ Console.WriteLine("From Program0:test1!");
Test30 test1 = default(Test30);
test1.i1 = 1;
test1.i2 = 2;
[MethodImplAttribute(MethodImplOptions.NoInlining)]
Test31 test2()
{
+ Console.WriteLine("From Program0:test2!");
Test31 test2 = default(Test31);
Foo3 foo = new Foo3();
foo.iFoo = 3;
[MethodImplAttribute(MethodImplOptions.NoInlining)]
Test32 test3()
{
+ Console.WriteLine("From Program0:test3!");
Test32 test3 = default(Test32);
Foo3 foo = new Foo3();
foo.iFoo = 4;
[MethodImplAttribute(MethodImplOptions.NoInlining)]
Test33 test4()
{
+ Console.WriteLine("From Program0:test4!");
Test33 test4 = default(Test33);
Foo3 foo1 = new Foo3();
Foo3 foo2 = new Foo3();
}
[MethodImplAttribute(MethodImplOptions.NoInlining)]
+ Test35 test6(Test35 t5)
+ {
+ int fRes = t5.foo1.iFoo + t5.foo2.iFoo;
+ Console.WriteLine("From Test6 members: {0} {1}", t5.foo1.iFoo, t5.foo2.iFoo);
+ Console.WriteLine("From Test6: Res {0}", fRes);
+ if (fRes != 43) {
+ throw new Exception("Failed inside test6 test!");
+ }
+
+ Test35 tst5 = default(Test35);
+ tst5.foo1 = new Foo3();
+ tst5.foo2 = new Foo3();
+ tst5.foo1.iFoo = 28;
+ tst5.foo2.iFoo = 29;
+ return tst5;
+ }
+
+ [MethodImplAttribute(MethodImplOptions.NoInlining)]
public static int Main1()
{
Program0 p = new Program0();
throw new Exception("Failed test5 test!");
}
+ Test35 test6 = default(Test35);
+ test6.foo1 = new Foo3();
+ test6.foo2 = new Foo3();
+ test6.foo1.iFoo = 21;
+ test6.foo2.iFoo = 22;
+
+ Test35 t6Res = p.test6(test6);
+
+ Console.WriteLine("test6 Result: {0}", t6Res.foo1.iFoo + t6Res.foo2.iFoo);
+ if ((t6Res.foo1.iFoo + t6Res.foo2.iFoo) != 57) {
+ throw new Exception("Failed test6 test!");
+ }
+
return 100;
}
}