using System; using CPUx86 = Cosmos.Assembler.x86; namespace Cosmos.IL2CPU.X86.IL { /// /// Converts the value on top of the evaluation stack to float32. /// [Cosmos.IL2CPU.OpCode(ILOpCode.Code.Conv_R4)] public class Conv_R4 : ILOp { public Conv_R4(Cosmos.Assembler.Assembler aAsmblr) : base(aAsmblr) { } public override void Execute(MethodInfo aMethod, ILOpCode aOpCode) { var xSource = aOpCode.StackPopTypes[0]; var xSourceIsFloat = TypeIsFloat(xSource); var xSourceSize = SizeOfType(xSource); switch (xSourceSize) { case 1: case 2: { new CPUx86.SSE.ConvertSI2SS { DestinationReg = CPUx86.Registers.XMM0, SourceReg = CPUx86.Registers.ESP, SourceIsIndirect = true }; new CPUx86.SSE.MoveSS { DestinationReg = CPUx86.Registers.ESP, DestinationIsIndirect = true, SourceReg = CPUx86.Registers.XMM0 }; break; } case 4: { if (!xSourceIsFloat) { if (IsIntegerSigned(xSource)) { new CPUx86.SSE.ConvertSI2SS { DestinationReg = CPUx86.Registers.XMM0, SourceReg = CPUx86.Registers.ESP, SourceIsIndirect = true }; new CPUx86.SSE.MoveSS { DestinationReg = CPUx86.Registers.ESP, DestinationIsIndirect = true, SourceReg = CPUx86.Registers.XMM0 }; } else { #if false /* * x86 assembler generated by GCC 4.86 with SSE2 arch enforced: * uint2float: * * LCPI0_0: * .quad 4841369599423283200 # double 4503599627370496 * * push ebp * mov ebp, esp * sub esp, 8 * mov eax, dword ptr [ebp + 8] * mov dword ptr [ebp - 4], eax * movsd xmm0, qword ptr [.LCPI0_0] * movaps xmm1, xmm0 * movd xmm2, eax * por xmm2, xmm1 * subsd xmm2, xmm0 * cvtsd2ss xmm0, xmm2 * movss dword ptr [ebp - 8], xmm0 * ; Why moving the value on x87 registers after having avoided to touch them until now? Maybe is the x87 calling convention * ; but we follow the .NET calling convention so we should push the result directly onto the stack * fld dword ptr [ebp - 8] * add esp, 8 * pop ebp * ret * * Let's to write it in the Cosmos dialect... */ new CPUx86.Push { DestinationReg = CPUx86.Registers.EBP }; new CPUx86.Mov { DestinationReg = CPUx86.Registers.EBP, SourceReg = CPUx86.Registers.ESP }; new CPUx86.Sub { DestinationReg = CPUx86.Registers.ESP, SourceValue = 8 }; new CPUx86.Mov { DestinationReg = CPUx86.Registers.EAX, SourceReg = CPUx86.Registers.EBP + 8}; new CPUx86.Mov { DestinationReg = CPUx86.Registers.EBP - 4, SourceReg = CPUx86.Registers.EAX }; /* Magic number */ new CPUx86.SSE.MoveSD { DestinationReg = CPUx86.Registers.XMM0, SourceValue = 4841369599423283200 }; new CPUx86.SSE.MoveAPS { DestinationReg = CPUx86.Registers.XMM1, SourceReg = CPUx86.Registers.XMM0 }; new CPUx86.Mov { DestinationReg = CPUx86.Registers.XMM2, SourceReg = CPUx86.Registers.EAX }; new CPUx86.SSE.Por { DestinationReg = CPUx86.Registers.XMM2, SourceReg = CPUx86.Registers.XMM1 }; new CPUx86.SSE.SubSD { DestinationReg = CPUx86.Registers.XMM2, SourceReg = CPUx86.Registers.XMM0 }; new CPUx86.SSE.ConvertSD2SS { DestinationReg = CPUx86.Registers.XMM0, SourceReg = CPUx86.Registers.XMM2 }; new CPUx86.SSE.MoveSS { DestinationReg = CPUx86.Registers.EBP - 8, SourceReg = CPUx86.Registers.XMM0 }; //new CPUx86.Add { DestinationReg = CPUx86.Registers.ESP, SourceValue = 8 }; //new CPUx86.x87.FloatLoad { DestinationReg = CPUx86.Registers.EBP - 8, Size = 32 }; // Let's try to avoid the x87 fld instruction in EPB - 8 there is the result already! new CPUx86.Mov { DestinationReg = CPUx86.Registers.ESP, SourceReg = CPUx86.Registers.EBP - 8 }; //new CPUx86.Add { DestinationReg = CPUx86.Registers.ESP, SourceValue = 8 }; new CPUx86.Pop { DestinationReg = CPUx86.Registers.EBP }; #endif throw new NotImplementedException("Cosmos.IL2CPU.x86->IL->Conv_R4.cs->Conversion of UInt32 to Float is not yet implemented!"); } } break; } case 8: { if (xSourceIsFloat) { new CPUx86.SSE.ConvertSD2SS { SourceReg = CPUx86.Registers.ESP, DestinationReg = CPUx86.Registers.XMM0, SourceIsIndirect = true }; new CPUx86.SSE.MoveSS { SourceReg = CPUx86.Registers.XMM0, DestinationReg = CPUx86.Registers.ESP, DestinationIsIndirect = true }; } else { if (IsIntegerSigned(xSource)) { /* * Again there is no SSE instruction in x86 to do this conversion as we need a 64 Bit register to do this! So we are forced * to use the legacy x87 FPU to do this operation. In x64 the SSE instruction ConvertSIQ2SS should exist. */ new CPUx86.x87.IntLoad { DestinationReg = CPUx86.Registers.ESP, Size = 64, DestinationIsIndirect = true }; new CPUx86.x87.FloatStoreAndPop { DestinationReg = CPUx86.Registers.ESP, Size = 32, DestinationIsIndirect = true }; //throw new NotImplementedException("Cosmos.IL2CPU.x86->IL->Conv_R4.cs->Conversion of Int64 to Float is not yet implemented!"); } else { throw new NotImplementedException("Cosmos.IL2CPU.x86->IL->Conv_R4.cs->Conversion of UInt64 to Float is not yet implemented!"); } } // Why I need to do all this Pop / Pop / Pushing or I get stack corruption? The result in the stack as expected so? new CPUx86.Pop { DestinationReg = CPUx86.Registers.EAX }; new CPUx86.Pop { DestinationReg = CPUx86.Registers.ECX }; new CPUx86.Push { DestinationReg = CPUx86.Registers.EAX }; break; } default: //EmitNotImplementedException( Assembler, GetServiceProvider(), "Conv_U4: SourceSize " + xStackItem.Size + " not supported!", mCurLabel, mMethodInformation, mCurOffset, mNextLabel ); throw new NotImplementedException(); } } } }