private void UpdateUses(Context ctx, IDictionary <StackOperand, StackOperand> liveOut) { int index = 0; foreach (Operand op1 in ctx.Operands) { // Is this a stack operand? StackOperand op = op1 as StackOperand; if (op != null) { StackOperand ssa; // Determine the most recent version Debug.Assert(liveOut.TryGetValue(op, out ssa), "Stack operand not in live variable list."); ssa = liveOut[op]; // Replace the use with the most recent version ctx.SetOperand(index++, ssa); if (TRACING.TraceInfo) { Trace.WriteLine(String.Format("\\tUse {0} has been replaced with {1}", op, ssa)); } } } }
/// <summary> /// Replaces the operand. /// </summary> /// <param name="lr">The lr.</param> /// <param name="replacement">The replacement.</param> private void ReplaceOperand(LiveRange lr, RegisterOperand replacement) { int opIdx; // Iterate all definition sites first foreach (int index in lr.Op.Definitions.ToArray()) { Context def = new Context(InstructionSet, index); if (def.Offset == lr.Start) { opIdx = 0; foreach (Operand r in def.Results) { // Is this the operand? if (object.ReferenceEquals(r, lr.Op)) { def.SetResult(opIdx, replacement); } opIdx++; } break; } } // Iterate all use sites foreach (int index in lr.Op.Uses.ToArray()) { Context instr = new Context(InstructionSet, index); if (instr.Offset <= lr.Start) { // A use on instr.Offset == lr.Start is one from a previous definition!! } else if (instr.Offset <= lr.End) { opIdx = 0; foreach (Operand r in instr.Operands) { // Is this the operand? if (object.ReferenceEquals(r, lr.Op)) { instr.SetOperand(opIdx, replacement); } opIdx++; } } else { break; } } }
/// <summary> /// Assigns the operands from CIL stack. /// </summary> /// <param name="ctx">The context.</param> /// <param name="currentStack">The current stack.</param> private void AssignOperandsFromCILStack(Context ctx, Stack <Operand> currentStack) { for (int index = ctx.OperandCount - 1; index >= 0; --index) { if (ctx.GetOperand(index) != null) { continue; } ctx.SetOperand(index, currentStack.Pop()); } }
/// <summary> /// Assigns the operands from CIL stack. /// </summary> /// <param name="ctx">The context.</param> /// <param name="currentStack">The current stack.</param> private static void AssignOperandsFromCILStack(Context ctx, IList <Operand> currentStack) { for (int index = ctx.OperandCount - 1; index >= 0; --index) { if (ctx.GetOperand(index) == null) { Operand operand = currentStack[currentStack.Count - 1]; currentStack.RemoveAt(currentStack.Count - 1); ctx.SetOperand(index, operand); } } }
/// <summary> /// Replaces the instrinsic call site /// </summary> /// <param name="context">The context.</param> /// <param name="typeSystem">The type system.</param> public void ReplaceIntrinsicCall(Context context, ITypeSystem typeSystem) { SigType u4 = new SigType(Runtime.Metadata.CilElementType.U4); // Retrieve register context context.SetInstruction(CPUx86.Instruction.MovInstruction, new RegisterOperand(u4, GeneralPurposeRegister.EAX), new MemoryOperand(u4, GeneralPurposeRegister.ESP, new IntPtr(28))); // Restore registers (Note: EAX and EDX are NOT restored!) context.AppendInstruction(CPUx86.Instruction.MovInstruction, new RegisterOperand(u4, GeneralPurposeRegister.EDX), new MemoryOperand(u4, GeneralPurposeRegister.EAX, new IntPtr(28))); context.AppendInstruction(CPUx86.Instruction.MovInstruction, new RegisterOperand(u4, GeneralPurposeRegister.EBX), new MemoryOperand(u4, GeneralPurposeRegister.EAX, new IntPtr(4))); context.AppendInstruction(CPUx86.Instruction.MovInstruction, new RegisterOperand(u4, GeneralPurposeRegister.EDI), new MemoryOperand(u4, GeneralPurposeRegister.EAX, new IntPtr(20))); context.AppendInstruction(CPUx86.Instruction.MovInstruction, new RegisterOperand(u4, GeneralPurposeRegister.ESI), new MemoryOperand(u4, GeneralPurposeRegister.EAX, new IntPtr(16))); context.AppendInstruction(CPUx86.Instruction.MovInstruction, new RegisterOperand(u4, GeneralPurposeRegister.ESP), new MemoryOperand(u4, GeneralPurposeRegister.EAX, new IntPtr(32))); context.AppendInstruction(CPUx86.Instruction.MovInstruction, new RegisterOperand(u4, GeneralPurposeRegister.EBP), new MemoryOperand(u4, GeneralPurposeRegister.EAX, new IntPtr(24))); // Jmp to EIP (stored in EDX) context.AppendInstruction(CPUx86.Instruction.JmpInstruction); context.SetOperand(0, new RegisterOperand(u4, GeneralPurposeRegister.EDX)); }
/// <summary> /// Assigns registers to operands of the instruction. /// </summary> /// <param name="ctx">The context.</param> private void AssignRegisters(Context ctx) { // Retrieve the register constraints for the instruction IRegisterConstraint rc = null; // _architecture.GetRegisterConstraint(instr); // FIXME PG - pass context instead??? // Operand index int opIdx; if (rc == null && TRACING.TraceWarning) Trace.WriteLine(String.Format(@"Failed to get register constraints for instruction {0}!", ctx.Instruction.ToString(ctx))); // Only process the instruction, if we have constraints... if (rc != null) { /* FIXME: Spill used registers, if they are in use :( * It is not as simple as it sounds, as the register may be used by the instruction itself, * but dirty afterwards anyways. So we need to decide this at some later point depending on * the arg list. If the register is also a result reg, and they represent the same operand, * we may get away without spilling... Oo? */ //Register[] used = rc.GetRegistersUsed(); opIdx = 0; Context at = ctx.Clone(); foreach (Operand op in ctx.Operands) { // Only allocate registers, if we really have to. if (!rc.IsValidOperand(opIdx, op)) { // The register operand allocated RegisterOperand rop; // List of compatible registers Register[] regs = rc.GetRegistersForOperand(opIdx); Debug.Assert(null != regs, @"IRegisterConstraint.GetRegistersForOperand returned null."); Debug.Assert(0 != regs.Length, @"WTF IRegisterConstraint.GetRegistersForOperand returned zero-length array - what shall we pass then if no register/memory?"); // Is this operand in a register? rop = GetRegisterOfOperand(op); if (rop != null && Array.IndexOf(regs, rop.Register) == -1) { // Spill the register... SpillRegister(ctx, op, rop); rop = null; } // Attempt to allocate a free register if (rop == null) { rop = AllocateRegister(regs, op); if (rop != null) { // We need to place a load here... :( InsertMove(ctx, rop, op); } } // Still failed to get one? Spill! if (rop == null) { // Darn, need to spill one... Always use the first one. rop = SpillRegister(at, op.Type, regs); // We need to place a load here... :( InsertMove(at, rop, op); } // Assign the register Debug.Assert(rop != null, @"Whoa: Failed to allocate a register operand??"); AssignRegister(rop, op, at); ctx.SetOperand(opIdx, rop); // FIXME PG - opIdx? hmmm. is this phidata? } opIdx++; } opIdx = 0; foreach (Operand res in ctx.Results) { // FIXME: Check support first, spill if register is target and in use if (!rc.IsValidResult(opIdx, res)) { // Is this operand in a register? RegisterOperand rop = GetRegisterOfOperand(res); if (rop == null && !rc.IsValidResult(opIdx, res) && res.Uses.Count == 0) // Do not allocate: Not in a register, allows memory & has no uses... oO wtf? continue; // Retrieve compliant registers Register[] regs = rc.GetRegistersForResult(opIdx); // Do we already have a register? if (rop != null && Array.IndexOf(regs, rop.Register) == -1) { // Hmm, current register doesn't match, release it - since we're overwriting the operand, // we don't need to spill. This should be safe. _activeOperands[rop.Register.Index] = null; } // Allocate a register if (rop == null) rop = AllocateRegister(regs, res); // Do we need to spill? if (rop == null) { // Darn, need to spill one... rop = SpillRegister(at, res.Type, regs); // We don't need to place a load here, as we're defining the register... } // Assign the register Debug.Assert(null != rop, @"Whoa: Failed to allocate a register operand??"); AssignRegister(rop, res, at); ctx.SetResult(opIdx, rop); // FIXME PG - same } else { RegisterOperand rop = res as RegisterOperand; if (rop != null) SpillRegisterIfInUse(ctx, rop); } opIdx++; } } }
/// <summary> /// Assigns the operands from CIL stack. /// </summary> /// <param name="ctx">The context.</param> /// <param name="currentStack">The current stack.</param> private void AssignOperandsFromCILStack(Context ctx, Stack<Operand> currentStack) { for (int index = ctx.OperandCount - 1; index >= 0; --index) { if (ctx.GetOperand(index) != null) continue; ctx.SetOperand(index, currentStack.Pop()); } }
/// <summary> /// Assigns the operands from CIL stack. /// </summary> /// <param name="ctx">The context.</param> /// <param name="currentStack">The current stack.</param> private static void AssignOperandsFromCILStack(Context ctx, IList<Operand> currentStack) { for (int index = ctx.OperandCount - 1; index >= 0; --index) { if (ctx.GetOperand (index) == null) { Operand operand = currentStack[currentStack.Count - 1]; currentStack.RemoveAt (currentStack.Count - 1); ctx.SetOperand (index, operand); } } }
/// <summary> /// Replaces the operand. /// </summary> /// <param name="lr">The lr.</param> /// <param name="replacement">The replacement.</param> private void ReplaceOperand(LiveRange lr, RegisterOperand replacement) { int opIdx; // Iterate all definition sites first foreach (int index in lr.Op.Definitions.ToArray()) { Context def = new Context(InstructionSet, index); if (def.Offset == lr.Start) { opIdx = 0; foreach (Operand r in def.Results) { // Is this the operand? if (object.ReferenceEquals(r, lr.Op)) def.SetResult(opIdx, replacement); opIdx++; } break; } } // Iterate all use sites foreach (int index in lr.Op.Uses.ToArray()) { Context instr = new Context(InstructionSet, index); if (instr.Offset <= lr.Start) { // A use on instr.Offset == lr.Start is one From a previous definition!! } else if (instr.Offset <= lr.End) { opIdx = 0; foreach (Operand r in instr.Operands) { // Is this the operand? if (object.ReferenceEquals(r, lr.Op)) instr.SetOperand(opIdx, replacement); opIdx++; } } else { break; } } }
/// <summary> /// Replaces the instruction with an internal call. /// </summary> /// <param name="ctx">The transformation context.</param> /// <param name="internalCallTarget">The internal call target.</param> private void ReplaceWithInternalCall(Context ctx, VmCall internalCallTarget) { RuntimeType rt = RuntimeBase.Instance.TypeLoader.GetType(@"Mosa.Runtime.RuntimeBase"); RuntimeMethod callTarget = FindMethod(rt, internalCallTarget.ToString()); ctx.ReplaceInstructionOnly(IR.Instruction.CallInstruction); ctx.SetOperand(0, SymbolOperand.FromMethod(callTarget)); }
/// <summary> /// Processes a method call instruction. /// </summary> /// <param name="ctx">The transformation context.</param> /// <param name="destinationOperand">The operand, which holds the call destination.</param> /// <param name="resultOperand"></param> /// <param name="operands"></param> private void ProcessInvokeInstruction(Context ctx, Operand destinationOperand, Operand resultOperand, List<Operand> operands) { ctx.SetInstruction(Instruction.CallInstruction, (byte)(operands.Count + 1), (byte)(resultOperand != null ? 1 : 0)); ctx.SetResult(resultOperand); int operandIndex = 0; ctx.SetOperand(operandIndex++, destinationOperand); foreach (Operand op in operands) { ctx.SetOperand(operandIndex++, op); } }
/// <summary> /// Visitation function for <see cref="ICILVisitor.Newarr"/>. /// </summary> /// <param name="ctx">The context.</param> public void Newarr(Context ctx) { Operand thisReference = ctx.Result; Debug.Assert(thisReference != null, @"Newobj didn't specify class signature?"); SZArraySigType arrayType = (SZArraySigType)ctx.Result.Type; ClassSigType elementSigType = arrayType.ElementType as ClassSigType; RuntimeType elementType = RuntimeBase.Instance.TypeLoader.GetType(this.MethodCompiler.Method, this.MethodCompiler.Assembly, elementSigType.Token); Debug.Assert(elementType != null, @"Newarr didn't specify class signature?"); Operand lengthOperand = ctx.Operand1; int elementSize = elementType.Size; // HACK: If we can't determine the size now, assume 16 bytes per array element. if (elementSize == 0) { elementSize = 16; } ReplaceWithInternalCall(ctx, VmCall.AllocateArray); ctx.SetOperand(1, new ConstantOperand(BuiltInSigType.IntPtr, 0)); ctx.SetOperand(2, new ConstantOperand(BuiltInSigType.Int32, elementSize)); ctx.SetOperand(3, lengthOperand); ctx.OperandCount = 4; }
/// <summary> /// Assigns registers to operands of the instruction. /// </summary> /// <param name="ctx">The context.</param> private void AssignRegisters(Context ctx) { // Retrieve the register constraints for the instruction IRegisterConstraint rc = null; // _architecture.GetRegisterConstraint(instr); // FIXME PG - pass context instead??? // Operand index int opIdx; if (rc == null && TRACING.TraceWarning) { Trace.WriteLine(String.Format(@"Failed to get register constraints for instruction {0}!", ctx.Instruction.ToString(ctx))); } // Only process the instruction, if we have constraints... if (rc != null) { /* FIXME: Spill used registers, if they are in use :( * It is not as simple as it sounds, as the register may be used by the instruction itself, * but dirty afterwards anyways. So we need to decide this at some later point depending on * the arg list. If the register is also a result reg, and they represent the same operand, * we may get away without spilling... Oo? */ //Register[] used = rc.GetRegistersUsed(); opIdx = 0; Context at = ctx.Clone(); foreach (Operand op in ctx.Operands) { // Only allocate registers, if we really have to. if (!rc.IsValidOperand(opIdx, op)) { // The register operand allocated RegisterOperand rop; // List of compatible registers Register[] regs = rc.GetRegistersForOperand(opIdx); Debug.Assert(null != regs, @"IRegisterConstraint.GetRegistersForOperand returned null."); Debug.Assert(0 != regs.Length, @"WTF IRegisterConstraint.GetRegistersForOperand returned zero-length array - what shall we pass then if no register/memory?"); // Is this operand in a register? rop = GetRegisterOfOperand(op); if (rop != null && Array.IndexOf(regs, rop.Register) == -1) { // Spill the register... SpillRegister(ctx, op, rop); rop = null; } // Attempt to allocate a free register if (rop == null) { rop = AllocateRegister(regs, op); if (rop != null) { // We need to place a load here... :( InsertMove(ctx, rop, op); } } // Still failed to get one? Spill! if (rop == null) { // Darn, need to spill one... Always use the first one. rop = SpillRegister(at, op.Type, regs); // We need to place a load here... :( InsertMove(at, rop, op); } // Assign the register Debug.Assert(rop != null, @"Whoa: Failed to allocate a register operand??"); AssignRegister(rop, op, at); ctx.SetOperand(opIdx, rop); // FIXME PG - opIdx? hmmm. is this phidata? } opIdx++; } opIdx = 0; foreach (Operand res in ctx.Results) { // FIXME: Check support first, spill if register is target and in use if (!rc.IsValidResult(opIdx, res)) { // Is this operand in a register? RegisterOperand rop = GetRegisterOfOperand(res); if (rop == null && !rc.IsValidResult(opIdx, res) && res.Uses.Count == 0) { // Do not allocate: Not in a register, allows memory & has no uses... oO wtf? continue; } // Retrieve compliant registers Register[] regs = rc.GetRegistersForResult(opIdx); // Do we already have a register? if (rop != null && Array.IndexOf(regs, rop.Register) == -1) { // Hmm, current register doesn't match, release it - since we're overwriting the operand, // we don't need to spill. This should be safe. _activeOperands[rop.Register.Index] = null; } // Allocate a register if (rop == null) { rop = AllocateRegister(regs, res); } // Do we need to spill? if (rop == null) { // Darn, need to spill one... rop = SpillRegister(at, res.Type, regs); // We don't need to place a load here, as we're defining the register... } // Assign the register Debug.Assert(null != rop, @"Whoa: Failed to allocate a register operand??"); AssignRegister(rop, res, at); ctx.SetResult(opIdx, rop); // FIXME PG - same } else { RegisterOperand rop = res as RegisterOperand; if (rop != null) { SpillRegisterIfInUse(ctx, rop); } } opIdx++; } } }
/// <summary> /// Replaces the instruction with an internal call. /// </summary> /// <param name="context">The transformation context.</param> /// <param name="internalCallTarget">The internal call target.</param> private void ReplaceWithVmCall(Context context, VmCall internalCallTarget) { RuntimeType type = typeSystem.GetType(@"Mosa.Internal.Runtime"); Debug.Assert(type != null, "Cannot find Mosa.Internal.Runtime"); RuntimeMethod method = type.FindMethod(internalCallTarget.ToString()); Debug.Assert(method != null, "Cannot find method: " + internalCallTarget.ToString()); context.ReplaceInstructionOnly(IR.Instruction.CallInstruction); context.SetOperand(0, SymbolOperand.FromMethod(method)); }
/// <summary> /// Visitation function for Newarr instruction. /// </summary> /// <param name="context">The context.</param> public void Newarr(Context context) { Operand thisReference = context.Result; Debug.Assert(thisReference != null, @"Newobj didn't specify class signature?"); SZArraySigType arrayType = (SZArraySigType)context.Result.Type; ClassSigType elementSigType = arrayType.ElementType as ClassSigType; RuntimeType elementType = typeModule.GetType(elementSigType.Token); Debug.Assert(elementType != null, @"Newarr didn't specify class signature?"); Operand lengthOperand = context.Operand1; int elementSize = typeLayout.GetTypeSize(elementType); // HACK: If we can't determine the size now, assume 16 bytes per array element. if (elementSize == 0) { elementSize = 16; } ReplaceWithVmCall(context, VmCall.AllocateArray); context.SetOperand(1, new ConstantOperand(BuiltInSigType.IntPtr, 0)); context.SetOperand(2, new ConstantOperand(BuiltInSigType.Int32, elementSize)); context.SetOperand(3, lengthOperand); context.OperandCount = 4; }
/// <summary> /// Replaces the instruction with an internal call. /// </summary> /// <param name="context">The transformation context.</param> /// <param name="internalCallTarget">The internal call target.</param> private void ReplaceWithVmCall(Context context, VmCall internalCallTarget) { RuntimeType rt = typeSystem.GetType(@"Mosa.Vm.Runtime"); Debug.Assert(rt != null, "@rt / @callTarget=" + internalCallTarget.ToString()); RuntimeMethod callTarget = rt.FindMethod(internalCallTarget.ToString()); Debug.Assert(callTarget != null, "@callTarget=" + internalCallTarget.ToString()); context.ReplaceInstructionOnly(IR.Instruction.CallInstruction); context.SetOperand(0, SymbolOperand.FromMethod(callTarget)); }