using System; using XSharp.Compiler; using CPUx86 = Cosmos.Assembler.x86; using Cosmos.Assembler.x86; using static XSharp.Compiler.XSRegisters; using static Cosmos.Assembler.x86.SSE.ComparePseudoOpcodes; 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"; XS.Set(EAX, ESP, sourceIsIndirect: true); XS.Set(EBP, EAX, destinationDisplacement: -0xE0, destinationIsIndirect: true); XS.SSE2.ConvertSI2SD(XMM0, EBP, sourceDisplacement: -0xE0, sourceIsIndirect: true); XS.Set(ECX, 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... XS.ShiftRight(ECX, 31); /* * 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 */ XS.Compare(ECX, 0x00); XS.Jump(ConditionalTestEnum.Equal, LabelSign_Bit_Unset); XS.LiteralCode(@"addsd xmm0, [__xmm@41f0000000000000]"); XS.Label(LabelSign_Bit_Unset); // We have converted our value to double put it on ESP // expand stack, that moved data is valid stack XS.Sub(ESP, 4); XS.SSE2.MoveSD(ESP, XMM0, destinationIsIndirect: true); 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.RegistersEnum.ESP, DestinationReg = CPUx86.RegistersEnum.EAX, SourceIsIndirect = true }; XS.SSE.ConvertSI2SS(XSRegisters.XMM0, XSRegisters.EAX); XS.SSE.MoveSS(XSRegisters.ESP, XSRegisters.XMM0, destinationIsIndirect: true); 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) XS.Set(EAX, ESP, sourceIsIndirect: true, sourceDisplacement: 4); XS.FPU.IntLoad(ESP, isIndirect: true, size: RegisterSize.Long64); // Get its highest bit to check if the value was signed or unsigned //XS.ShiftRight(EAX, 31); //XS.Compare(EAX, 0x00); //XS.Jump(ConditionalTestEnum.Equal, LabelSign_Bit_Unset); XS.Test(EAX, EAX); XS.Jump(ConditionalTestEnum.NotSign, LabelSign_Bit_Unset); //XS.LiteralCode(@"fadd qword [__ulong2double_const3]"); XS.LiteralCode(@"fadd qword [0x5F800000]"); XS.Label(LabelSign_Bit_Unset); XS.FPU.FloatStoreAndPop(ESP, isIndirect: true, size: RegisterSize.Long64); 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(); } } } }