// this code is mostly copied from Newarr.cs in Il2CPU, just the code to find the size and length is different public override void AssembleNew(Assembler aAssembler, object aMethodInfo) { string xTypeID = ILOp.GetTypeIDLabel(typeof(Array)); MethodBase xCtor = typeof(Array).GetConstructors(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance)[0]; string xCtorName = LabelName.Get(xCtor); XS.Set(ECX, EBP, sourceDisplacement: 8); // size XS.Set(EDX, EBP, sourceDisplacement: 12); // length XS.Push(ECX); // size of element XS.Set(EAX, ECX); XS.Multiply(EDX); // total element size XS.Add(EAX, ObjectUtils.FieldDataOffset + 4); // total array size XS.Push(EAX); XS.Call(LabelName.Get(GCImplementationRefs.AllocNewObjectRef)); XS.Label(".AfterAlloc"); XS.Pop(EAX); XS.Pop(ESI); XS.Push(EAX); XS.Push(ESP, isIndirect: true); XS.Push(ESP, isIndirect: true); // it's on the stack 3 times now, once from the return value, twice from the pushes; XS.Pop(EAX); XS.Set(EBX, xTypeID, sourceIsIndirect: true); // array type id XS.Set(EAX, EBX, destinationIsIndirect: true); // array type id XS.Set(EAX, (uint)ObjectUtils.InstanceTypeEnum.Array, destinationDisplacement: 4, destinationIsIndirect: true); XS.Set(EAX, ESI, destinationDisplacement: 8, destinationIsIndirect: true); XS.Set(EAX, ECX); XS.Push(0); XS.Call(xCtorName); XS.Push(0); }
public static void Assemble(Assembler aAssembler, uint aElementSize, _MethodInfo aMethod, ILOpCode aOpCode, bool debugEnabled) { // stack == the new value // stack + 1 == the index // stack + 2 == the array DoNullReferenceCheck(aAssembler, debugEnabled, (int)(8 + Align(aElementSize, 4))); uint xStackSize = aElementSize; if (xStackSize % 4 != 0) { xStackSize += 4 - xStackSize % 4; } // calculate element offset into array memory (including header) XS.Set(XSRegisters.EAX, XSRegisters.ESP, sourceDisplacement: (int)xStackSize); // the index XS.Set(XSRegisters.EDX, aElementSize); XS.Multiply(XSRegisters.EDX); XS.Add(XSRegisters.EAX, ObjectUtils.FieldDataOffset + 4); XS.Set(XSRegisters.EDX, XSRegisters.ESP, sourceDisplacement: (int)xStackSize + 8); // the array XS.Add(XSRegisters.EDX, XSRegisters.EAX); XS.Push(XSRegisters.EDX); XS.Pop(XSRegisters.ECX); for (int i = (int)(aElementSize / 4) - 1; i >= 0; i -= 1) { new Comment(aAssembler, "Start 1 dword"); XS.Pop(XSRegisters.EBX); XS.Set(XSRegisters.ECX, XSRegisters.EBX, destinationIsIndirect: true); XS.Add(XSRegisters.ECX, 4); } switch (aElementSize % 4) { case 1: { new Comment(aAssembler, "Start 1 byte"); XS.Pop(XSRegisters.EBX); XS.Set(XSRegisters.ECX, XSRegisters.BL, destinationIsIndirect: true); break; } case 2: { new Comment(aAssembler, "Start 1 word"); XS.Pop(XSRegisters.EBX); XS.Set(XSRegisters.ECX, XSRegisters.BX, destinationIsIndirect: true); break; } case 0: { break; } default: throw new Exception("Remainder size " + (aElementSize % 4) + " not supported!"); } XS.Add(XSRegisters.ESP, 12); }
public static void InitializeArray(Array array, RuntimeFieldHandle fldHandle) { // Arguments: // Array aArray, RuntimeFieldHandle aFieldHandle XS.Set(XSRegisters.EDI, XSRegisters.EBP, sourceDisplacement: 0xC); // array XS.Set(EDI, EDI, sourceIsIndirect: true); XS.Set(XSRegisters.ESI, XSRegisters.EBP, sourceDisplacement: 8); // aFieldHandle XS.Add(XSRegisters.EDI, 8); XS.Push(EDI, isIndirect: true); XS.Add(XSRegisters.EDI, 4); XS.Set(EAX, EDI, sourceIsIndirect: true); XS.Multiply(ESP, isIndirect: true, size: RegisterSize.Int32); XS.Pop(XSRegisters.ECX); XS.Set(XSRegisters.ECX, XSRegisters.EAX); XS.Set(XSRegisters.EAX, 0); XS.Add(XSRegisters.EDI, 4); XS.Label(".StartLoop"); XS.Set(DL, ESI, sourceIsIndirect: true); XS.Set(EDI, DL, destinationIsIndirect: true); XS.Add(XSRegisters.EAX, 1); XS.Add(XSRegisters.ESI, 1); XS.Add(XSRegisters.EDI, 1); XS.Compare(XSRegisters.EAX, XSRegisters.ECX); XS.Jump(CPUx86.ConditionalTestEnum.Equal, ".EndLoop"); XS.Jump(".StartLoop"); XS.Label(".EndLoop"); }
public override void AssembleNew(Assembler aAssembler, object aMethodInfo) { // load element size into eax // load length into ebx // calculate entire size into eax and move to exc // load start into edi // clear ecx bytes starting at edi // load element size into eax XS.Set(EAX, EBP, sourceDisplacement: SourceArrayDisplacement); XS.Add(EAX, 8); XS.Set(EAX, EAX, sourceIsIndirect: true); // load length into ebx XS.Set(EBX, EBP, sourceDisplacement: SourceArrayDisplacement); XS.Add(EBX, 12); XS.Set(EBX, EBX, sourceIsIndirect: true); // calculate size in bytes and move to ecx XS.Multiply(EBX); XS.Set(ECX, EAX); // load start into esi XS.Set(EDI, EBP, sourceDisplacement: SourceArrayDisplacement); XS.Add(EDI, 16); // clear eax bytes starting at esi XS.Set(EAX, 0); XS.LiteralCode("rep stosb"); }
public static void Assemble(Cosmos.Assembler.Assembler aAssembler, OpType aOpType, uint aElementSize, bool debugEnabled) { XS.Comment("Arraytype: " + aOpType.StackPopTypes.Last().FullName); XS.Comment("Size: " + aElementSize); DoNullReferenceCheck(aAssembler, debugEnabled, 4); // calculate element offset into array memory (including header) XS.Pop(XSRegisters.EAX); XS.Set(XSRegisters.EDX, aElementSize); XS.Multiply(XSRegisters.EDX); XS.Add(XSRegisters.EAX, (uint)(ObjectImpl.FieldDataOffset + 4)); // pop the array now XS.Pop(XSRegisters.EDX); // translate it to actual memory XS.Set(XSRegisters.EDX, XSRegisters.EDX, sourceIsIndirect: true); if (aOpType.StackPopTypes.Last().GetElementType().IsClass) { XS.Set(XSRegisters.EDX, XSRegisters.EDX, sourceIsIndirect: true); } XS.Add(XSRegisters.EDX, XSRegisters.EAX); XS.Push(XSRegisters.EDX); }
public static void Assemble(XSharp.Assembler.Assembler aAssembler, OpType aOpType, uint aElementSize, bool debugEnabled, _MethodInfo aMethod, ILOpCode aOpCode) { XS.Comment("Arraytype: " + aOpType.StackPopTypes.Last().FullName); XS.Comment("Size: " + aElementSize); DoNullReferenceCheck(aAssembler, debugEnabled, 8); //Do check for index out of range var xBaseLabel = GetLabel(aMethod, aOpCode); var xNoIndexOutOfRangeExeptionLabel = xBaseLabel + "_NoIndexOutOfRangeException"; var xIndexOutOfRangeExeptionLabel = xBaseLabel + "_IndexOutOfRangeException"; XS.Pop(EBX); //get Position _, array, 0, index -> _, array, 0 XS.Push(ESP, true, 4); // _, array, 0 => _, array, 0, array XS.Push(ESP, true, 12); // _, array, 0, array => _, array, 0, array, 0 Ldlen.Assemble(aAssembler, debugEnabled, false); // _, array, 0, array, 0 -> _, array, 0, length XS.Pop(EAX); //Length of array _, array, 0, length -> _, array, 0 XS.Compare(EAX, EBX); XS.Jump(CPUx86.ConditionalTestEnum.LessThanOrEqualTo, xIndexOutOfRangeExeptionLabel); XS.Compare(EBX, 0); XS.Jump(CPUx86.ConditionalTestEnum.GreaterThanOrEqualTo, xNoIndexOutOfRangeExeptionLabel); XS.Label(xIndexOutOfRangeExeptionLabel); XS.Pop(EAX); XS.Pop(EAX); Call.DoExecute(aAssembler, aMethod, ExceptionHelperRefs.ThrowIndexOutOfRangeException, aOpCode, xNoIndexOutOfRangeExeptionLabel, debugEnabled); XS.Label(xNoIndexOutOfRangeExeptionLabel); XS.Push(EBX); //_, array, 0 -> _, array, 0, index // calculate element offset into array memory (including header) XS.Pop(EAX); XS.Set(EDX, aElementSize); XS.Multiply(EDX); XS.Add(EAX, (uint)(ObjectUtils.FieldDataOffset + 4)); // pop the array now XS.Add(ESP, 4); XS.Pop(EDX); XS.Add(EDX, EAX); XS.Push(EDX); }
public override void Execute(_MethodInfo aMethod, ILOpCode aOpCode) { var xType = (ILOpCodes.OpType)aOpCode; uint xSize = SizeOfType(xType.Value.GetElementType() ?? xType.Value); string xTypeID = GetTypeIDLabel(xType.Value.GetElementType() ?? xType.Value); MethodBase xCtor = typeof(Array).GetConstructors(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance)[0]; string xCtorName = LabelName.Get(xCtor); XS.Comment("Element Size = " + xSize); XS.Pop(EAX); // element count XS.Push(EAX); XS.Set(EDX, xSize); XS.Multiply(EDX); // total element size XS.Add(EAX, ObjectUtils.FieldDataOffset + 4); // total array size XS.Push(EAX); //XS.Push(0x4E3A44A9); //XS.LiteralCode("Call DebugStub_SendSimpleNumber"); //XS.Pop(EAX); //XS.Push(".AfterAlloc"); //XS.LiteralCode("Call DebugStub_SendSimpleNumber"); //XS.Pop(EAX); XS.Call(LabelName.Get(GCImplementationRefs.AllocNewObjectRef)); XS.Label(".AfterAlloc"); XS.Pop(EAX); // location //XS.LiteralCode("Call DebugStub_SendSimpleNumber"); XS.Pop(ESI); // element count XS.Push(EAX); XS.Push(ESP, isIndirect: true); XS.Push(ESP, isIndirect: true); // it's on the stack 3 times now, once from the return value, twice from the pushes; XS.Pop(EAX); XS.Set(EBX, xTypeID, sourceIsIndirect: true); // array type id XS.Set(EAX, EBX, destinationIsIndirect: true); // array type id XS.Set(EAX, (uint)ObjectUtils.InstanceTypeEnum.Array, destinationDisplacement: 4, destinationIsIndirect: true); XS.Set(EAX, ESI, destinationDisplacement: 8, destinationIsIndirect: true); // element count XS.Set(EAX, xSize, destinationDisplacement: 12, destinationIsIndirect: true); // element size XS.Push(0); XS.Call(xCtorName); XS.Push(0); }
public static void Assemble(Cosmos.Assembler.Assembler aAssembler, OpType aOpType, uint aElementSize, bool debugEnabled) { XS.Comment("Arraytype: " + aOpType.StackPopTypes.Last().FullName); XS.Comment("Size: " + aElementSize); DoNullReferenceCheck(aAssembler, debugEnabled, 8); // calculate element offset into array memory (including header) XS.Pop(EAX); XS.Set(EDX, aElementSize); XS.Multiply(EDX); XS.Add(EAX, (uint)(ObjectImpl.FieldDataOffset + 4)); // pop the array now XS.Add(ESP, 4); XS.Pop(EDX); XS.Add(EDX, EAX); XS.Push(EDX); }
/* void Copy(Array sourceArray, ebp + 0x1C * int sourceIndex, ebp + 0x18 * Array destinationArray, ebp + 0x14 * int destinationIndex, ebp + 0x10 * int length, ebp + 0xC * bool reliable); ebp + 0x8 */ public override void AssembleNew(Assembler.Assembler aAssembler, object aMethodInfo) { XS.Set(XSRegisters.EAX, XSRegisters.EBP, sourceDisplacement: SourceArrayDisplacement); XS.Set(XSRegisters.EAX, XSRegisters.EAX, sourceIsIndirect: true); // dereference memory handle to pointer XS.Push(XSRegisters.EAX); new CPUx86.Add { DestinationReg = CPUx86.RegistersEnum.ESP, DestinationIsIndirect = true, SourceValue = 12, Size = 32 }; // pointer is at the element size XS.Pop(XSRegisters.EAX); XS.Set(XSRegisters.EAX, XSRegisters.EAX, sourceIsIndirect: true); // element size XS.Set(XSRegisters.EBX, XSRegisters.EBP, sourceDisplacement: SourceIndexDisplacement); XS.Multiply(XSRegisters.EBX); XS.Add(XSRegisters.EAX, 16); XS.Set(XSRegisters.ESI, XSRegisters.EBP, sourceDisplacement: SourceArrayDisplacement); XS.Set(XSRegisters.ESI, XSRegisters.ESI, sourceIsIndirect: true); // dereference memory handle to pointer XS.Add(XSRegisters.ESI, XSRegisters.EAX); // source ptr XS.Set(XSRegisters.EDX, XSRegisters.EBP, sourceDisplacement: DestinationArrayDisplacement); XS.Set(XSRegisters.EDX, XSRegisters.EDX, sourceIsIndirect: true); // dereference memory handle to pointer XS.Push(XSRegisters.EDX); new CPUx86.Add { DestinationReg = CPUx86.RegistersEnum.ESP, DestinationIsIndirect = true, SourceValue = 12, Size = 32 }; // pointer is at element size XS.Pop(XSRegisters.EAX); XS.Set(XSRegisters.EAX, XSRegisters.EAX, sourceIsIndirect: true); // dereference handle to pointer XS.Set(XSRegisters.ECX, XSRegisters.EBP, sourceDisplacement: DestinationIndexDisplacement); XS.Multiply(XSRegisters.ECX); XS.Add(XSRegisters.EAX, 16); XS.Set(XSRegisters.EDI, XSRegisters.EBP, sourceDisplacement: DestinationArrayDisplacement); XS.Set(XSRegisters.EDI, XSRegisters.EDI, sourceIsIndirect: true); // dereference handle to pointer XS.Add(XSRegisters.EDI, XSRegisters.EAX); // calculate byte count to copy XS.Set(XSRegisters.EAX, XSRegisters.EBP, sourceDisplacement: DestinationArrayDisplacement); XS.Set(XSRegisters.EAX, XSRegisters.EAX, sourceIsIndirect: true); // dereference memory handle to pointer XS.Add(XSRegisters.EAX, 12); XS.Set(XSRegisters.EAX, XSRegisters.EAX, sourceIsIndirect: true); XS.Set(XSRegisters.EDX, XSRegisters.EBP, sourceDisplacement: LengthDisplacement); XS.Multiply(XSRegisters.EDX); XS.Set(XSRegisters.ECX, XSRegisters.EAX); new CPUx86.Movs { Size = 8, Prefixes = CPUx86.InstructionPrefixes.Repeat }; }
/* void Copy( * Array sourceArray, ebp + 36 * int sourceIndex, ebp + 28 * Array destinationArray, ebp + 24 * int destinationIndex, ebp + 16 * int length, ebp + 12 * bool reliable); ebp + 8 */ public override void AssembleNew(Assembler.Assembler aAssembler, object aMethodInfo) { XS.Comment("Source"); XS.Comment("Element size"); XS.Set(XSRegisters.EAX, XSRegisters.EBP, sourceDisplacement: SourceArrayDisplacement); XS.Add(XSRegisters.EAX, ObjectUtils.FieldDataOffset); XS.Set(XSRegisters.EAX, XSRegisters.EAX, sourceIsIndirect: true); // element size XS.Comment("Source ptr"); XS.Set(XSRegisters.EBX, XSRegisters.EBP, sourceDisplacement: SourceIndexDisplacement); XS.Multiply(XSRegisters.EBX); XS.Add(XSRegisters.EAX, ObjectUtils.FieldDataOffset + 4); // first element XS.Set(XSRegisters.ESI, XSRegisters.EBP, sourceDisplacement: SourceArrayDisplacement); XS.Add(XSRegisters.ESI, XSRegisters.EAX); // source ptr XS.Comment("Destination"); XS.Comment("Element size"); XS.Set(XSRegisters.EAX, XSRegisters.EBP, sourceDisplacement: DestinationArrayDisplacement); XS.Add(XSRegisters.EAX, ObjectUtils.FieldDataOffset); XS.Set(XSRegisters.EAX, XSRegisters.EAX, sourceIsIndirect: true); // element size XS.Comment("Destination ptr"); XS.Set(XSRegisters.ECX, XSRegisters.EBP, sourceDisplacement: DestinationIndexDisplacement); XS.Multiply(XSRegisters.ECX); XS.Add(XSRegisters.EAX, ObjectUtils.FieldDataOffset + 4); // first element XS.Set(XSRegisters.EDI, XSRegisters.EBP, sourceDisplacement: DestinationArrayDisplacement); XS.Add(XSRegisters.EDI, XSRegisters.EAX); // destination ptr XS.Comment("Copy byte count"); XS.Comment("Element size"); XS.Set(XSRegisters.EAX, XSRegisters.EBP, sourceDisplacement: DestinationArrayDisplacement); XS.Add(XSRegisters.EAX, ObjectUtils.FieldDataOffset); XS.Set(XSRegisters.EAX, XSRegisters.EAX, sourceIsIndirect: true); // element size XS.Comment("Count"); XS.Set(XSRegisters.EDX, XSRegisters.EBP, sourceDisplacement: LengthDisplacement); XS.Multiply(XSRegisters.EDX); XS.Set(XSRegisters.ECX, XSRegisters.EAX); new CPUx86.Movs { Size = 8, Prefixes = CPUx86.InstructionPrefixes.Repeat }; }
public static void Assemble(Assembler aAssembler, _MethodInfo aMethod, OpMethod xMethod, string currentLabel, Type objectType, MethodBase constructor) { // call cctor: if (aMethod != null) { var xCctor = (objectType.GetConstructors(BindingFlags.Static | BindingFlags.NonPublic) ?? Array.Empty <ConstructorInfo>()).SingleOrDefault(); if (xCctor != null) { XS.Call(LabelName.Get(xCctor)); EmitExceptionLogic(aAssembler, aMethod, xMethod, true, null, ".AfterCCTorExceptionCheck"); XS.Label(".AfterCCTorExceptionCheck"); } } if (objectType.IsValueType) { #region Valuetypes XS.Comment("ValueType"); XS.Comment("Type: " + objectType); /* * Current sitation on stack: * $ESP Arg * $ESP+.. other items * * What should happen: * + The stack should be increased to allow space to contain: * + .ctor arguments * + struct _pointer_ (ref to start of emptied space) * + empty space for struct * + arguments should be copied to the new place * + old place where arguments were should be cleared * + pointer should be set * + call .ctor */ // Size of return value - we need to make room for this on the stack. uint xStorageSize = Align(SizeOfType(objectType), 4); XS.Comment("StorageSize: " + xStorageSize); if (xStorageSize == 0) { throw new Exception("ValueType storage size cannot be 0."); } uint xArgSize = 0; var xParameterList = constructor.GetParameters(); foreach (var xParam in xParameterList) { xArgSize = xArgSize + Align(SizeOfType(xParam.ParameterType), 4); } XS.Comment("ArgSize: " + xArgSize); // set source of args copy XS.Set(ESI, ESP); // allocate space for struct XS.Sub(ESP, xStorageSize + 4); // set destination and count of args copy XS.Set(EDI, ESP); XS.Set(ECX, xArgSize / 4); // move the args to their new location new CPUx86.Movs { Size = 32, Prefixes = CPUx86.InstructionPrefixes.Repeat }; // set struct ptr XS.Set(EAX, ESP); XS.Add(EAX, xArgSize + 4); XS.Set(ESP, EAX, destinationDisplacement: (int)xArgSize); XS.Push(EAX); var xOpType = new OpType(xMethod.OpCode, xMethod.Position, xMethod.NextPosition, xMethod.Value.DeclaringType, xMethod.CurrentExceptionRegion); new Initobj(aAssembler).Execute(aMethod, xOpType); new Call(aAssembler).Execute(aMethod, xMethod); // Need to put these *after* the call because the Call pops the args from the stack // and we have mucked about on the stack, so this makes it right before the next // op. #endregion Valuetypes } else { // If not ValueType, then we need gc var xParams = constructor.GetParameters(); // array length + 8 bool xHasCalcSize = false; #region Special string handling // try calculating size: if (constructor.DeclaringType == typeof(string)) { if (xParams.Length == 1 && xParams[0].ParameterType == typeof(char[])) { xHasCalcSize = true; XS.Set(EAX, ESP, sourceDisplacement: 4, sourceIsIndirect: true); // address XS.Set(EAX, EAX, sourceDisplacement: 8, sourceIsIndirect: true); // element count XS.Set(EDX, 2); // element size XS.Multiply(EDX); XS.Push(EAX); } else if (xParams.Length == 3 && (xParams[0].ParameterType == typeof(char[]) || xParams[0].ParameterType == typeof(char *)) && xParams[1].ParameterType == typeof(int) && xParams[2].ParameterType == typeof(int)) { xHasCalcSize = true; XS.Set(EAX, ESP, sourceIsIndirect: true); XS.ShiftLeft(EAX, 1); XS.Push(EAX); } else if (xParams.Length == 2 && xParams[0].ParameterType == typeof(char) && xParams[1].ParameterType == typeof(int)) { xHasCalcSize = true; XS.Set(EAX, ESP, sourceIsIndirect: true); XS.ShiftLeft(EAX, 1); XS.Push(EAX); } /* * TODO see if something is needed in stack / register to make them really work */ else if (xParams.Length == 3 && (xParams[0].ParameterType == typeof(sbyte *) && xParams[1].ParameterType == typeof(int) && xParams[2].ParameterType == typeof(int))) { xHasCalcSize = true; XS.Push(ESP, isIndirect: true); } else if (xParams.Length == 1 && xParams[0].ParameterType == typeof(sbyte *)) { xHasCalcSize = true; /* xParams[0] contains a C / ASCII Z string the following ASM is de facto the C strlen() function */ var xSByteCountLabel = currentLabel + ".SByteCount"; XS.Set(EAX, ESP, sourceIsIndirect: true); XS.Or(ECX, 0xFFFFFFFF); XS.Label(xSByteCountLabel); XS.Increment(EAX); XS.Increment(ECX); XS.Compare(EAX, 0, destinationIsIndirect: true); XS.Jump(CPUx86.ConditionalTestEnum.NotEqual, xSByteCountLabel); XS.Push(ECX); } else { throw new NotImplementedException("In NewObj, a string ctor implementation is missing!"); } } #endregion Special string handling uint xMemSize = GetStorageSize(objectType); int xExtraSize = 12; // additional size for set values after alloc XS.Push((uint)(xMemSize + xExtraSize)); if (xHasCalcSize) { XS.Pop(EAX); XS.Add(ESP, EAX, destinationIsIndirect: true); } // todo: probably we want to check for exceptions after calling Alloc XS.Call(LabelName.Get(GCImplementationRefs.AllocNewObjectRef)); XS.Label(".AfterAlloc"); XS.Push(ESP, isIndirect: true); XS.Push(ESP, isIndirect: true); // it's on the stack now 3 times. Once from the Alloc return value, twice from the pushes // todo: use a cleaner approach here. this class shouldnt assemble the string string strTypeId = GetTypeIDLabel(constructor.DeclaringType); XS.Pop(EAX); XS.Set(EBX, strTypeId, sourceIsIndirect: true); XS.Set(EAX, EBX, destinationIsIndirect: true); XS.Set(EAX, (uint)ObjectUtils.InstanceTypeEnum.NormalObject, destinationDisplacement: 4, destinationIsIndirect: true, size: RegisterSize.Int32); XS.Set(EAX, xMemSize, destinationDisplacement: 8, destinationIsIndirect: true, size: RegisterSize.Int32); uint xSize = (uint)(from item in xParams let xQSize = Align(SizeOfType(item.ParameterType), 4) select(int) xQSize).Take(xParams.Length).Sum(); XS.Push(0); foreach (var xParam in xParams) { uint xParamSize = Align(SizeOfType(xParam.ParameterType), 4); XS.Comment($"Arg {xParam.Name}: {xParamSize}"); for (int i = 0; i < xParamSize; i += 4) { XS.Push(ESP, isIndirect: true, displacement: (int)(xSize + 8)); } } XS.Call(LabelName.Get(constructor)); // should the complete error handling happen by ILOp.EmitExceptionLogic? if (aMethod != null) { // todo: only happening for real methods now, not for ctor's ? XS.Test(ECX, 2); string xNoErrorLabel = currentLabel + ".NoError" + LabelName.LabelCount.ToString(); XS.Jump(CPUx86.ConditionalTestEnum.Equal, xNoErrorLabel); PushAlignedParameterSize(constructor); // an exception occurred, we need to cleanup the stack, and jump to the exit XS.Add(ESP, 4); new Comment(aAssembler, "[ Newobj.Execute cleanup end ]"); Jump_Exception(aMethod); XS.Label(xNoErrorLabel); } XS.Pop(EAX); PushAlignedParameterSize(constructor); XS.Push(EAX); XS.Push(0); } }
/* void Copy( * Array sourceArray, ebp + 36 * int sourceIndex, ebp + 28 * Array destinationArray, ebp + 24 * int destinationIndex, ebp + 16 * int length, ebp + 12 * bool reliable); ebp + 8 */ public override void AssembleNew(Assembler aAssembler, object aMethodInfo) { var xArrayCopyReverseLabel = "ArrayCopy_Reverse"; var xArrayCopyReverseLoopLabel = "ArrayCopy_Reverse_Loop"; var xArrayCopyEndLabel = "ArrayCopy_End"; XS.Comment("Source"); XS.Comment("Element size"); XS.Set(EAX, EBP, sourceDisplacement: SourceArrayDisplacement); XS.Add(EAX, ObjectUtils.FieldDataOffset); XS.Set(EAX, EAX, sourceIsIndirect: true); // element size XS.Comment("Source ptr"); XS.Set(EBX, EBP, sourceDisplacement: SourceIndexDisplacement); XS.Multiply(EBX); XS.Add(EAX, ObjectUtils.FieldDataOffset + 4); // first element XS.Set(ESI, EBP, sourceDisplacement: SourceArrayDisplacement); XS.Add(ESI, EAX); // source ptr XS.Comment("Destination"); XS.Comment("Element size"); XS.Set(EAX, EBP, sourceDisplacement: DestinationArrayDisplacement); XS.Add(EAX, ObjectUtils.FieldDataOffset); XS.Set(EAX, EAX, sourceIsIndirect: true); // element size XS.Comment("Destination ptr"); XS.Set(ECX, EBP, sourceDisplacement: DestinationIndexDisplacement); XS.Multiply(ECX); XS.Add(EAX, ObjectUtils.FieldDataOffset + 4); // first element XS.Set(EDI, EBP, sourceDisplacement: DestinationArrayDisplacement); XS.Add(EDI, EAX); // destination ptr XS.Compare(EDI, ESI); XS.Jump(ConditionalTestEnum.Equal, xArrayCopyEndLabel); XS.Comment("Copy byte count"); XS.Comment("Element size"); XS.Set(EAX, EBP, sourceDisplacement: DestinationArrayDisplacement); XS.Add(EAX, ObjectUtils.FieldDataOffset); XS.Set(EAX, EAX, sourceIsIndirect: true); // element size XS.Comment("Count"); XS.Set(EDX, EBP, sourceDisplacement: LengthDisplacement); // if length is 0, jump to end XS.Compare(EDX, 0); XS.Jump(ConditionalTestEnum.Equal, xArrayCopyEndLabel); XS.Multiply(EDX); XS.Set(ECX, EAX); XS.Compare(EDI, ESI); XS.Jump(ConditionalTestEnum.GreaterThan, xArrayCopyReverseLabel); new Movs { Size = 8, Prefixes = InstructionPrefixes.Repeat }; XS.Jump(xArrayCopyEndLabel); XS.Label(xArrayCopyReverseLabel); XS.Add(ESI, ECX); XS.Add(EDI, ECX); XS.Label(xArrayCopyReverseLoopLabel); XS.Decrement(ESI); XS.Decrement(EDI); XS.Decrement(ECX); XS.Set(AL, ESI, sourceIsIndirect: true); XS.Set(EDI, AL, destinationIsIndirect: true); XS.Compare(ECX, 0); XS.Jump(ConditionalTestEnum.NotEqual, xArrayCopyReverseLoopLabel); XS.Label(xArrayCopyEndLabel); }
public static void Assemble(Assembler aAssembler, uint aElementSize, bool isSigned, _MethodInfo aMethod, ILOpCode aOpCode, bool debugEnabled) { // stack = index // stack + 2 = array DoNullReferenceCheck(aAssembler, debugEnabled, 8); // calculate element offset into array memory (including header) XS.Pop(EAX); XS.Set(EDX, aElementSize); XS.Multiply(EDX); XS.Add(EAX, ObjectUtils.FieldDataOffset + 4); if (aElementSize > 4) { // we start copying the last bytes XS.Add(EAX, aElementSize - 4); } // pop the array now XS.Add(ESP, 4); XS.Pop(EDX); XS.Add(EDX, EAX); var xSizeLeft = aElementSize; while (xSizeLeft > 0) { var xCurrentStep = Math.Min(xSizeLeft, 4); if (xSizeLeft % 4 != 0) { xCurrentStep = xSizeLeft % 4; } xSizeLeft = xSizeLeft - xCurrentStep; switch (xCurrentStep) { case 1: if (isSigned) { XS.MoveSignExtend(ECX, EDX, sourceIsIndirect: true, size: RegisterSize.Byte8); } else { XS.MoveZeroExtend(ECX, EDX, sourceIsIndirect: true, size: RegisterSize.Byte8); } XS.Push(ECX); break; case 2: if (isSigned) { XS.MoveSignExtend(ECX, EDX, sourceIsIndirect: true, size: RegisterSize.Short16); } else { XS.MoveZeroExtend(ECX, EDX, sourceIsIndirect: true, size: RegisterSize.Short16); } XS.Push(ECX); break; case 4: // copy a full dword XS.Push(EDX, true); XS.Sub(EDX, 4); // move to previous 4 bytes break; //case 8: // new CPUx86.Push {DestinationReg = CPUx86.Registers.EDX, DestinationDisplacement = 4, DestinationIsIndirect = true}; // XS.Push(XSRegisters.EDX, isIndirect: true); // break; } } }
public static void Assemble(Cosmos.Assembler.Assembler aAssembler, uint aElementSize, bool isSigned, bool debugEnabled) { DoNullReferenceCheck(aAssembler, debugEnabled, 4); //if (aElementSize <= 0 || aElementSize > 8 || (aElementSize > 4 && aElementSize < 8)) //{ // throw new Exception("Unsupported size for Ldelem_Ref: " + aElementSize); //} XS.Pop(XSRegisters.EAX); XS.Set(XSRegisters.EDX, aElementSize); XS.Multiply(XSRegisters.EDX); XS.Add(XSRegisters.EAX, (ObjectImpl.FieldDataOffset + 4)); if (aElementSize > 4) { // we start copying the last bytes XS.Add(XSRegisters.EAX, aElementSize - 4); } // pop the array XS.Pop(XSRegisters.EDX); // convert to real memory address XS.Set(XSRegisters.EDX, XSRegisters.EDX, sourceIsIndirect: true); XS.Add(XSRegisters.EDX, XSRegisters.EAX); var xSizeLeft = aElementSize; while (xSizeLeft > 0) { var xCurrentStep = Math.Min(xSizeLeft, 4); if (xSizeLeft % 4 != 0) { xCurrentStep = xSizeLeft % 4; } xSizeLeft = xSizeLeft - xCurrentStep; switch (xCurrentStep) { case 1: if (isSigned) { new CPUx86.MoveSignExtend { DestinationReg = CPUx86.RegistersEnum.ECX, Size = 8, SourceReg = CPUx86.RegistersEnum.EDX, SourceIsIndirect = true }; } else { new CPUx86.MoveZeroExtend { DestinationReg = CPUx86.RegistersEnum.ECX, Size = 8, SourceReg = CPUx86.RegistersEnum.EDX, SourceIsIndirect = true }; } XS.Push(XSRegisters.ECX); break; case 2: if (isSigned) { new CPUx86.MoveSignExtend { DestinationReg = CPUx86.RegistersEnum.ECX, Size = 16, SourceReg = CPUx86.RegistersEnum.EDX, SourceIsIndirect = true }; } else { new CPUx86.MoveZeroExtend { DestinationReg = CPUx86.RegistersEnum.ECX, Size = 16, SourceReg = CPUx86.RegistersEnum.EDX, SourceIsIndirect = true }; } XS.Push(XSRegisters.ECX); break; case 4: // copy a full dword XS.Push(XSRegisters.EDX, isIndirect: true); XS.Sub(XSRegisters.EDX, 4); // move to previous 4 bytes break; //case 8: // new CPUx86.Push {DestinationReg = CPUx86.Registers.EDX, DestinationDisplacement = 4, DestinationIsIndirect = true}; // XS.Push(XSRegisters.EDX, isIndirect: true); // break; } } }
public static void Assemble(Assembler aAssembler, uint aElementSize, bool isSigned, _MethodInfo aMethod, ILOpCode aOpCode, bool debugEnabled) { // stack = index // stack + 2 = array DoNullReferenceCheck(aAssembler, debugEnabled, 8); var xBaseLabel = GetLabel(aMethod, aOpCode); var xNoIndexOutOfRangeExeptionLabel = xBaseLabel + "_NoIndexOutOfRangeException"; var xIndexOutOfRangeExeptionLabel = xBaseLabel + "_IndexOutOfRangeException"; XS.Pop(EBX); //get Position _, array, 0, index -> _, array, 0 XS.Push(ESP, true, 4); // _, array, 0 => _, array, 0, array XS.Push(ESP, true, 12); // _, array, 0, array => _, array, 0, array, 0 Ldlen.Assemble(aAssembler, debugEnabled, false); // _, array, 0, array, 0 -> _, array, 0, length XS.Pop(EAX); //Length of array _, array, 0, length -> _, array, 0 XS.Compare(EAX, EBX); XS.Jump(CPUx86.ConditionalTestEnum.LessThanOrEqualTo, xIndexOutOfRangeExeptionLabel); XS.Compare(EBX, 0); XS.Jump(CPUx86.ConditionalTestEnum.GreaterThanOrEqualTo, xNoIndexOutOfRangeExeptionLabel); XS.Label(xIndexOutOfRangeExeptionLabel); XS.Pop(EAX); XS.Pop(EAX); Call.DoExecute(aAssembler, aMethod, ExceptionHelperRefs.ThrowIndexOutOfRangeException, aOpCode, xNoIndexOutOfRangeExeptionLabel, debugEnabled); XS.Label(xNoIndexOutOfRangeExeptionLabel); XS.Push(EBX); //_, array, 0 -> _, array, 0, index // calculate element offset into array memory (including header) XS.Pop(EAX); XS.Set(EDX, aElementSize); XS.Multiply(EDX); XS.Add(EAX, ObjectUtils.FieldDataOffset + 4); if (aElementSize > 4) { // we start copying the last bytes XS.Add(EAX, aElementSize - 4); } // pop the array now XS.Add(ESP, 4); XS.Pop(EDX); XS.Add(EDX, EAX); var xSizeLeft = aElementSize; while (xSizeLeft > 0) { var xCurrentStep = Math.Min(xSizeLeft, 4); if (xSizeLeft % 4 != 0) { xCurrentStep = xSizeLeft % 4; } xSizeLeft = xSizeLeft - xCurrentStep; switch (xCurrentStep) { case 1: if (isSigned) { XS.MoveSignExtend(ECX, EDX, sourceIsIndirect: true, size: RegisterSize.Byte8); } else { XS.MoveZeroExtend(ECX, EDX, sourceIsIndirect: true, size: RegisterSize.Byte8); } XS.Push(ECX); break; case 2: if (isSigned) { XS.MoveSignExtend(ECX, EDX, sourceIsIndirect: true, size: RegisterSize.Short16); } else { XS.MoveZeroExtend(ECX, EDX, sourceIsIndirect: true, size: RegisterSize.Short16); } XS.Push(ECX); break; case 4: // copy a full dword XS.Push(EDX, true); XS.Sub(EDX, 4); // move to previous 4 bytes break; //case 8: // new CPUx86.Push {DestinationReg = CPUx86.Registers.EDX, DestinationDisplacement = 4, DestinationIsIndirect = true}; // XS.Push(XSRegisters.EDX, isIndirect: true); // break; } } }
public static void Assemble(Cosmos.Assembler.Assembler aAssembler, MethodInfo aMethod, OpMethod xMethod, string currentLabel, Type objectType, MethodBase constructor) { // call cctor: if (aMethod != null) { var xCctor = (objectType.GetConstructors(BindingFlags.Static | BindingFlags.NonPublic) ?? new ConstructorInfo[0]).SingleOrDefault(); if (xCctor != null) { XS.Call(LabelName.Get(xCctor)); ILOp.EmitExceptionLogic(aAssembler, aMethod, xMethod, true, null, ".AfterCCTorExceptionCheck"); XS.Label(".AfterCCTorExceptionCheck"); } } if (objectType.IsValueType) { #region Valuetypes XS.Comment("ValueType"); /* * Current sitation on stack: * $ESP Arg * $ESP+.. other items * * What should happen: * + The stack should be increased to allow space to contain: * + .ctor arguments * + struct _pointer_ (ref to start of emptied space) * + empty space for struct * + arguments should be copied to the new place * + old place where arguments were should be cleared * + pointer should be set * + call .ctor */ // Size of return value - we need to make room for this on the stack. uint xStorageSize = Align(SizeOfType(objectType), 4); XS.Comment("StorageSize: " + xStorageSize); if (xStorageSize == 0) { throw new Exception("ValueType storage size cannot be 0."); } //var xStorageSize = aCtorDeclTypeInfo.StorageSize; uint xArgSize = 0; var xParameterList = constructor.GetParameters(); foreach (var xParam in xParameterList) { xArgSize = xArgSize + Align(SizeOfType(xParam.ParameterType), 4); } XS.Comment("ArgSize: " + xArgSize); // Set ESP so we can push the struct ptr int xShift = (int)(xArgSize - xStorageSize); XS.Comment("Shift: " + xShift); if (xShift < 0) { XS.Sub(XSRegisters.ESP, (uint)Math.Abs(xShift)); } else if (xShift > 0) { XS.Add(XSRegisters.ESP, (uint)xShift); } // push struct ptr XS.Push(XSRegisters.ESP); // Shift args foreach (var xParam in xParameterList) { uint xArgSizeForThis = Align(SizeOfType(xParam.ParameterType), 4); for (int i = 1; i <= xArgSizeForThis / 4; i++) { new CPUx86.Push { DestinationReg = CPUx86.RegistersEnum.ESP, DestinationIsIndirect = true, DestinationDisplacement = (int)xStorageSize }; } } new Call(aAssembler).Execute(aMethod, xMethod); // Need to put these *after* the call because the Call pops the args from the stack // and we have mucked about on the stack, so this makes it right before the next // op. #endregion Valuetypes } else { // If not ValueType, then we need gc var xParams = constructor.GetParameters(); // array length + 8 bool xHasCalcSize = false; #region Special string handling // try calculating size: if (constructor.DeclaringType == typeof(string)) { if (xParams.Length == 1 && xParams[0].ParameterType == typeof(char[])) { xHasCalcSize = true; XS.Set(EAX, ESP, sourceIsIndirect: true); // EAX contains a memory handle now, lets dereference it to a pointer XS.Set(EAX, EAX, sourceIsIndirect: true); XS.Set(XSRegisters.EAX, XSRegisters.EAX, sourceDisplacement: 8); XS.Set(XSRegisters.EDX, 2); XS.Multiply(XSRegisters.EDX); XS.Push(XSRegisters.EAX); } else if (xParams.Length == 3 && (xParams[0].ParameterType == typeof(char[]) || xParams[0].ParameterType == typeof(char *)) && xParams[1].ParameterType == typeof(int) && xParams[2].ParameterType == typeof(int)) { xHasCalcSize = true; XS.Set(EAX, ESP, sourceIsIndirect: true); XS.ShiftLeft(XSRegisters.EAX, 1); XS.Push(XSRegisters.EAX); } else if (xParams.Length == 2 && xParams[0].ParameterType == typeof(char) && xParams[1].ParameterType == typeof(int)) { xHasCalcSize = true; XS.Set(EAX, ESP, sourceIsIndirect: true); XS.ShiftLeft(XSRegisters.EAX, 1); XS.Push(XSRegisters.EAX); } else { throw new NotImplementedException("In NewObj, a string ctor implementation is missing!"); } } #endregion Special string handling uint xMemSize = GetStorageSize(objectType); int xExtraSize = 12; // additional size for set values after alloc XS.Push((uint)(xMemSize + xExtraSize)); if (xHasCalcSize) { XS.Pop(XSRegisters.EAX); XS.Add(ESP, EAX, destinationIsIndirect: true); } // todo: probably we want to check for exceptions after calling Alloc XS.Call(LabelName.Get(GCImplementationRefs.AllocNewObjectRef)); XS.Label(".AfterAlloc"); XS.Push(ESP, isIndirect: true); XS.Push(ESP, isIndirect: true); // it's on the stack now 3 times. Once from the Alloc return value, twice from the pushes //? ?? uint xObjSize;// = 0; //int xGCFieldCount = ( from item in aCtorDeclTypeInfo.Fields.Values //where item.NeedsGC //select item ).Count(); //int xGCFieldCount = ( from item in aCtorDeclTypeInfo.Fields.Values //where item.NeedsGC //select item ).Count(); int xGCFieldCount = objectType.GetFields().Count(x => x.FieldType.IsValueType); // todo: use a cleaner approach here. this class shouldnt assemble the string string strTypeId = GetTypeIDLabel(constructor.DeclaringType); XS.Pop(XSRegisters.EAX); XS.Set(EAX, EAX, sourceIsIndirect: true); XS.Set(EBX, strTypeId, sourceIsIndirect: true); XS.Set(EAX, EBX, destinationIsIndirect: true); XS.Set(EAX, (uint)InstanceTypeEnum.NormalObject, destinationDisplacement: 4, size: RegisterSize.Int32); XS.Set(EAX, (uint)xGCFieldCount, destinationDisplacement: 8, size: RegisterSize.Int32); uint xSize = (uint)(from item in xParams let xQSize = Align(SizeOfType(item.ParameterType), 4) select(int) xQSize).Take(xParams.Length).Sum(); foreach (var xParam in xParams) { uint xParamSize = Align(SizeOfType(xParam.ParameterType), 4); new Comment(aAssembler, String.Format("Arg {0}: {1}", xParam.Name, xParamSize)); for (int i = 0; i < xParamSize; i += 4) { new CPUx86.Push { DestinationReg = CPUx86.RegistersEnum.ESP, DestinationIsIndirect = true, DestinationDisplacement = (int)(xSize + 4) }; } } XS.Call(LabelName.Get(constructor)); // should the complete error handling happen by ILOp.EmitExceptionLogic? if (aMethod != null) { // todo: only happening for real methods now, not for ctor's ? XS.Test(XSRegisters.ECX, 2); string xNoErrorLabel = currentLabel + ".NoError" + LabelName.LabelCount.ToString(); XS.Jump(CPUx86.ConditionalTestEnum.Equal, xNoErrorLabel); //for( int i = 1; i < aCtorMethodInfo.Arguments.Length; i++ ) //{ // new CPUx86.Add // { // DestinationReg = CPUx86.Registers.ESP, // SourceValue = ( aCtorMethodInfo.Arguments[ i ].Size % 4 == 0 // ? aCtorMethodInfo.Arguments[ i ].Size // : ( ( aCtorMethodInfo.Arguments[ i ].Size / 4 ) * 4 ) + 1 ) // }; //} PushAlignedParameterSize(constructor); // an exception occurred, we need to cleanup the stack, and jump to the exit XS.Add(XSRegisters.ESP, 4); //new Comment(aAssembler, "[ Newobj.Execute cleanup start count = " + aAssembler.Stack.Count.ToString() + " ]"); //foreach( var xStackInt in Assembler.Stack ) //{ // new CPUx86.Add { DestinationReg = CPUx86.Registers.ESP, SourceValue = ( uint )xStackInt.Size }; //} new Comment(aAssembler, "[ Newobj.Execute cleanup end ]"); Jump_Exception(aMethod); XS.Label(xNoErrorLabel); } XS.Pop(XSRegisters.EAX); //for( int i = 1; i < aCtorMethodInfo.Arguments.Length; i++ ) //{ // new CPUx86.Add // { // DestinationReg = CPUx86.Registers.ESP, // SourceValue = ( aCtorMethodInfo.Arguments[ i ].Size % 4 == 0 // ? aCtorMethodInfo.Arguments[ i ].Size // : ( ( aCtorMethodInfo.Arguments[ i ].Size / 4 ) * 4 ) + 1 ) // }; //} PushAlignedParameterSize(constructor); XS.Push(XSRegisters.EAX); } }
public static void DoExecute(uint xStackContentSize, bool xStackContentIsFloat, string aBaseLabel) { if (xStackContentSize > 4) { if (xStackContentIsFloat) { XS.SSE2.MoveSD(XMM0, ESP, sourceIsIndirect: true); XS.Add(ESP, 8); XS.SSE2.MoveSD(XMM1, ESP, sourceIsIndirect: true); XS.SSE2.MulSD(XMM1, XMM0); XS.SSE2.MoveSD(ESP, XMM1, destinationIsIndirect: true); } else { // div of both == LEFT_LOW * RIGHT_LOW + ((LEFT_LOW * RIGHT_HIGH + RIGHT_LOW * LEFT_HIGH) << 32) string Simple32Multiply = aBaseLabel + "Simple32Multiply"; string MoveReturnValue = aBaseLabel + "MoveReturnValue"; // right value // low // SourceReg = CPUx86.Registers.ESP, SourceIsIndirect = true // high // SourceReg = CPUx86.Registers.ESP, SourceIsIndirect = true, SourceDisplacement = 4 // left value // low // SourceReg = CPUx86.Registers.ESP, SourceIsIndirect = true, SourceDisplacement = 8 // high // SourceReg = CPUx86.Registers.ESP, SourceIsIndirect = true, SourceDisplacement = 12 // compair LEFT_HIGH, RIGHT_HIGH , on zero only simple multiply is used //mov RIGHT_HIGH to eax, is useable on Full 64 multiply XS.Set(EAX, ESP, sourceDisplacement: 4); XS.Or(EAX, ESP, sourceDisplacement: 12); XS.Jump(CPUx86.ConditionalTestEnum.Zero, Simple32Multiply); // Full 64 Multiply // copy again, or could change EAX //TODO is there an opcode that does OR without change EAX? XS.Set(EAX, ESP, sourceDisplacement: 4); // eax contains already RIGHT_HIGH // multiply with LEFT_LOW XS.Multiply(ESP, displacement: 8); // save result of LEFT_LOW * RIGHT_HIGH XS.Set(ECX, EAX); //mov RIGHT_LOW to eax XS.Set(EAX, ESP, sourceIsIndirect: true); // multiply with LEFT_HIGH XS.Multiply(ESP, displacement: 12); // add result of LEFT_LOW * RIGHT_HIGH + RIGHT_LOW + LEFT_HIGH XS.Add(ECX, EAX); //mov RIGHT_LOW to eax XS.Set(EAX, ESP, sourceIsIndirect: true); // multiply with LEFT_LOW XS.Multiply(ESP, displacement: 8); // add LEFT_LOW * RIGHT_HIGH + RIGHT_LOW + LEFT_HIGH to high dword of last result XS.Add(EDX, ECX); XS.Jump(MoveReturnValue); XS.Label(Simple32Multiply); //mov RIGHT_LOW to eax XS.Set(EAX, ESP, sourceIsIndirect: true); // multiply with LEFT_LOW XS.Multiply(ESP, displacement: 8); XS.Label(MoveReturnValue); // move high result to left high XS.Set(ESP, EDX, destinationDisplacement: 12); // move low result to left low XS.Set(ESP, EAX, destinationDisplacement: 8); // pop right 64 value XS.Add(ESP, 8); } } else { if (xStackContentIsFloat) { XS.SSE.MoveSS(XMM0, ESP, sourceIsIndirect: true); XS.Add(ESP, 4); XS.SSE.MoveSS(XMM1, ESP, sourceIsIndirect: true); XS.SSE.MulSS(XMM1, XMM0); XS.SSE.MoveSS(ESP, XMM1, destinationIsIndirect: true); } else { XS.Pop(EAX); XS.Multiply(ESP, isIndirect: true, size: RegisterSize.Int32); XS.Add(ESP, 4); XS.Push(EAX); } } }
public static void Assemble(Assembler aAssembler, uint aElementSize, _MethodInfo aMethod, ILOpCode aOpCode, bool debugEnabled) { DoNullReferenceCheck(aAssembler, debugEnabled, (int)(8 + Align(aElementSize, 4))); uint xStackSize = aElementSize; if (xStackSize % 4 != 0) { xStackSize += 4 - xStackSize % 4; } // Do index out of range check var xBaseLabel = GetLabel(aMethod, aOpCode); var xNoIndexOutOfRangeExeptionLabel = xBaseLabel + "_NoIndexOutOfRangeException"; var xIndexOutOfRangeExeptionLabel = xBaseLabel + "_IndexOutOfRangeException"; XS.Push(ESP, displacement: 4 + 4 + (int)xStackSize); // _, array, 0, index, value * n => _, array, 0, index, value * n, array XS.Push(0); // _, array, 0, index, value * n, array => _, array, 0, index, value * n, array, 0 Ldlen.Assemble(aAssembler, debugEnabled, false); // _, array, 0, index, value * n, array, 0 -> _, array, 0, index, value * n, length XS.Pop(EAX); //Length of array _, array, 0, index, value * n, length -> _, array, 0, index, value * n XS.Compare(EAX, ESP, sourceIsIndirect: true, sourceDisplacement: (int)xStackSize); XS.Jump(CPUx86.ConditionalTestEnum.LessThanOrEqualTo, xIndexOutOfRangeExeptionLabel); XS.Compare(EAX, 0); XS.Jump(CPUx86.ConditionalTestEnum.GreaterThanOrEqualTo, xNoIndexOutOfRangeExeptionLabel); XS.Label(xIndexOutOfRangeExeptionLabel); Call.DoExecute(aAssembler, aMethod, ExceptionHelperRefs.ThrowIndexOutOfRangeException, aOpCode, xNoIndexOutOfRangeExeptionLabel, debugEnabled); XS.Label(xNoIndexOutOfRangeExeptionLabel); // calculate element offset into array memory (including header) XS.Set(EAX, ESP, sourceDisplacement: (int)xStackSize); // the index XS.Set(EDX, aElementSize); XS.Multiply(EDX); XS.Add(EAX, ObjectUtils.FieldDataOffset + 4); XS.Set(EDX, ESP, sourceDisplacement: (int)xStackSize + 8); // the array XS.Add(EDX, EAX); XS.Push(EDX); XS.Pop(ECX); for (int i = (int)(aElementSize / 4) - 1; i >= 0; i -= 1) { new Comment(aAssembler, "Start 1 dword"); XS.Pop(EBX); XS.Set(ECX, EBX, destinationIsIndirect: true); XS.Add(ECX, 4); } switch (aElementSize % 4) { case 1: { new Comment(aAssembler, "Start 1 byte"); XS.Pop(EBX); XS.Set(ECX, BL, destinationIsIndirect: true); break; } case 2: { new Comment(aAssembler, "Start 1 word"); XS.Pop(EBX); XS.Set(ECX, BX, destinationIsIndirect: true); break; } case 0: { break; } default: throw new Exception("Remainder size " + (aElementSize % 4) + " not supported!"); } XS.Add(ESP, 12); }