private static void PatchInvoke(BaseMethodCompiler methodCompiler, bool withReturn)
{
// check if instance is null (if so, it's a static call to the methodPointer)
MosaField methodPointerField = GetField(methodCompiler.Method.DeclaringType, "methodPointer");
int methodPointerOffset = methodCompiler.TypeLayout.GetFieldOffset(methodPointerField);
Operand methodPointerOffsetOperand = Operand.CreateConstant(methodCompiler.TypeSystem, methodPointerOffset);
MosaField instanceField = GetField(methodCompiler.Method.DeclaringType, "instance");
int instanceOffset = methodCompiler.TypeLayout.GetFieldOffset(instanceField);
Operand instanceOffsetOperand = Operand.CreateConstant(methodCompiler.TypeSystem, instanceOffset);
var size = methodCompiler.Architecture.NativeInstructionSize;
Context b0 = new Context(CreateMethodStructure(methodCompiler, false));
Context b1 = new Context(methodCompiler.BasicBlocks.CreateBlock());
Context b2 = new Context(methodCompiler.BasicBlocks.CreateBlock());
Context b3 = new Context(methodCompiler.BasicBlocks.CreateBlock());
Operand[] vrs = new Operand[methodCompiler.Parameters.Length];
for (int i = 0; i < methodCompiler.Parameters.Length; i++)
{
vrs[i] = methodCompiler.VirtualRegisters.Allocate(methodCompiler.Parameters[i].Type);
if (methodCompiler.TypeLayout.IsCompoundType(methodCompiler.Parameters[i].Type))
{
b0.AppendInstruction(IRInstruction.CompoundMove, vrs[i], methodCompiler.Parameters[i]);
}
else
{
b0.AppendInstruction(IRInstruction.Move, vrs[i], methodCompiler.Parameters[i]);
}
}
Operand thisOperand = vrs[0];
Operand opMethod = methodCompiler.VirtualRegisters.Allocate(methodCompiler.TypeSystem.BuiltIn.U4);
Operand opInstance = methodCompiler.VirtualRegisters.Allocate(thisOperand.Type);
Operand opCompare = methodCompiler.VirtualRegisters.Allocate(methodCompiler.TypeSystem.BuiltIn.I4);
Operand opReturn = withReturn ? methodCompiler.VirtualRegisters.Allocate(methodCompiler.TypeSystem.BuiltIn.Object) : null;
Operand c0 = Operand.CreateConstant(methodCompiler.TypeSystem, 0);
b0.AppendInstruction(IRInstruction.Load, size, opMethod, thisOperand, methodPointerOffsetOperand);
b0.AppendInstruction(IRInstruction.Load, size, opInstance, thisOperand, instanceOffsetOperand);
b0.AppendInstruction(IRInstruction.IntegerCompare, ConditionCode.Equal, opCompare, opInstance, c0);
b0.AppendInstruction(IRInstruction.IntegerCompareBranch, ConditionCode.Equal, null, opCompare, c0);
b0.AddBranchTarget(b2.Block);
b0.AppendInstruction(IRInstruction.Jmp, b1.Block);
// no instance
b1.AppendInstruction(IRInstruction.Call, opReturn, opMethod);
b1.InvokeMethod = methodCompiler.Method;
for (int i = 1; i < methodCompiler.Parameters.Length; i++)
{
b1.AddOperand(vrs[i]);
}
b1.AppendInstruction(IRInstruction.Jmp, b3.Block);
// instance
b2.AppendInstruction(IRInstruction.Call, opReturn, opMethod);
b2.InvokeMethod = methodCompiler.Method;
b2.AddOperand(opInstance);
for (int i = 1; i < methodCompiler.Parameters.Length; i++)
{
b2.AddOperand(vrs[i]);
}
b2.AppendInstruction(IRInstruction.Jmp, b3.Block);
// return
b3.AppendInstruction(IRInstruction.Return, methodCompiler.BasicBlocks.EpilogueBlock);
if (withReturn)
{
b3.SetOperand(0, opReturn);
}
}