Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Wrighton <davidwr@microsoft.com>2017-07-21 05:27:06 +0300
committerDavid Wrighton <davidwr@microsoft.com>2017-07-21 05:27:06 +0300
commitb89039fa25ec5eecb17bf5a79a6fff40f73cfa80 (patch)
treebbd54e09d0e6a1c4c4d2c68681499dff4edcd744 /src/System.Private.TypeLoader
parentdf3930a848b7e2da303713b5fd96d8b09d0ff482 (diff)
Add Sign-extension/Zero-truncation behavior to the calling convention converter
- Arm32 requires this, x86 and x64 do not - I've currently enabled it for all platforms, as it shouldn't cause incorrect behavior on any of them - When copying a primitive integer value into the registers, sign extend or zero truncate all values which are less than pointer size. - Fixes corefx issue https://github.com/dotnet/corefx/issues/21379 [tfs-changeset: 1667207]
Diffstat (limited to 'src/System.Private.TypeLoader')
-rw-r--r--src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.cs202
-rw-r--r--src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallInterceptor.cs195
2 files changed, 343 insertions, 54 deletions
diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.cs
index 3c0c2453d..1038c02b7 100644
--- a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.cs
+++ b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallConverterThunk.cs
@@ -116,7 +116,9 @@ namespace Internal.Runtime.TypeLoader
private extern static unsafe void CallingConventionConverter_SpecifyCommonStubData(IntPtr commonStubData);
#endif
- static unsafe CallConverterThunk()
+ private static bool s_callConverterThunk = CallConverterThunk_LazyCctor();
+
+ private static unsafe bool CallConverterThunk_LazyCctor()
{
#if PLATFORM_UNIX
// TODO
@@ -137,6 +139,7 @@ namespace Internal.Runtime.TypeLoader
#endif //_TARGET_ARM_
#endif // PLATFORM_UNIX
+ return true;
}
internal static bool GetByRefIndicatorAtIndex(int index, bool[] lookup)
@@ -811,34 +814,14 @@ namespace Internal.Runtime.TypeLoader
if (isCalleeArgPassedByRef == isCallerArgPassedByRef)
{
// Argument copies without adjusting calling convention.
- switch (stackSizeCallee)
+ if (isCalleeArgPassedByRef)
{
- case 1:
- case 2:
- case 4:
- *((int*)pDest) = *((int*)pSrc);
- break;
-
- case 8:
- *((long*)pDest) = *((long*)pSrc);
- break;
-
- default:
- if (isCalleeArgPassedByRef)
- {
- // even though this argument is passed by value, the actual calling convention
- // passes a pointer to the value of the argument.
- Debug.Assert(isCallerArgPassedByRef);
- // Copy the pointer from the incoming arguments to the outgoing arguments.
- *((void**)pDest) = *((void**)pSrc);
- }
- else
- {
- // In this case, the valuetype is passed directly on the stack, even though it is
- // a non-integral size.
- Buffer.MemoryCopy(pSrc, pDest, stackSizeCallee, stackSizeCallee);
- }
- break;
+ *((IntPtr*)pDest) = *(IntPtr*)pSrc;
+ }
+ else
+ {
+ CorElementType argElemType = conversionParams._calleeArgs.GetArgType(out thValueType);
+ ExtendingCopy_NoWriteBarrier(pSrc, pDest, argElemType, stackSizeCaller);
}
}
else
@@ -853,7 +836,9 @@ namespace Internal.Runtime.TypeLoader
else
{
// Copy into the destination the data pointed at by the pointer in the source(caller) data.
- Buffer.MemoryCopy(*(byte**)pSrc, pDest, stackSizeCaller, stackSizeCaller);
+ byte* pRealSrc = *(byte**)pSrc;
+ CorElementType argElemType = conversionParams._calleeArgs.GetArgType(out thValueType);
+ ExtendingCopy_NoWriteBarrier(pRealSrc, pDest, argElemType, stackSizeCaller);
}
}
@@ -1109,14 +1094,11 @@ namespace Internal.Runtime.TypeLoader
if (returnValueToCopy == null)
{
// object array delegate thunk result is a null object. We'll fill the return buffer with 'returnSize' zeros in that case
- memzeroPointer((byte*)(&((TransitionBlock*)callerTransitionBlock)->m_returnBlock), returnSize);
+ memzeroPointer((byte*)(&((TransitionBlock*)callerTransitionBlock)->m_returnBlock), sizeof(ReturnBlock));
}
else
{
- if (isPointerAligned(&((TransitionBlock*)callerTransitionBlock)->m_returnBlock) && isPointerAligned(returnValueToCopy) && (returnSize % sizeof(IntPtr) == 0))
- RuntimeAugments.BulkMoveWithWriteBarrier(new IntPtr(&((TransitionBlock*)callerTransitionBlock)->m_returnBlock), new IntPtr(returnValueToCopy), returnSize);
- else
- Buffer.MemoryCopy(returnValueToCopy, &((TransitionBlock*)callerTransitionBlock)->m_returnBlock, returnSize, returnSize);
+ ExtendingCopy_WriteBarrier(returnValueToCopy, &((TransitionBlock*)callerTransitionBlock)->m_returnBlock, returnType, returnSize);
}
#endif
}
@@ -1140,6 +1122,10 @@ namespace Internal.Runtime.TypeLoader
CallConversionParameters.s_pinnedGCHandles._returnObjectHandle.Target = returnValue;
pinnedResultObject = CallConversionParameters.s_pinnedGCHandles._returnObjectHandle.GetRawTargetAddress();
returnValueToCopy = (void*)&pinnedResultObject;
+ // Since we've changed the returnValueToCopy here, we need to update the idea of what we are returning
+ returnType = CorElementType.ELEMENT_TYPE_OBJECT;
+ thValueType = default(TypeHandle);
+ returnSize = TypeHandle.GetElemSize(returnType, thValueType);
#if _TARGET_X86_
((TransitionBlock*)callerTransitionBlock)->m_returnBlock.returnValue = pinnedResultObject;
@@ -1214,26 +1200,158 @@ namespace Internal.Runtime.TypeLoader
}
else
{
- if (isPointerAligned(callerTransitionBlock + TransitionBlock.GetOffsetOfArgumentRegisters()) && isPointerAligned(returnValueToCopy) && (returnSize % sizeof(IntPtr) == 0))
- RuntimeAugments.BulkMoveWithWriteBarrier(new IntPtr(callerTransitionBlock + TransitionBlock.GetOffsetOfArgumentRegisters()), new IntPtr(returnValueToCopy), returnSize);
- else
- Buffer.MemoryCopy(returnValueToCopy, callerTransitionBlock + TransitionBlock.GetOffsetOfArgumentRegisters(), returnSize, returnSize);
+ ExtendingCopy_WriteBarrier(returnValueToCopy, callerTransitionBlock + TransitionBlock.GetOffsetOfArgumentRegisters(), returnType, returnSize);
}
}
else
{
Debug.Assert(returnValueToCopy != null);
- Buffer.MemoryCopy(returnValueToCopy,
- callerTransitionBlock + TransitionBlock.GetOffsetOfArgumentRegisters(),
- ArchitectureConstants.ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE_PRIMITIVE,
- ArchitectureConstants.ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE_PRIMITIVE);
+ ExtendingCopy_NoWriteBarrier(returnValueToCopy, callerTransitionBlock + TransitionBlock.GetOffsetOfArgumentRegisters(), returnType, returnSize);
}
conversionParams._invokeReturnValue = ReturnIntegerPointReturnThunk;
#endif
}
}
+ //
+ // Converting by-ref values to non-by-ref form requires the converter to be capable of taking a pointer to a small integer
+ // value anywhere in memory and then copying the referenced value into an ABI-compliant pointer-sized "slot" which
+ // faithfully communicates the value. In such cases, the argument slot prepared by the converter must conform to all
+ // sign/zero-extension rules mandated by the ABI.
+ //
+ // ARM32 requires all less-than-pointer-sized values to be sign/zero-extended when they are placed into pointer-sized
+ // slots (i.e., requires "producer-oriented" sign/zero-extension). x86/amd64 do not have this requirement (i.e., the
+ // unused high bytes of the pointer-sized slot are ignored by the consumer and are allowed to take on any value); however
+ // to reduce the need for ever more #ifs in this file, this behavior will not be #if'd away. (Its not wrong, its just unnecessary)
+ //
+ private static unsafe void ExtendingCopy_WriteBarrier(void* pSrcVoid, void* pDestVoid, CorElementType type, int typeSize)
+ {
+ byte* pSrc = (byte*)pSrcVoid;
+ byte* pDest = (byte*)pDestVoid;
+
+ if (SignExtendType(type))
+ SignExtend(pSrc, pDest, typeSize);
+ else if (ZeroExtendType(type))
+ ZeroExtend(pSrc, pDest, typeSize);
+ else if (isPointerAligned(pSrc) && isPointerAligned(pDest) && (typeSize % sizeof(IntPtr) == 0))
+ RuntimeAugments.BulkMoveWithWriteBarrier(new IntPtr(pDest), new IntPtr(pSrc), typeSize);
+ else
+ Buffer.MemoryCopy(pSrc, pDest, typeSize, typeSize);
+ }
+
+ //
+ // Converting by-ref values to non-by-ref form requires the converter to be capable of taking a pointer to a small integer
+ // value anywhere in memory and then copying the referenced value into an ABI-compliant pointer-sized "slot" which
+ // faithfully communicates the value. In such cases, the argument slot prepared by the converter must conform to all
+ // sign/zero-extension rules mandated by the ABI.
+ //
+ // ARM32 requires all less-than-pointer-sized values to be sign/zero-extended when they are placed into pointer-sized
+ // slots (i.e., requires "producer-oriented" sign/zero-extension). x86/amd64 do not have this requirement (i.e., the
+ // unused high bytes of the pointer-sized slot are ignored by the consumer and are allowed to take on any value); however
+ // to reduce the need for ever more #ifs in this file, this behavior will not be #if'd away. (Its not wrong, its just unnecessary)
+ //
+ private static unsafe void ExtendingCopy_NoWriteBarrier(void* pSrcVoid, void* pDestVoid, CorElementType type, int typeSize)
+ {
+ byte* pSrc = (byte*)pSrcVoid;
+ byte* pDest = (byte*)pDestVoid;
+
+ if (SignExtendType(type))
+ SignExtend(pSrc, pDest, typeSize);
+ else if (ZeroExtendType(type))
+ ZeroExtend(pSrc, pDest, typeSize);
+ else
+ Buffer.MemoryCopy(pSrc, pDest, typeSize, typeSize);
+ }
+
+ private static bool SignExtendType(CorElementType type)
+ {
+ switch (type)
+ {
+ case CorElementType.ELEMENT_TYPE_I1:
+ case CorElementType.ELEMENT_TYPE_I2:
+#if BIT64
+ case CorElementType.ELEMENT_TYPE_I4:
+#endif
+ return true;
+
+ }
+
+ return false;
+ }
+
+ private static bool ZeroExtendType(CorElementType type)
+ {
+ switch (type)
+ {
+ case CorElementType.ELEMENT_TYPE_U1:
+ case CorElementType.ELEMENT_TYPE_BOOLEAN:
+ case CorElementType.ELEMENT_TYPE_CHAR:
+ case CorElementType.ELEMENT_TYPE_U2:
+#if BIT64
+ case CorElementType.ELEMENT_TYPE_U4:
+#endif
+ return true;
+
+ }
+
+ return false;
+ }
+
+ internal static unsafe void SignExtend(void* pSrcVoid, void* pDestVoid, int size)
+ {
+ byte* pSrc = (byte*)pSrcVoid;
+ byte* pDest = (byte*)pDestVoid;
+
+ switch (size)
+ {
+ case 1:
+ *((IntPtr*)pDest) = new IntPtr(*(sbyte*)pSrc);
+ break;
+
+ case 2:
+ *((IntPtr*)pDest) = new IntPtr(*(short*)pSrc);
+ break;
+
+#if BIT64
+ // On 64 bit platforms, a 32 bit parameter may require truncation/extension
+ case 4:
+ *((IntPtr*)pDest) = new IntPtr(*(int*)pSrc);
+ break;
+#endif
+ default:
+ Debug.Assert(false, "Should only be called for sizes where sign extension is a meaningful concept");
+ break;
+ }
+ }
+
+ internal static unsafe void ZeroExtend(void* pSrcVoid, void* pDestVoid, int size)
+ {
+ byte* pSrc = (byte*)pSrcVoid;
+ byte* pDest = (byte*)pDestVoid;
+
+ switch (size)
+ {
+ case 1:
+ *((UIntPtr*)pDest) = new UIntPtr(*(byte*)pSrc);
+ break;
+
+ case 2:
+ *((UIntPtr*)pDest) = new UIntPtr(*(ushort*)pSrc);
+ break;
+
+#if BIT64
+ // On 64 bit platforms, a 32 bit parameter may require truncation/extension
+ case 4:
+ *((UIntPtr*)pDest) = new UIntPtr(*(uint*)pSrc);
+ break;
+#endif
+ default:
+ Debug.Assert(false, "Should only be called for sizes where sign extension is a meaningful concept");
+ break;
+ }
+ }
+
#if CALLINGCONVENTION_CALLEE_POPS
private static unsafe void SetupCallerPopArgument(byte* callerTransitionBlock, ArgIterator callerArgs)
{
diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallInterceptor.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallInterceptor.cs
index f7b090965..002b3782f 100644
--- a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallInterceptor.cs
+++ b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallInterceptor.cs
@@ -440,8 +440,9 @@ namespace Internal.Runtime.CallInterceptor
if (ofsCallee < 0)
needsFloatArgs = true;
- TypeHandle dummyTypeHandle;
- if (calleeArgs.IsArgPassedByRef() && calleeArgs.GetArgType(out dummyTypeHandle) != CorElementType.ELEMENT_TYPE_BYREF)
+ TypeHandle argTypeHandle;
+ CorElementType argType = calleeArgs.GetArgType(out argTypeHandle);
+ if (calleeArgs.IsArgPassedByRef() && argType != CorElementType.ELEMENT_TYPE_BYREF)
{
callConversionOps.Add(new CallConversionOperation(
CallConversionOperation.OpCode.COPY_X_BYTES_FROM_LOCALBLOCK_Y_OFFSET_Z_IN_LOCALBLOCK_TO_OFFSET_W_IN_TRANSITION_BLOCK,
@@ -456,16 +457,69 @@ namespace Internal.Runtime.CallInterceptor
}
else
{
- callConversionOps.Add(new CallConversionOperation(
- CallConversionOperation.OpCode.COPY_X_BYTES_FROM_LOCALBLOCK_Y_POINTER_Z_TO_OFFSET_W_IN_TRANSITION_BLOCK,
- calleeArgs.GetArgSize(),
- CallConversionInterpreter.ArgBlock,
- i,
- ofsCallee
+ //
+ // Converting by-ref values to non-by-ref form requires the converter to be capable of taking a pointer to a small integer
+ // value anywhere in memory and then copying the referenced value into an ABI-compliant pointer-sized "slot" which
+ // faithfully communicates the value. In such cases, the argument slot prepared by the converter must conform to all
+ // sign/zero-extension rules mandated by the ABI.
+ //
+ // ARM32 requires all less-than-pointer-sized values to be sign/zero-extended when they are placed into pointer-sized
+ // slots (i.e., requires "producer-oriented" sign/zero-extension). x86/amd64 do not have this requirement (i.e., the
+ // unused high bytes of the pointer-sized slot are ignored by the consumer and are allowed to take on any value); however
+ // to reduce the need for ever more #ifs in this file, this behavior will not be #if'd away. (Its not wrong, its just unnecessary)
+ //
+
+ switch (argType)
+ {
+ case CorElementType.ELEMENT_TYPE_I1:
+ case CorElementType.ELEMENT_TYPE_I2:
+#if BIT64
+ case CorElementType.ELEMENT_TYPE_I4:
+#endif
+ callConversionOps.Add(new CallConversionOperation(
+ CallConversionOperation.OpCode.SIGNEXTEND_X_BYTES_FROM_LOCALBLOCK_Y_POINTER_Z_TO_OFFSET_W_IN_TRANSITION_BLOCK,
+ calleeArgs.GetArgSize(),
+ CallConversionInterpreter.ArgBlock,
+ i,
+ ofsCallee
#if CCCONVERTER_TRACE
- , "Arg #" + i.LowLevelToString()
+ , "Arg #" + i.LowLevelToString()
#endif
- ));
+ ));
+ break;
+
+ case CorElementType.ELEMENT_TYPE_U1:
+ case CorElementType.ELEMENT_TYPE_BOOLEAN:
+ case CorElementType.ELEMENT_TYPE_U2:
+ case CorElementType.ELEMENT_TYPE_CHAR:
+#if BIT64
+ case CorElementType.ELEMENT_TYPE_U4:
+#endif
+ callConversionOps.Add(new CallConversionOperation(
+ CallConversionOperation.OpCode.ZEROEXTEND_X_BYTES_FROM_LOCALBLOCK_Y_POINTER_Z_TO_OFFSET_W_IN_TRANSITION_BLOCK,
+ calleeArgs.GetArgSize(),
+ CallConversionInterpreter.ArgBlock,
+ i,
+ ofsCallee
+#if CCCONVERTER_TRACE
+ , "Arg #" + i.LowLevelToString()
+#endif
+ ));
+ break;
+
+ default:
+ callConversionOps.Add(new CallConversionOperation(
+ CallConversionOperation.OpCode.COPY_X_BYTES_FROM_LOCALBLOCK_Y_POINTER_Z_TO_OFFSET_W_IN_TRANSITION_BLOCK,
+ calleeArgs.GetArgSize(),
+ CallConversionInterpreter.ArgBlock,
+ i,
+ ofsCallee
+#if CCCONVERTER_TRACE
+ , "Arg #" + i.LowLevelToString()
+#endif
+ ));
+ break;
+ }
}
}
@@ -559,6 +613,12 @@ namespace Internal.Runtime.CallInterceptor
case OpCode.COPY_X_BYTES_FROM_LOCALBLOCK_Y_POINTER_Z_TO_OFFSET_W_IN_TRANSITION_BLOCK:
s = "COPY_X_BYTES_FROM_LOCALBLOCK_Y_POINTER_Z_TO_OFFSET_W_IN_TRANSITION_BLOCK";
break;
+ case OpCode.ZEROEXTEND_X_BYTES_FROM_LOCALBLOCK_Y_POINTER_Z_TO_OFFSET_W_IN_TRANSITION_BLOCK:
+ s = "ZEROEXTEND_X_BYTES_FROM_LOCALBLOCK_Y_POINTER_Z_TO_OFFSET_W_IN_TRANSITION_BLOCK";
+ break;
+ case OpCode.SIGNEXTEND_X_BYTES_FROM_LOCALBLOCK_Y_POINTER_Z_TO_OFFSET_W_IN_TRANSITION_BLOCK:
+ s = "SIGNEXTEND_X_BYTES_FROM_LOCALBLOCK_Y_POINTER_Z_TO_OFFSET_W_IN_TRANSITION_BLOCK";
+ break;
case OpCode.COPY_X_BYTES_TO_LOCALBLOCK_Y_POINTER_Z_FROM_OFFSET_W_IN_TRANSITION_BLOCK:
s = "COPY_X_BYTES_TO_LOCALBLOCK_Y_POINTER_Z_FROM_OFFSET_W_IN_TRANSITION_BLOCK";
break;
@@ -583,6 +643,12 @@ namespace Internal.Runtime.CallInterceptor
case OpCode.RETURN_INTEGER_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z:
s = "RETURN_INTEGER_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z";
break;
+ case OpCode.RETURN_SIGNEXTENDED_INTEGER_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z:
+ s = "RETURN_SIGNEXTENDED_INTEGER_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z";
+ break;
+ case OpCode.RETURN_ZEROEXTENDED_INTEGER_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z:
+ s = "RETURN_ZEROEXTENDED_INTEGER_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z";
+ break;
case OpCode.CALL_DESCR_MANAGED_WITH_RETBUF_AS_LOCALBLOCK_X_POINTER_Y_STACKSLOTS_Z_FPCALLINFO_W:
s = "CALL_DESCR_MANAGED_WITH_RETBUF_AS_LOCALBLOCK_X_POINTER_Y_STACKSLOTS_Z_FPCALLINFO_W";
break;
@@ -622,7 +688,7 @@ namespace Internal.Runtime.CallInterceptor
}
#else
- public CallConversionOperation(OpCode op, int X, int Y, int Z, int W)
+ public CallConversionOperation(OpCode op, int X, int Y, int Z, int W)
{
this.Op = op;
this.X = X;
@@ -672,6 +738,8 @@ namespace Internal.Runtime.CallInterceptor
SET_LOCALBLOCK_X_POINTER_Y_TO_OFFSET_Z_IN_LOCALBLOCK,
COPY_X_BYTES_FROM_LOCALBLOCK_Y_OFFSET_Z_IN_LOCALBLOCK_TO_OFFSET_W_IN_TRANSITION_BLOCK,
COPY_X_BYTES_FROM_LOCALBLOCK_Y_POINTER_Z_TO_OFFSET_W_IN_TRANSITION_BLOCK,
+ SIGNEXTEND_X_BYTES_FROM_LOCALBLOCK_Y_POINTER_Z_TO_OFFSET_W_IN_TRANSITION_BLOCK,
+ ZEROEXTEND_X_BYTES_FROM_LOCALBLOCK_Y_POINTER_Z_TO_OFFSET_W_IN_TRANSITION_BLOCK,
COPY_X_BYTES_TO_LOCALBLOCK_Y_POINTER_Z_FROM_OFFSET_W_IN_TRANSITION_BLOCK,
COPY_X_BYTES_TO_LOCALBLOCK_Y_OFFSET_Z_IN_LOCALBLOCK_FROM_OFFSET_W_IN_TRANSITION_BLOCK,
CALL_INTERCEPTOR,
@@ -680,6 +748,8 @@ namespace Internal.Runtime.CallInterceptor
RETURN_RETBUF_FROM_OFFSET_X_IN_TRANSITION_BLOCK,
RETURN_FLOATINGPOINT_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z,
RETURN_INTEGER_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z,
+ RETURN_SIGNEXTENDED_INTEGER_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z,
+ RETURN_ZEROEXTENDED_INTEGER_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z,
CALL_DESCR_MANAGED_WITH_RETBUF_AS_LOCALBLOCK_X_POINTER_Y_STACKSLOTS_Z_FPCALLINFO_W,
CALL_DESCR_NATIVE_WITH_RETBUF_AS_LOCALBLOCK_X_POINTER_Y_STACKSLOTS_Z_FPCALLINFO_W,
COPY_X_BYTES_FROM_RETBUF_TO_LOCALBLOCK_Y_POINTER_Z,
@@ -827,6 +897,30 @@ namespace Internal.Runtime.CallInterceptor
}
break;
+ case CallConversionOperation.OpCode.SIGNEXTEND_X_BYTES_FROM_LOCALBLOCK_Y_POINTER_Z_TO_OFFSET_W_IN_TRANSITION_BLOCK:
+ {
+ void* pSrc = locals.GetLocalBlock(op.Y).GetRawMemoryPointer()[op.Z].ToPointer();
+ void* pDst = locals.TransitionBlockPtr + op.W;
+ CallConverterThunk.SignExtend(pSrc, pDst, op.X);
+
+#if CCCONVERTER_TRACE
+ CallingConventionConverterLogger.WriteLine(" -> SignExtend " + op.X.LowLevelToString() + " bytes from [" + new IntPtr(pSrc).LowLevelToString() + "] to [" + new IntPtr(pDst).LowLevelToString() + "]");
+#endif
+ }
+ break;
+
+ case CallConversionOperation.OpCode.ZEROEXTEND_X_BYTES_FROM_LOCALBLOCK_Y_POINTER_Z_TO_OFFSET_W_IN_TRANSITION_BLOCK:
+ {
+ void* pSrc = locals.GetLocalBlock(op.Y).GetRawMemoryPointer()[op.Z].ToPointer();
+ void* pDst = locals.TransitionBlockPtr + op.W;
+ CallConverterThunk.ZeroExtend(pSrc, pDst, op.X);
+
+#if CCCONVERTER_TRACE
+ CallingConventionConverterLogger.WriteLine(" -> ZeroExtend " + op.X.LowLevelToString() + " bytes from [" + new IntPtr(pSrc).LowLevelToString() + "] to [" + new IntPtr(pDst).LowLevelToString() + "]");
+#endif
+ }
+ break;
+
case CallConversionOperation.OpCode.COPY_X_BYTES_TO_LOCALBLOCK_Y_OFFSET_Z_IN_LOCALBLOCK_FROM_OFFSET_W_IN_TRANSITION_BLOCK:
{
void* pSrc = locals.TransitionBlockPtr + op.W;
@@ -922,6 +1016,49 @@ namespace Internal.Runtime.CallInterceptor
#endif
}
break;
+
+ case CallConversionOperation.OpCode.RETURN_SIGNEXTENDED_INTEGER_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z:
+ {
+#if X86
+ CallConverterThunk.SetupCallerActualReturnData(locals.TransitionBlockPtr);
+ fixed (ReturnBlock* retBlk = &CallConverterThunk.t_NonArgRegisterReturnSpace)
+ {
+ CallConverterThunk.SignExtend(locals.GetLocalBlock(op.X).GetRawMemoryPointer()[op.Y].ToPointer(), retBlk, op.Z);
+ }
+ locals.IntPtrReturnVal = CallConverterThunk.ReturnIntegerPointReturnThunk;
+#else
+ byte* returnBlock = locals.TransitionBlockPtr + TransitionBlock.GetOffsetOfArgumentRegisters();
+ SignExtend(locals.GetLocalBlock(op.X).GetRawMemoryPointer()[op.Y].ToPointer(), returnBlock, op.Z);
+ locals.IntPtrReturnVal = CallConverterThunk.ReturnIntegerPointReturnThunk;
+#endif
+
+#if CCCONVERTER_TRACE
+ CallingConventionConverterLogger.WriteLine(" -> SignExtend " + op.Z.LowLevelToString() + " bytes from [" + new IntPtr(locals.GetLocalBlock(op.X).GetRawMemoryPointer()[op.Y].ToPointer()).LowLevelToString() + "] to return block");
+#endif
+ }
+ break;
+
+ case CallConversionOperation.OpCode.RETURN_ZEROEXTENDED_INTEGER_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z:
+ {
+#if X86
+ CallConverterThunk.SetupCallerActualReturnData(locals.TransitionBlockPtr);
+ fixed (ReturnBlock* retBlk = &CallConverterThunk.t_NonArgRegisterReturnSpace)
+ {
+ CallConverterThunk.ZeroExtend(locals.GetLocalBlock(op.X).GetRawMemoryPointer()[op.Y].ToPointer(), retBlk, op.Z);
+ }
+ locals.IntPtrReturnVal = CallConverterThunk.ReturnIntegerPointReturnThunk;
+#else
+ byte* returnBlock = locals.TransitionBlockPtr + TransitionBlock.GetOffsetOfArgumentRegisters();
+ ZeroExtend(locals.GetLocalBlock(op.X).GetRawMemoryPointer()[op.Y].ToPointer(), returnBlock, op.Z);
+ locals.IntPtrReturnVal = CallConverterThunk.ReturnIntegerPointReturnThunk;
+#endif
+
+#if CCCONVERTER_TRACE
+ CallingConventionConverterLogger.WriteLine(" -> ZeroExtend " + op.Z.LowLevelToString() + " bytes from [" + new IntPtr(locals.GetLocalBlock(op.X).GetRawMemoryPointer()[op.Y].ToPointer()).LowLevelToString() + "] to return block");
+#endif
+ }
+ break;
+
case CallConversionOperation.OpCode.RETURN_FLOATINGPOINT_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z:
{
#if CALLDESCR_FPARGREGSARERETURNREGS
@@ -1343,7 +1480,41 @@ namespace Internal.Runtime.CallInterceptor
}
else
{
- callConversionOps.Add(new CallConversionOperation(CallConversionOperation.OpCode.RETURN_INTEGER_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z, CallConversionInterpreter.ArgBlock, 0, checked((int)returnType.GetSize())));
+ //
+ // Converting by-ref values to non-by-ref form requires the converter to be capable of taking a pointer to a small integer
+ // value anywhere in memory and then copying the referenced value into an ABI-compliant pointer-sized "slot" which
+ // faithfully communicates the value. In such cases, the argument slot prepared by the converter must conform to all
+ // sign/zero-extension rules mandated by the ABI.
+ //
+ // ARM32 requires all less-than-pointer-sized values to be sign/zero-extended when they are placed into pointer-sized
+ // slots (i.e., requires "producer-oriented" sign/zero-extension). x86/amd64 do not have this requirement (i.e., the
+ // unused high bytes of the pointer-sized slot are ignored by the consumer and are allowed to take on any value); however
+ // to reduce the need for ever more #ifs in this file, this behavior will not be #if'd away. (Its not wrong, its just unnecessary)
+ //
+ switch (returnType.GetCorElementType())
+ {
+ case CorElementType.ELEMENT_TYPE_I1:
+ case CorElementType.ELEMENT_TYPE_I2:
+#if BIT64
+ case CorElementType.ELEMENT_TYPE_I4:
+#endif
+ callConversionOps.Add(new CallConversionOperation(CallConversionOperation.OpCode.RETURN_SIGNEXTENDED_INTEGER_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z, CallConversionInterpreter.ArgBlock, 0, checked((int)returnType.GetSize())));
+ break;
+
+ case CorElementType.ELEMENT_TYPE_U1:
+ case CorElementType.ELEMENT_TYPE_BOOLEAN:
+ case CorElementType.ELEMENT_TYPE_U2:
+ case CorElementType.ELEMENT_TYPE_CHAR:
+#if BIT64
+ case CorElementType.ELEMENT_TYPE_U4:
+#endif
+ callConversionOps.Add(new CallConversionOperation(CallConversionOperation.OpCode.RETURN_ZEROEXTENDED_INTEGER_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z, CallConversionInterpreter.ArgBlock, 0, checked((int)returnType.GetSize())));
+ break;
+
+ default:
+ callConversionOps.Add(new CallConversionOperation(CallConversionOperation.OpCode.RETURN_INTEGER_BYVALUE_FROM_LOCALBLOCK_X_POINTER_Y_OF_SIZE_Z, CallConversionInterpreter.ArgBlock, 0, checked((int)returnType.GetSize())));
+ break;
+ }
}
Debug.Assert(callConversionOps[0].Op == CallConversionOperation.OpCode.ALLOC_X_LOCALBLOCK_BYTES_FOR_BLOCK_Y);