diff options
author | David Wrighton <davidwr@microsoft.com> | 2017-07-21 05:27:06 +0300 |
---|---|---|
committer | David Wrighton <davidwr@microsoft.com> | 2017-07-21 05:27:06 +0300 |
commit | b89039fa25ec5eecb17bf5a79a6fff40f73cfa80 (patch) | |
tree | bbd54e09d0e6a1c4c4d2c68681499dff4edcd744 /src/System.Private.TypeLoader | |
parent | df3930a848b7e2da303713b5fd96d8b09d0ff482 (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.cs | 202 | ||||
-rw-r--r-- | src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/CallInterceptor.cs | 195 |
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); |