From 31cb7975335b33bd04268216e3409a4cd6e9dc29 Mon Sep 17 00:00:00 2001 From: Lubomir Litchev Date: Wed, 24 Feb 2016 21:26:55 -0800 Subject: Add support for emitting GC-ness of the second return register for 16 byte structs. This changeset adds support for emitting the GC-ness of the second return register (RDX for System V Amd64) for multi-register return structs. It closes a hole in the GC info and resolves multiple GC stress failures. Fixes #2757. --- src/jit/codegen.h | 29 +++++----- src/jit/codegencommon.cpp | 19 ++++--- src/jit/codegenxarch.cpp | 134 +++++++++++++++++++++++++++------------------- src/jit/emit.cpp | 125 +++++++++++++++++++++++++++++------------- src/jit/emit.h | 29 ++++++++++ src/jit/emitxarch.cpp | 73 ++++++++++++++++++------- src/jit/emitxarch.h | 84 +++++++++++++++-------------- src/jit/instr.cpp | 71 +++++++++++++----------- src/jit/instr.h | 35 ++++++------ 9 files changed, 378 insertions(+), 221 deletions(-) (limited to 'src/jit') 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 de2339efdf..5492a3f023 100644 --- a/src/jit/codegencommon.cpp +++ b/src/jit/codegencommon.cpp @@ -9316,15 +9316,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 { @@ -9667,8 +9669,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 3025675d9d..8b38930de0 100644 --- a/src/jit/codegenxarch.cpp +++ b/src/jit/codegenxarch.cpp @@ -5172,16 +5172,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; @@ -5191,7 +5192,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, @@ -5204,13 +5206,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; @@ -5222,7 +5225,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, @@ -5495,7 +5499,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; @@ -5529,20 +5532,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); @@ -5668,16 +5673,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; @@ -5722,7 +5745,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 @@ -5734,7 +5758,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); } } @@ -5748,7 +5773,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)); } @@ -5761,7 +5787,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 @@ -5817,7 +5844,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); } @@ -8459,11 +8487,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; @@ -8520,19 +8545,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 4bf0affe4c..c1c086fd2f 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 -- cgit v1.2.3