using System; using CPUx86 = Cosmos.Assembler.x86; namespace Cosmos.IL2CPU.X86.IL { /// /// 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. /// [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(); } } } }