using System; using System.Linq; using System.Reflection; using Indy.IL2CPU.Assembler; using Indy.IL2CPU.Assembler.X86; using CPU = Indy.IL2CPU.Assembler; using CPUx86 = Indy.IL2CPU.Assembler.X86; namespace Indy.IL2CPU.IL.X86 { public abstract class Op: IL.Op { private bool mNeedsExceptionPush; private bool mNeedsExceptionClear; private Type mCatchType; private string mNextInstructionLabel; private bool mNeedsTypeCheck = false; /// /// Emits code for checking a given address for null, and emits a "throw new NullRefException();" if so. /// /// /// public static void EmitCompareWithNull(Assembler.Assembler aAssembler, MethodInformation aMethodInfo, string aAddress, string aCurrentLabel, string aNextLabel, Action aEmitCleanupMethod, int aCurrentILOffset) { new CPUx86.Compare("dword " + aAddress, "0"); new CPUx86.JumpIfNotEquals(aNextLabel); Type xNullRefExcType = typeof(NullReferenceException); Newobj.Assemble(aAssembler, xNullRefExcType.GetConstructor(new Type[0]), Engine.RegisterType(xNullRefExcType), aCurrentLabel, aMethodInfo, aCurrentILOffset); aAssembler.StackContents.Pop(); new CPUx86.Move("[" + DataMember.GetStaticFieldName(CPU.Assembler.CurrentExceptionRef) + "]", "eax"); Engine.QueueMethod(CPU.Assembler.CurrentExceptionOccurredRef); new CPUx86.Call(Label.GenerateLabelName(CPU.Assembler.CurrentExceptionOccurredRef)); new CPUx86.Move("ecx", "3"); aEmitCleanupMethod(); Call.EmitExceptionLogic(aAssembler, aCurrentILOffset, aMethodInfo, aNextLabel, false, null); } public Op(ILReader aReader, MethodInformation aMethodInfo) : base(aReader, aMethodInfo) { if (aMethodInfo != null && aMethodInfo.CurrentHandler != null) { mNeedsExceptionPush = ((aMethodInfo.CurrentHandler.HandlerOffset > 0 && aMethodInfo.CurrentHandler.HandlerOffset == aReader.Position) || ((aMethodInfo.CurrentHandler.Flags & ExceptionHandlingClauseOptions.Filter) > 0 && aMethodInfo.CurrentHandler.FilterOffset > 0 && aMethodInfo.CurrentHandler.FilterOffset == aReader.Position)) && (aMethodInfo.CurrentHandler.Flags == ExceptionHandlingClauseOptions.Clause); // todo: add support for exception clear again bool mNeedsExceptionClear = false; //mNeedsExceptionClear = ((aMethodInfo.CurrentHandler.HandlerOffset + aMethodInfo.CurrentHandler.HandlerLength) == (aReader.Offset + 1)) || // ((aMethodInfo.CurrentHandler.FilterOffset+aMethodInfo.CurrentHandler.Filterle == (aReader.Offset + 1)); if (mNeedsExceptionPush && aMethodInfo.CurrentHandler.CatchType != null) { mCatchType = aMethodInfo.CurrentHandler.CatchType; } } if (mCatchType != null && mCatchType.FullName != "System.Exception") { var xHandler = (from item in aMethodInfo.Method.GetMethodBody().ExceptionHandlingClauses where item.TryOffset == aMethodInfo.CurrentHandler.TryOffset && item.TryLength == aMethodInfo.CurrentHandler.TryLength && item.HandlerOffset == aMethodInfo.CurrentHandler.HandlerOffset && item.Flags == ExceptionHandlingClauseOptions.Clause select item).FirstOrDefault(); if (xHandler != null) { mNextInstructionLabel = GetInstructionLabel(xHandler.HandlerOffset); } else { // Here we need to detect where to leave to when this catch clause doesnt' handle a specific exception, and is the last one throw new NotImplementedException("TODO: Implement exiting here!"); } } else { mCatchType = null; } if (mCatchType != null && aMethodInfo != null && aMethodInfo.CurrentHandler != null && aMethodInfo.CurrentHandler.HandlerOffset > 0) { mNeedsTypeCheck = aMethodInfo.CurrentHandler.HandlerOffset == aReader.NextPosition; } } public static void Ldarg(Assembler.Assembler aAssembler, MethodInformation.Argument aArg) { Ldarg(aAssembler, aArg, true); } public static void Ldarg(Assembler.Assembler aAssembler, MethodInformation.Argument aArg, bool aAddGCCode) { foreach (string xAddress in aArg.VirtualAddresses.Reverse()) { new Move(CPUx86.Registers.EAX, "[" + xAddress + "]"); new Push(CPUx86.Registers.EAX); } aAssembler.StackContents.Push(new StackContent(aArg.Size, aArg.ArgumentType)); if (!aAssembler.InMetalMode && aAddGCCode && aArg.IsReferenceType) { new CPUx86.Push(CPUx86.Registers.EAX); Engine.QueueMethod(GCImplementationRefs.IncRefCountRef); new CPUx86.Call(Label.GenerateLabelName(GCImplementationRefs.IncRefCountRef)); } } public static void Ldflda(Assembler.Assembler aAssembler, TypeInformation aType, TypeInformation.Field aField) { Ldflda(aAssembler, aType, aField, true); } public static void Ldflda(Assembler.Assembler aAssembler, TypeInformation aType, TypeInformation.Field aField, bool aDerefExternalAddress) { int aExtraOffset = 0; if (aType.NeedsGC && !aAssembler.InMetalMode) { aExtraOffset = 12; } new Popd(CPUx86.Registers.EAX); new CPUx86.Add(CPUx86.Registers.EAX, "0x" + (aField.Offset + aExtraOffset).ToString("X")); aAssembler.StackContents.Pop(); aAssembler.StackContents.Push(new StackContent(4, aField.FieldType)); if (aDerefExternalAddress && aField.IsExternalField) { new Pushd(CPUx86.Registers.AtEAX); } else { new Pushd(CPUx86.Registers.EAX); } } public static void Multiply(Assembler.Assembler aAssembler) { StackContent xStackContent = aAssembler.StackContents.Pop(); new CPUx86.Xor("edx", "edx"); if (xStackContent.IsFloat) { throw new Exception("Float support not yet supported!"); } else { if (xStackContent.Size > 4) { new CPUx86.Pop("eax"); new CPUx86.Add("esp", "4"); new CPUx86.Multiply("dword [esp]"); new CPUx86.Add("esp", "8"); new Pushd("0"); new Pushd("eax"); } else { new CPUx86.Pop("eax"); new CPUx86.Multiply("dword [esp]"); new CPUx86.Add("esp", "4"); new Pushd("eax"); } } } public static void Ldfld(Assembler.Assembler aAssembler, TypeInformation aType, string aFieldName) { Ldfld(aAssembler, aType, aType.Fields[aFieldName]); } public static void Ldfld(Assembler.Assembler aAssembler, TypeInformation aType, TypeInformation.Field aField) { Ldfld(aAssembler, aType, aField, true); } public static void Ldfld(Assembler.Assembler aAssembler, TypeInformation aType, TypeInformation.Field aField, bool aAddGCCode) { Ldfld(aAssembler, aType, aField, aAddGCCode, true); } public static void Ldfld(Assembler.Assembler aAssembler, TypeInformation aType, TypeInformation.Field aField, bool aAddGCCode, bool aDerefExternalField) { aAssembler.StackContents.Pop(); int aExtraOffset = 0; if (aType.NeedsGC && !aAssembler.InMetalMode) { aExtraOffset = 12; } new CPUx86.Pop("ecx"); new CPUx86.Add("ecx", "0x" + (aField.Offset + aExtraOffset).ToString("X")); if (aField.IsExternalField && aDerefExternalField) { new CPUx86.Move("ecx", "[ecx]"); } if (aField.Size >= 4) { for (int i = 1; i <= (aField.Size / 4); i++) { new CPUx86.Move("eax", "[ecx + 0x" + (aField.Size - (i * 4)).ToString("X") + "]"); new CPUx86.Pushd("eax"); } switch (aField.Size % 4) { case 1: { new CPUx86.Move("eax", "0"); new CPUx86.Move("al", "[ecx]"); new CPUx86.Push("eax"); break; } case 2: { new CPUx86.Move("eax", "0"); new CPUx86.Move("ax", "[ecx + 0x]"); new CPUx86.Push("eax"); break; } case 0: { break; } default: throw new Exception("Remainder size " + (aField.Size % 4) + " not supported!"); } } else { switch (aField.Size) { case 1: { new CPUx86.Move("eax", "0"); new CPUx86.Move("al", "[ecx]"); new CPUx86.Push("eax"); break; } case 2: { new CPUx86.Move("eax", "0"); new CPUx86.Move("ax", "[ecx]"); new CPUx86.Push("eax"); break; } case 0: { break; } default: throw new Exception("Remainder size " + (aField.Size) + " not supported!"); } } if (aAddGCCode && aField.NeedsGC && !aAssembler.InMetalMode) { new CPUx86.Pushd(Registers.AtESP); Engine.QueueMethod(GCImplementationRefs.IncRefCountRef); new CPUx86.Call(Label.GenerateLabelName(GCImplementationRefs.IncRefCountRef)); } aAssembler.StackContents.Push(new StackContent(aField.Size, aField.FieldType)); } public static void Stfld(Assembler.Assembler aAssembler, TypeInformation aType, TypeInformation.Field aField) { aAssembler.StackContents.Pop(); int xRoundedSize = aField.Size; if (xRoundedSize % 4 != 0) { xRoundedSize += 4 - (xRoundedSize % 4); } int aExtraOffset = 0; if (aType.NeedsGC && !aAssembler.InMetalMode) { aExtraOffset = 12; new CPUx86.Pushd("[esp + 4]"); //Ldfld(aAssembler, aType, aField, false); new CPUx86.Pop("eax"); new CPUx86.Pushd("[eax + " + (aField.Offset + aExtraOffset) + "]"); Engine.QueueMethod(GCImplementationRefs.DecRefCountRef); new CPUx86.Call(Label.GenerateLabelName(GCImplementationRefs.DecRefCountRef)); } new CPUx86.Move("ecx", "[esp + 0x" + xRoundedSize.ToString("X") + "]"); new CPUx86.Add("ecx", "0x" + (aField.Offset + aExtraOffset).ToString("X")); for (int i = 0; i < (aField.Size / 4); i++) { new CPUx86.Pop("eax"); new Move("dword [ecx + 0x" + (i * 4).ToString("X") + "]", "eax"); } switch (aField.Size % 4) { case 1: { new CPUx86.Pop("eax"); new Move("byte [ecx + " + ((aField.Size / 4) * 4) + "]", "al"); break; } case 2: { new CPUx86.Pop("eax"); new Move("word [ecx + " + ((aField.Size / 4) * 4) + "]", "ax"); break; } case 0: { break; } default: throw new Exception("Remainder size " + (aField.Size % 4) + " not supported!"); } if (aField.NeedsGC && !aAssembler.InMetalMode) { new CPUx86.Pushd(Registers.ECX); new CPUx86.Pushd(Registers.EAX); new CPUx86.Call(Label.GenerateLabelName(GCImplementationRefs.DecRefCountRef)); new CPUx86.Call(Label.GenerateLabelName(GCImplementationRefs.DecRefCountRef)); } new CPUx86.Add("esp", "4"); aAssembler.StackContents.Pop(); } public static void Add(Assembler.Assembler aAssembler) { StackContent xSize = aAssembler.StackContents.Pop(); if (xSize.IsFloat) { throw new Exception("Floats not yet supported!"); } if (xSize.Size > 8) { throw new Exception("Size '" + xSize.Size + "' not supported"); } if (xSize.Size > 4) { new CPUx86.Pop("eax"); new CPUx86.Pop("edx"); new CPUx86.Add("[esp]", "eax"); new CPUx86.AddWithCarry("[esp + 4]", "edx"); } else { new CPUx86.Pop("eax"); new CPUx86.Add("[esp]", "eax"); } } public static void Ldloc(Assembler.Assembler aAssembler, MethodInformation.Variable aLocal) { Ldloc(aAssembler, aLocal, true); } public static void Ldloc(Assembler.Assembler aAssembler, MethodInformation.Variable aLocal, bool aAddGCCode) { foreach (string s in aLocal.VirtualAddresses) { new CPUx86.Move("eax", "[" + s + "]"); new CPUx86.Push("eax"); } aAssembler.StackContents.Push(new StackContent(aLocal.Size, aLocal.VariableType)); if (!aAssembler.InMetalMode && aAddGCCode && aLocal.IsReferenceType) { new CPUx86.Push("eax"); Engine.QueueMethod(GCImplementationRefs.IncRefCountRef); new CPUx86.Call(Label.GenerateLabelName(GCImplementationRefs.IncRefCountRef)); } } protected override void AssembleHeader() { base.AssembleHeader(); new Comment("Next Instruction = " + mNextInstructionLabel); string xCurExceptionFieldName = DataMember.GetStaticFieldName(IL2CPU.Assembler.Assembler.CurrentExceptionRef); if (mNeedsTypeCheck) { // call VTablesImpl.IsInstance to see the actual instance name.. new CPUx86.Move("eax", "[" + xCurExceptionFieldName + "]"); new CPUx86.Pushd(Registers.AtEAX); new CPUx86.Pushd(Engine.RegisterType(mCatchType).ToString()); new CPUx86.Call(Label.GenerateLabelName(VTablesImplRefs.IsInstanceRef)); new CPUx86.Compare(Registers.EAX, "0"); new CPUx86.JumpIfEquals(mNextInstructionLabel); } if (mNeedsExceptionPush) { new CPUx86.Push("dword [" + xCurExceptionFieldName + "]"); Assembler.StackContents.Push(new StackContent(4, typeof(Exception))); } } } }