diff options
author | Lubomir Litchev <llitchev@live.com> | 2015-02-19 11:42:30 -0800 |
---|---|---|
committer | Lubomir Litchev <lubol@microsoft.com> | 2015-10-20 14:20:36 -0700 |
commit | 378e304f9e22b3c4d03c3b1b62c47b0aa58ceaf5 (patch) | |
tree | b83aec8f77caeb9ca94c6d7505a548b93cdb7259 /src/vm/object.cpp | |
parent | 3015ff7afb4936a1c5c5856daa4e3482e6b390a9 (diff) | |
download | coreclr-378e304f9e22b3c4d03c3b1b62c47b0aa58ceaf5.tar.gz coreclr-378e304f9e22b3c4d03c3b1b62c47b0aa58ceaf5.tar.bz2 coreclr-378e304f9e22b3c4d03c3b1b62c47b0aa58ceaf5.zip |
Implementation of System V ABI struct passing.
This PR adds support for System V x86_64 ABI classification and calling
convention to the VM and the Jit, including, but not limited to Ubuntu
Linux and Mac OS X.
The general rules outlined in the System V x86_64 ABI (described at
http://www.x86-64.org/documentation/abi.pdf) are followed with a few
little exceptions, described below:
1. The hidden argument for by-value passed structs is always after
the ÎéÎíthisÎéÎí parameter (if there is one.). This is a difference with
the Sysetem V ABI and affects only the internal jit calling conventions.
For PInvoke calls the hidden argument is always the first parameter since
there is no ÎéÎíthisÎéÎí parameter in this case.
2. Managed structs that have no fields are always passed by-value on
the stack.
3. The jit proactively generates frame register frames (with RBP as a
frame register) in order to aid the native OS tooling for stack unwinding
and the like.
Diffstat (limited to 'src/vm/object.cpp')
-rw-r--r-- | src/vm/object.cpp | 135 |
1 files changed, 132 insertions, 3 deletions
diff --git a/src/vm/object.cpp b/src/vm/object.cpp index 3b07a12543..25a7109905 100644 --- a/src/vm/object.cpp +++ b/src/vm/object.cpp @@ -24,6 +24,7 @@ #endif #include "field.h" #include "gcscan.h" +#include "argdestination.h" #ifdef FEATURE_COMPRESSEDSTACK void* CompressedStackObject::GetUnmanagedCompressedStack() @@ -1498,6 +1499,31 @@ void CopyValueClassChecked(void* dest, void* src, MethodTable *pMT, AppDomain *p EX_END_CATCH(SwallowAllExceptions); CopyValueClassUnchecked(dest,src,pMT); } + +// Copy value class into the argument specified by the argDest, performing an appdomain check first. +// The destOffset is nonzero when copying values into Nullable<T>, it is the offset +// of the T value inside of the Nullable<T> +void CopyValueClassArgChecked(ArgDestination *argDest, void* src, MethodTable *pMT, AppDomain *pDomain, int destOffset) +{ + STATIC_CONTRACT_DEBUG_ONLY; + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + STATIC_CONTRACT_MODE_COOPERATIVE; + + DEBUG_ONLY_FUNCTION; + + FAULT_NOT_FATAL(); + EX_TRY + { + Object::AssignValueTypeAppDomain(pMT, src, pDomain); + } + EX_CATCH + { + } + EX_END_CATCH(SwallowAllExceptions); + CopyValueClassArgUnchecked(argDest, src, pMT, destOffset); +} #endif void STDCALL CopyValueClassUnchecked(void* dest, void* src, MethodTable *pMT) @@ -1563,6 +1589,51 @@ void STDCALL CopyValueClassUnchecked(void* dest, void* src, MethodTable *pMT) } } +// Copy value class into the argument specified by the argDest. +// The destOffset is nonzero when copying values into Nullable<T>, it is the offset +// of the T value inside of the Nullable<T> +void STDCALL CopyValueClassArgUnchecked(ArgDestination *argDest, void* src, MethodTable *pMT, int destOffset) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + STATIC_CONTRACT_MODE_COOPERATIVE; + +#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + + if (argDest->IsStructPassedInRegs()) + { + argDest->CopyStructToRegisters(src, pMT->GetNumInstanceFieldBytes(), destOffset); + return; + } + +#endif // UNIX_AMD64_ABI && FEATURE_UNIX_AMD64_STRUCT_PASSING + // destOffset is only valid for Nullable<T> passed in registers + _ASSERTE(destOffset == 0); + + CopyValueClassUnchecked(argDest->GetDestinationAddress(), src, pMT); +} + +// Initialize the value class argument to zeros +void InitValueClassArg(ArgDestination *argDest, MethodTable *pMT) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + STATIC_CONTRACT_MODE_COOPERATIVE; + +#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + + if (argDest->IsStructPassedInRegs()) + { + argDest->ZeroStructInRegisters(pMT->GetNumInstanceFieldBytes()); + return; + } + +#endif + InitValueClass(argDest->GetDestinationAddress(), pMT); +} + #if defined (VERIFY_HEAP) #include "dbginterface.h" @@ -3245,7 +3316,7 @@ BOOL Nullable::UnBox(void* destPtr, OBJECTREF boxedVal, MethodTable* destMT) if (boxedVal == NULL) { - // logicall we are doing *dest->HasValueAddr(destMT) = false; + // Logically we are doing *dest->HasValueAddr(destMT) = false; // We zero out the whole structure becasue it may contain GC references // and these need to be initialized to zero. (could optimize in the non-GC case) InitValueClass(destPtr, destMT); @@ -3302,7 +3373,7 @@ BOOL Nullable::UnBoxNoGC(void* destPtr, OBJECTREF boxedVal, MethodTable* destMT) if (boxedVal == NULL) { - // logicall we are doing *dest->HasValueAddr(destMT) = false; + // Logically we are doing *dest->HasValueAddr(destMT) = false; // We zero out the whole structure becasue it may contain GC references // and these need to be initialized to zero. (could optimize in the non-GC case) InitValueClass(destPtr, destMT); @@ -3328,6 +3399,64 @@ BOOL Nullable::UnBoxNoGC(void* destPtr, OBJECTREF boxedVal, MethodTable* destMT) } //=============================================================================== +// Special Logic to unbox a boxed T as a nullable<T> into an argument +// specified by the argDest. +// Does not handle type equivalence (may conservatively return FALSE) +BOOL Nullable::UnBoxIntoArgNoGC(ArgDestination *argDest, OBJECTREF boxedVal, MethodTable* destMT) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + SO_TOLERANT; + } + CONTRACTL_END; + +#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) + if (argDest->IsStructPassedInRegs()) + { + // We should only get here if we are unboxing a T as a Nullable<T> + _ASSERTE(IsNullableType(destMT)); + + // We better have a concrete instantiation, or our field offset asserts are not useful + _ASSERTE(!destMT->ContainsGenericVariables()); + + if (boxedVal == NULL) + { + // Logically we are doing *dest->HasValueAddr(destMT) = false; + // We zero out the whole structure becasue it may contain GC references + // and these need to be initialized to zero. (could optimize in the non-GC case) + InitValueClassArg(argDest, destMT); + } + else + { + if (!IsNullableForTypeNoGC(destMT, boxedVal->GetMethodTable())) + { + // For safety's sake, also allow true nullables to be unboxed normally. + // This should not happen normally, but we want to be robust + if (destMT == boxedVal->GetMethodTable()) + { + CopyValueClassArg(argDest, boxedVal->GetData(), destMT, boxedVal->GetAppDomain(), 0); + return TRUE; + } + return FALSE; + } + + Nullable* dest = (Nullable*)argDest->GetStructGenRegDestinationAddress(); + *dest->HasValueAddr(destMT) = true; + int destOffset = (BYTE*)dest->ValueAddr(destMT) - (BYTE*)dest; + CopyValueClassArg(argDest, boxedVal->UnBox(), boxedVal->GetMethodTable(), boxedVal->GetAppDomain(), destOffset); + } + return TRUE; + } + +#endif // UNIX_AMD64_ABI && FEATURE_UNIX_AMD64_STRUCT_PASSING + + return UnBoxNoGC(argDest->GetDestinationAddress(), boxedVal, destMT); +} + +//=============================================================================== // Special Logic to unbox a boxed T as a nullable<T> // Does not do any type checks. void Nullable::UnBoxNoCheck(void* destPtr, OBJECTREF boxedVal, MethodTable* destMT) @@ -3350,7 +3479,7 @@ void Nullable::UnBoxNoCheck(void* destPtr, OBJECTREF boxedVal, MethodTable* dest if (boxedVal == NULL) { - // logicall we are doing *dest->HasValueAddr(destMT) = false; + // Logically we are doing *dest->HasValueAddr(destMT) = false; // We zero out the whole structure becasue it may contain GC references // and these need to be initialized to zero. (could optimize in the non-GC case) InitValueClass(destPtr, destMT); |