Cosmos/source/Cosmos.IL2CPU/IL/Conv_R_Un.cs
fanoI 49192ffa24 - Conv.r.un is now partially implemented: an uint can be converted to double, not working code for ulong to double conversion
- Added (not working) test for ulong to double conversion
- Added to Assembler.cs costants needed for floating point conversions
2016-06-21 23:03:37 +02:00

184 lines
No EOL
13 KiB
C#

using System;
using CPUx86 = Cosmos.Assembler.x86;
namespace Cosmos.IL2CPU.X86.IL
{
/// <summary>
/// Converts the unsigned integer value on top of the evaluation stack to F (native float) it can be double or some FPU extended precision Floating Point
/// type as the weird 80 bit float of x87). For now we assume it to be always equal to double.
/// </summary>
[Cosmos.IL2CPU.OpCode( ILOpCode.Code.Conv_R_Un )]
public class Conv_R_Un : ILOp
{
public Conv_R_Un( Cosmos.Assembler.Assembler aAsmblr )
: base( aAsmblr )
{
}
public override void Execute( MethodInfo aMethod, ILOpCode aOpCode )
{
var xValue = aOpCode.StackPopTypes[0];
var xValueIsFloat = TypeIsFloat(xValue);
var xValueSize = SizeOfType(xValue);
if (xValueSize > 8)
{
//EmitNotImplementedException( Assembler, aServiceProvider, "Size '" + xSize.Size + "' not supported (add)", aCurrentLabel, aCurrentMethodInfo, aCurrentOffset, aNextLabel );
throw new NotImplementedException();
}
//TODO if on stack a float it is first truncated, http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.conv_r_un.aspx
if (!xValueIsFloat)
{
switch (xValueSize)
{
case 1:
case 2:
case 4:
/*
* Code generated by C# / Visual Studio 2015
* mov eax,dword ptr [anInt]
* mov dword ptr [ebp-0E0h],eax
* cvtsi2sd xmm0,dword ptr [ebp-0E0h]
* mov ecx,dword ptr [ebp-0E0h]
* shr ecx,1Fh
* addsd xmm0,mmword ptr __xmm@41f00000000000000000000000000000 (01176B40h)[ecx*8]
* movsd mmword ptr [aDouble],xmm0 # This for now means to copy our converted double to ESP
*/
string BaseLabel = GetLabel(aMethod, aOpCode) + ".";
string LabelSign_Bit_Unset = BaseLabel + "LabelSign_Bit_Unset";
new CPUx86.Mov { DestinationReg = CPUx86.Registers.EAX, SourceReg = CPUx86.Registers.ESP, SourceIsIndirect = true };
new CPUx86.Mov { DestinationReg = CPUx86.Registers.EBP, SourceReg = CPUx86.Registers.EAX, DestinationDisplacement = -0xE0, DestinationIsIndirect = true };
new CPUx86.SSE.ConvertSI2SD { DestinationReg = CPUx86.Registers.XMM0, SourceReg = CPUx86.Registers.EBP, SourceDisplacement = -0xE0, SourceIsIndirect = true };
new CPUx86.Mov { DestinationReg = CPUx86.Registers.ECX, SourceReg = CPUx86.Registers.EBP, SourceDisplacement = -0xE0, SourceIsIndirect = true };
// OK now we put in ECX the last bit of our unsigned value, we call it "SIGN_BIT" but is a little improper...
new CPUx86.ShiftRight { DestinationReg = CPUx86.Registers.ECX, SourceValue = 0x1F };
/*
* if the 'SIGN_BIT' is 0 it means that our uint could have been placed in a normal int so ConvertSI2SD did already
* the right thing: we have finished
* if the value is 1 we need to do that addition with that weird constant to obtain the real value as double
*/
new CPUx86.Compare { DestinationReg = CPUx86.Registers.ECX, SourceValue = 0x00 };
new CPUx86.ConditionalJump { Condition = CPUx86.ConditionalTestEnum.Equal, DestinationLabel = LabelSign_Bit_Unset };
new Assembler.LiteralAssemblerCode(@"addsd xmm0, [__xmm@41f0000000000000]");
new Assembler.Label(LabelSign_Bit_Unset);
// We have converted our value to double put it on ESP
// expand stack, that moved data is valid stack
new CPUx86.Sub { DestinationReg = CPUx86.Registers.ESP, SourceValue = 4 };
new CPUx86.SSE.MoveSD { DestinationReg = CPUx86.Registers.ESP, SourceReg = CPUx86.Registers.XMM0, DestinationIsIndirect = true };
#if false
new Assembler.LiteralAssemblerCode(@"addsd xmm0, [__xmm@41f00000000000000000000000000000]");
new CPUx86.SSE.MoveSD { DestinationReg = CPUx86.Registers.ESP, SourceReg = CPUx86.Registers.XMM0, DestinationIsIndirect = true };
//new CPUx86.SSE.AddSD { DestinationReg = CPUx86.Registers.XMM0, SourceValue = 0x41f00000000000000000000000000000, SourceIsIndirect = true };
#endif
break;
#if false
/*
* This is the original code. It cannot work as ConvertSI2SS want a *signed* int as input but here we have an *unsigned* int
* indeed we get always 0!
*/
new CPUx86.Mov { SourceReg = CPUx86.Registers.ESP, DestinationReg = CPUx86.Registers.EAX, SourceIsIndirect = true };
new CPUx86.SSE.ConvertSI2SS { SourceReg = CPUx86.Registers.EAX, DestinationReg = CPUx86.Registers.XMM0 };
new CPUx86.SSE.MoveSS { SourceReg = CPUx86.Registers.XMM0, DestinationReg = CPUx86.Registers.ESP, DestinationIsIndirect = true };
// This will be the correct thing to do instead to return a random value but Il2CPU crashes!
//EmitNotImplementedException(Assembler, GetServiceProvider(), "Conv_I: SourceSize " + xSource + " not supported!", mCurLabel, mMethodInformation, mCurOffset, mNextLabel);
break;
#endif
case 8:
/*
* This code is generated by GCC 4.8.4 it works perfectly but in Cosmos I continue to get stack corruption (a bug?)
* sub esp, 20
* mov edx, DWORD PTR [esp+28]
* mov eax, DWORD PTR [esp+24]
* mov DWORD PTR [esp+4], edx
* test edx, edx
* mov DWORD PTR [esp], eax
* fild QWORD PTR [esp]
* js .L11
* fstp QWORD PTR [esp+8]
* // This is not needed as we need the Double onto the stack not on x87 registers!
* //fld QWORD PTR [esp+8]
* add esp, 20
* .L11:
* fadd DWORD PTR .LC1
* fstp QWORD PTR [esp+8]
* // This is not needed as we need the Double onto the stack not on x87 registers!
* //fld QWORD PTR [esp+8]
* add esp, 20
*/
BaseLabel = GetLabel(aMethod, aOpCode) + ".";
LabelSign_Bit_Unset = BaseLabel + "LabelSign_Bit_Unset";
#if false
//new CPUx86.Sub { DestinationReg = CPUx86.Registers.ESP, SourceValue = 20 };
//new CPUx86.Push { DestinationReg = CPUx86.Registers.EBP };
//new CPUx86.Push { DestinationReg = CPUx86.Registers.EDX };
//new CPUx86.Push { DestinationReg = CPUx86.Registers.EAX };
new CPUx86.Mov { DestinationReg = CPUx86.Registers.EBP, SourceReg = CPUx86.Registers.ESP };
new CPUx86.Sub { DestinationReg = CPUx86.Registers.ESP, SourceValue = 20 };
new CPUx86.Mov { DestinationReg = CPUx86.Registers.ESP, SourceReg = CPUx86.Registers.EBP };
new CPUx86.Mov { DestinationReg = CPUx86.Registers.EDX, SourceReg = CPUx86.Registers.ESP, SourceIsIndirect = true, SourceDisplacement = 28 };
new CPUx86.Mov { DestinationReg = CPUx86.Registers.EAX, SourceReg = CPUx86.Registers.ESP, SourceIsIndirect = true, SourceDisplacement = 24 };
new CPUx86.Mov { DestinationReg = CPUx86.Registers.ESP, SourceReg = CPUx86.Registers.EDX, DestinationIsIndirect = true, DestinationDisplacement = 4 };
new CPUx86.Test { DestinationReg = CPUx86.Registers.EDX, SourceReg = CPUx86.Registers.EDX };
new CPUx86.x87.IntLoad { DestinationReg = CPUx86.Registers.ESP, DestinationIsIndirect = true, Size = 64 };
new CPUx86.ConditionalJump { Condition = CPUx86.ConditionalTestEnum.NotSign, DestinationLabel = LabelSign_Bit_Unset };
/* If the value was signed (that is more than LONG_MAX) we need to adjust the double adding a "Magical Constant" */
new Assembler.LiteralAssemblerCode(@"fadd DWORD [__ulong2double_const]");
new Assembler.Label(LabelSign_Bit_Unset);
// We have converted our value to double put it on ESP
//new CPUx86.x87.FloatStoreAndPop { DestinationReg = CPUx86.Registers.ESP, DestinationIsIndirect = true, DestinationDisplacement = 8, Size = 64 };
//new CPUx86.Add { DestinationReg = CPUx86.Registers.ESP, SourceValue = };
new CPUx86.x87.FloatStoreAndPop { DestinationReg = CPUx86.Registers.ESP, DestinationIsIndirect = true, Size = 64 };
//new CPUx86.x87.FloatLoad { DestinationReg = CPUx86.Registers.ESP, Size = 64, DestinationIsIndirect = true, DestinationDisplacement = 8 };
//new CPUx86.Sub { DestinationReg = CPUx86.Registers.ESP, SourceValue = 20 };
new CPUx86.Add { DestinationReg = CPUx86.Registers.ESP, SourceValue = 20 };
//new CPUx86.Add { DestinationReg = CPUx86.Registers.ESP, SourceValue = 4 };
//new CPUx86.Pop { DestinationReg = CPUx86.Registers.EBP };
//new CPUx86.Pop { DestinationReg = CPUx86.Registers.EDX };
//new CPUx86.Pop { DestinationReg = CPUx86.Registers.EAX };
//new Assembler.LiteralAssemblerCode(@"ALIGN 32, db 0");
//new Assembler.LiteralAssemblerCode(@".p2align 3");
#endif
/* This is my try! It is really simple and it should work the only problem is what should be the value of the constant
* __ulong2double_const2?
*/
/*
* mov EAX, ESP + 4
* fild qword ptr [esp]
* shr EAX, 31
* cmp ESP, 0
* jpe LabelSign_Bit_Unset
* LabelSign_Bit_Unset:
* fadd dword ptr __ulong2double_const2
* fstp ESP
*/
// Save the high part of the ulong in EAX (we cannot move all of ESP as it has 64 bit size)
new CPUx86.Mov { DestinationReg = CPUx86.Registers.EAX, SourceReg = CPUx86.Registers.ESP, SourceIsIndirect = true, SourceDisplacement = 4 };
new CPUx86.x87.IntLoad { DestinationReg = CPUx86.Registers.ESP, DestinationIsIndirect = true, Size = 64 };
// Get its highest bit to check if the value was signed or unsigned
new CPUx86.ShiftRight { DestinationReg = CPUx86.Registers.EAX, SourceValue = 31 };
new CPUx86.Compare { DestinationReg = CPUx86.Registers.EAX, SourceValue = 0x00 };
new CPUx86.ConditionalJump { Condition = CPUx86.ConditionalTestEnum.Equal, DestinationLabel = LabelSign_Bit_Unset };
new Assembler.LiteralAssemblerCode(@"fadd qword [__ulong2double_const2]");
new Assembler.Label(LabelSign_Bit_Unset);
new CPUx86.x87.FloatStoreAndPop { DestinationReg = CPUx86.Registers.ESP, DestinationIsIndirect = true, Size = 64 };
break;
default:
//EmitNotImplementedException( Assembler, GetServiceProvider(), "Conv_I: SourceSize " + xSource + " not supported!", mCurLabel, mMethodInformation, mCurOffset, mNextLabel );
throw new NotImplementedException("Conv_R_Un with type " + xValue + " not supported!");
}
}
else
{
throw new NotImplementedException();
}
}
}
}