summaryrefslogtreecommitdiff
path: root/src/vm/callingconvention.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/callingconvention.h')
-rw-r--r--src/vm/callingconvention.h275
1 files changed, 230 insertions, 45 deletions
diff --git a/src/vm/callingconvention.h b/src/vm/callingconvention.h
index 244a3df878..490ae3ce87 100644
--- a/src/vm/callingconvention.h
+++ b/src/vm/callingconvention.h
@@ -42,6 +42,12 @@ struct ArgLocDesc
int m_idxStack; // First stack slot used (or -1)
int m_cStack; // Count of stack slots used (or 0)
+#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+
+ EEClass* m_eeClass; // For structs passed in register, it points to the EEClass of the struct
+
+#endif // UNIX_AMD64_ABI && FEATURE_UNIX_AMD64_STRUCT_PASSING
+
#if defined(_TARGET_ARM_)
BOOL m_fRequires64BitAlignment; // True if the argument should always be aligned (in registers or on the stack
#endif
@@ -63,6 +69,9 @@ struct ArgLocDesc
#if defined(_TARGET_ARM_)
m_fRequires64BitAlignment = FALSE;
#endif
+#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+ m_eeClass = NULL;
+#endif
}
};
@@ -138,9 +147,13 @@ struct TransitionBlock
{
LIMITED_METHOD_CONTRACT;
+#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+ return offset >= sizeof(TransitionBlock);
+#else
int ofsArgRegs = GetOffsetOfArgumentRegisters();
return offset >= (int) (ofsArgRegs + ARGUMENTREGISTERS_SIZE);
+#endif
}
static BOOL IsArgumentRegisterOffset(int offset)
@@ -156,14 +169,45 @@ struct TransitionBlock
static UINT GetArgumentIndexFromOffset(int offset)
{
LIMITED_METHOD_CONTRACT;
+
+#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+ _ASSERTE(offset != TransitionBlock::StructInRegsOffset);
+#endif
return (offset - GetOffsetOfArgumentRegisters()) / sizeof(TADDR);
}
+
+ static UINT GetStackArgumentIndexFromOffset(int offset)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return (offset - TransitionBlock::GetOffsetOfArgs()) / STACK_ELEM_SIZE;
+ }
+
#endif
#ifdef CALLDESCR_FPARGREGS
static BOOL IsFloatArgumentRegisterOffset(int offset)
{
LIMITED_METHOD_CONTRACT;
+#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+ return (offset != TransitionBlock::StructInRegsOffset) && (offset < 0);
+#else
+ return offset < 0;
+#endif
+ }
+
+ // Check if an argument has floating point register, that means that it is
+ // either a floating point argument or a struct passed in registers that
+ // has a floating point member.
+ static BOOL HasFloatRegister(int offset, ArgLocDesc* argLocDescForStructInRegs)
+ {
+ LIMITED_METHOD_CONTRACT;
+ #if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+ if (offset == TransitionBlock::StructInRegsOffset)
+ {
+ return argLocDescForStructInRegs->m_cFloatReg > 0;
+ }
+ #endif
return offset < 0;
}
@@ -172,7 +216,7 @@ struct TransitionBlock
LIMITED_METHOD_CONTRACT;
return -GetNegSpaceSize();
}
-#endif
+#endif // CALLDESCR_FPARGREGS
static int GetOffsetOfCalleeSavedRegisters()
{
@@ -194,6 +238,11 @@ struct TransitionBlock
}
static const int InvalidOffset = -1;
+#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+ // Special offset value to represent struct passed in registers. Such a struct can span both
+ // general purpose and floating point registers, so it can have two different offsets.
+ static const int StructInRegsOffset = -2;
+#endif
};
//-----------------------------------------------------------------------
@@ -340,11 +389,16 @@ public:
{
LIMITED_METHOD_CONTRACT;
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+ // No arguments are passed by reference on AMD64 on Unix
+ return FALSE;
+#else
// If the size is bigger than ENREGISTERED_PARAM_TYPE_MAXSIZE, or if the size is NOT a power of 2, then
// the argument is passed by reference.
return (size > ENREGISTERED_PARAMTYPE_MAXSIZE) || ((size & (size-1)) != 0);
+#endif
}
-#endif
+#endif // _TARGET_AMD64_
// This overload should be used for varargs only.
static BOOL IsVarArgPassedByRef(size_t size)
@@ -352,7 +406,13 @@ public:
LIMITED_METHOD_CONTRACT;
#ifdef _TARGET_AMD64_
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+ PORTABILITY_ASSERT("ArgIteratorTemplate::IsVarArgPassedByRef");
+ return FALSE;
+#else // FEATURE_UNIX_AMD64_STRUCT_PASSING
return IsArgPassedByRef(size);
+#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+
#else
return (size > ENREGISTERED_PARAMTYPE_MAXSIZE);
#endif
@@ -426,6 +486,15 @@ public:
void GetVASigCookieLoc(ArgLocDesc * pLoc) { WRAPPER_NO_CONTRACT; GetSimpleLoc(GetVASigCookieOffset(), pLoc); }
#endif // !_TARGET_X86_
+ ArgLocDesc* GetArgLocDescForStructInRegs()
+ {
+#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+ return m_hasArgLocDescForStructInRegs ? &m_argLocDescForStructInRegs : NULL;
+#else
+ return NULL;
+#endif
+ }
+
#ifdef _TARGET_ARM_
// Get layout information for the argument that the ArgIterator is currently visiting.
void GetArgLoc(int argOffset, ArgLocDesc *pLoc)
@@ -463,7 +532,7 @@ public:
}
else
{
- pLoc->m_idxStack = TransitionBlock::GetArgumentIndexFromOffset(argOffset) - 4;
+ pLoc->m_idxStack = TransitionBlock::GetStackArgumentIndexFromOffset(argOffset);
pLoc->m_cStack = cSlots;
}
}
@@ -509,7 +578,7 @@ public:
}
else
{
- pLoc->m_idxStack = TransitionBlock::GetArgumentIndexFromOffset(argOffset) - 8;
+ pLoc->m_idxStack = TransitionBlock::GetStackArgumentIndexFromOffset(argOffset);
pLoc->m_cStack = cSlots;
}
}
@@ -517,37 +586,46 @@ public:
#if defined(_TARGET_AMD64_) && defined(UNIX_AMD64_ABI)
// Get layout information for the argument that the ArgIterator is currently visiting.
- void GetArgLoc(int argOffset, ArgLocDesc *pLoc)
+ void GetArgLoc(int argOffset, ArgLocDesc* pLoc)
{
LIMITED_METHOD_CONTRACT;
+#if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+ if (m_hasArgLocDescForStructInRegs)
+ {
+ *pLoc = m_argLocDescForStructInRegs;
+ return;
+ }
+#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+
+ if (argOffset == TransitionBlock::StructInRegsOffset)
+ {
+ // We always already have argLocDesc for structs passed in registers, we
+ // compute it in the GetNextOffset for those since it is always needed.
+ _ASSERTE(false);
+ return;
+ }
+
pLoc->Init();
if (TransitionBlock::IsFloatArgumentRegisterOffset(argOffset))
{
// Dividing by 8 as size of each register in FloatArgumentRegisters is 8 bytes.
pLoc->m_idxFloatReg = (argOffset - TransitionBlock::GetOffsetOfFloatArgumentRegisters()) / 8;
-
- // UNIXTODO: Passing of structs, HFAs. For now, use the Windows convention.
pLoc->m_cFloatReg = 1;
- return;
}
-
- // UNIXTODO: Passing of structs, HFAs. For now, use the Windows convention.
- int cSlots = 1;
-
- if (!TransitionBlock::IsStackArgumentOffset(argOffset))
+ else if (!TransitionBlock::IsStackArgumentOffset(argOffset))
{
pLoc->m_idxGenReg = TransitionBlock::GetArgumentIndexFromOffset(argOffset);
- pLoc->m_cGenReg = cSlots;
- }
+ pLoc->m_cGenReg = 1;
+ }
else
{
- pLoc->m_idxStack = (argOffset - TransitionBlock::GetOffsetOfArgs()) / 8;
- pLoc->m_cStack = cSlots;
+ pLoc->m_idxStack = TransitionBlock::GetStackArgumentIndexFromOffset(argOffset);
+ pLoc->m_cStack = (GetArgSize() + STACK_ELEM_SIZE - 1) / STACK_ELEM_SIZE;
}
}
-#endif // _TARGET_ARM64_ && UNIX_AMD64_ABI
+#endif // _TARGET_AMD64_ && UNIX_AMD64_ABI
protected:
DWORD m_dwFlags; // Cached flags
@@ -559,6 +637,10 @@ protected:
CorElementType m_argType;
int m_argSize;
TypeHandle m_argTypeHandle;
+#if defined(_TARGET_AMD64_) && defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+ ArgLocDesc m_argLocDescForStructInRegs;
+ bool m_hasArgLocDescForStructInRegs;
+#endif // _TARGET_AMD64_ && UNIX_AMD64_ABI && FEATURE_UNIX_AMD64_STRUCT_PASSING
#ifdef _TARGET_X86_
int m_curOfs; // Current position of the stack iterator
@@ -567,9 +649,12 @@ protected:
#ifdef _TARGET_AMD64_
#ifdef UNIX_AMD64_ABI
- int m_idxGenReg;
- int m_idxStack;
- int m_idxFPReg;
+ int m_idxGenReg; // Next general register to be assigned a value
+ int m_idxStack; // Next stack slot to be assigned a value
+ int m_idxFPReg; // Next floating point register to be assigned a value
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+ bool m_fArgInRegisters; // Indicates that the current argument is stored in registers
+#endif
#else
int m_curOfs; // Current position of the stack iterator
#endif
@@ -843,6 +928,10 @@ int ArgIteratorTemplate<ARGITERATOR_BASE>::GetNextOffset()
m_argSize = argSize;
m_argTypeHandle = thValueType;
+#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+ m_hasArgLocDescForStructInRegs = false;
+#endif
+
#ifdef _TARGET_X86_
#ifdef FEATURE_INTERPRETER
if (m_fUnmanagedCallConv)
@@ -862,7 +951,12 @@ int ArgIteratorTemplate<ARGITERATOR_BASE>::GetNextOffset()
return m_curOfs;
#elif defined(_TARGET_AMD64_)
#ifdef UNIX_AMD64_ABI
+
+ m_fArgInRegisters = true;
+
int cFPRegs = 0;
+ int cbArg = StackElemSize(argSize);
+ int cGenRegs = cbArg / 8; // GP reg size
switch (argType)
{
@@ -879,8 +973,56 @@ int ArgIteratorTemplate<ARGITERATOR_BASE>::GetNextOffset()
case ELEMENT_TYPE_VALUETYPE:
{
- // UNIXTODO: Passing of structs, HFAs. For now, use the Windows convention.
- argSize = sizeof(TADDR);
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+ MethodTable *pMT = m_argTypeHandle.AsMethodTable();
+ if (pMT->IsRegPassedStruct())
+ {
+ EEClass* eeClass = pMT->GetClass();
+ cGenRegs = 0;
+ for (int i = 0; i < eeClass->GetNumberEightBytes(); i++)
+ {
+ switch (eeClass->GetEightByteClassification(i))
+ {
+ case SystemVClassificationTypeInteger:
+ case SystemVClassificationTypeIntegerReference:
+ cGenRegs++;
+ break;
+ case SystemVClassificationTypeSSE:
+ cFPRegs++;
+ break;
+ default:
+ _ASSERTE(false);
+ break;
+ }
+ }
+
+ // Check if we have enough registers available for the struct passing
+ if ((cFPRegs + m_idxFPReg <= NUM_FLOAT_ARGUMENT_REGISTERS) && (cGenRegs + m_idxGenReg) <= NUM_ARGUMENT_REGISTERS)
+ {
+ m_argLocDescForStructInRegs.Init();
+ m_argLocDescForStructInRegs.m_cGenReg = cGenRegs;
+ m_argLocDescForStructInRegs.m_cFloatReg = cFPRegs;
+ m_argLocDescForStructInRegs.m_idxGenReg = m_idxGenReg;
+ m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg;
+ m_argLocDescForStructInRegs.m_eeClass = eeClass;
+
+ m_hasArgLocDescForStructInRegs = true;
+
+ m_idxGenReg += cGenRegs;
+ m_idxFPReg += cFPRegs;
+
+ return TransitionBlock::StructInRegsOffset;
+ }
+ }
+
+ // Set the register counts to indicate that this argument will not be passed in registers
+ cFPRegs = 0;
+ cGenRegs = 0;
+
+#else // FEATURE_UNIX_AMD64_STRUCT_PASSING
+ argSize = sizeof(TADDR);
+#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+
break;
}
@@ -888,33 +1030,31 @@ int ArgIteratorTemplate<ARGITERATOR_BASE>::GetNextOffset()
break;
}
- int cbArg = StackElemSize(argSize);
- int cArgSlots = cbArg / STACK_ELEM_SIZE;
-
- if (cFPRegs>0)
+ if ((cFPRegs > 0) && (cFPRegs + m_idxFPReg <= NUM_FLOAT_ARGUMENT_REGISTERS))
{
- if (cFPRegs + m_idxFPReg <= 8)
- {
- int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8;
- m_idxFPReg += cFPRegs;
- return argOfs;
- }
+ int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8;
+ m_idxFPReg += cFPRegs;
+ return argOfs;
}
- else
+ else if ((cGenRegs > 0) && (m_idxGenReg + cGenRegs <= NUM_ARGUMENT_REGISTERS))
{
- if (m_idxGenReg + cArgSlots <= 6)
- {
- int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
- m_idxGenReg += cArgSlots;
- return argOfs;
- }
+ int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8;
+ m_idxGenReg += cGenRegs;
+ return argOfs;
}
- int argOfs = TransitionBlock::GetOffsetOfArgs() + m_idxStack * 8;
+#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+ m_fArgInRegisters = false;
+#endif
+
+ int argOfs = TransitionBlock::GetOffsetOfArgs() + m_idxStack * STACK_ELEM_SIZE;
+
+ int cArgSlots = cbArg / STACK_ELEM_SIZE;
m_idxStack += cArgSlots;
+
return argOfs;
#else
- // Each argument takes exactly one slot on AMD64
+ // Each argument takes exactly one slot on AMD64 on Windows
int argOfs = m_curOfs;
m_curOfs += sizeof(void *);
return argOfs;
@@ -1203,6 +1343,40 @@ void ArgIteratorTemplate<ARGITERATOR_BASE>::ComputeReturnFlags()
{
_ASSERTE(!thValueType.IsNull());
+#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+ MethodTable *pMT = thValueType.AsMethodTable();
+ if (pMT->IsRegPassedStruct())
+ {
+ EEClass* eeClass = pMT->GetClass();
+
+ if (eeClass->GetNumberEightBytes() == 1)
+ {
+ // Structs occupying just one eightbyte are treated as int / double
+ if (eeClass->GetEightByteClassification(0) == SystemVClassificationTypeSSE)
+ {
+ flags |= sizeof(double) << RETURN_FP_SIZE_SHIFT;
+ }
+ }
+ else
+ {
+ // Size of the struct is 16 bytes
+ flags |= (16 << RETURN_FP_SIZE_SHIFT);
+ // The lowest two bits of the size encode the order of the int and SSE fields
+ if (eeClass->GetEightByteClassification(0) == SystemVClassificationTypeSSE)
+ {
+ flags |= (1 << RETURN_FP_SIZE_SHIFT);
+ }
+
+ if (eeClass->GetEightByteClassification(1) == SystemVClassificationTypeSSE)
+ {
+ flags |= (2 << RETURN_FP_SIZE_SHIFT);
+ }
+ }
+
+ break;
+ }
+#else // UNIX_AMD64_ABI && FEATURE_UNIX_AMD64_STRUCT_PASSING
+
#ifdef FEATURE_HFA
if (thValueType.IsHFA() && !this->IsVarArg())
{
@@ -1229,6 +1403,7 @@ void ArgIteratorTemplate<ARGITERATOR_BASE>::ComputeReturnFlags()
if (size <= ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE)
break;
+#endif // UNIX_AMD64_ABI && FEATURE_UNIX_AMD64_STRUCT_PASSING
}
#endif // ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE
@@ -1348,22 +1523,32 @@ void ArgIteratorTemplate<ARGITERATOR_BASE>::ForceSigWalk()
int maxOffset = TransitionBlock::GetOffsetOfArgs();
- int ofs;
+ int ofs;
while (TransitionBlock::InvalidOffset != (ofs = GetNextOffset()))
{
int stackElemSize;
#ifdef _TARGET_AMD64_
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+ if (m_fArgInRegisters)
+ {
+ // Arguments passed in registers don't consume any stack
+ continue;
+ }
+
+ stackElemSize = StackElemSize(GetArgSize());
+#else // FEATURE_UNIX_AMD64_STRUCT_PASSING
// All stack arguments take just one stack slot on AMD64 because of arguments bigger
// than a stack slot are passed by reference.
stackElemSize = STACK_ELEM_SIZE;
-#else
+#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+#else // _TARGET_AMD64_
stackElemSize = StackElemSize(GetArgSize());
#if defined(ENREGISTERED_PARAMTYPE_MAXSIZE)
if (IsArgPassedByRef())
stackElemSize = STACK_ELEM_SIZE;
#endif
-#endif
+#endif // _TARGET_AMD64_
int endOfs = ofs + stackElemSize;
if (endOfs > maxOffset)