736 lines
24 KiB
C#
736 lines
24 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection.Emit;
|
|
using dnlib.DotNet;
|
|
using dnlib.DotNet.Emit;
|
|
using dnlib.Utils;
|
|
using ExceptionHandler = dnlib.DotNet.Emit.ExceptionHandler;
|
|
|
|
|
|
namespace dnlib.IR {
|
|
|
|
public partial class TransformContext {
|
|
|
|
private readonly MethodDef _methodDef;
|
|
private readonly VariableSet _vs;
|
|
private readonly EvalStack _es;
|
|
private readonly ParameterList _parameters;
|
|
private readonly LocalList _locals;
|
|
private readonly ICorLibTypes _corLibTypes;
|
|
|
|
public TransformContext(MethodDef methodDef) {
|
|
_methodDef = methodDef;
|
|
_vs = new VariableSet(_methodDef.Module);
|
|
_es = new EvalStack();
|
|
_parameters = methodDef.Parameters;
|
|
_locals = methodDef.Body.Variables;
|
|
_corLibTypes = methodDef.Module.CorLibTypes;
|
|
|
|
_vs.InitParams(_parameters);
|
|
_vs.InitLocals(_locals);
|
|
}
|
|
|
|
public VariableInfo GetParam(Instruction inst) {
|
|
return _vs.GetParam(inst.GetParameterIndex());
|
|
}
|
|
|
|
public VariableInfo GetLocal(Instruction inst) {
|
|
return _vs.GetLocal(inst.GetLocal(_locals).Index);
|
|
}
|
|
|
|
private void AddInstruction(IRInstruction inst) {
|
|
//_methodDef.Body.Instructions.Add(inst);
|
|
_curIrbb.AddInstruction(inst);
|
|
}
|
|
|
|
private void AddLoad(VariableInfo src) {
|
|
var dst = _vs.CreateTempVar(src.type);
|
|
_es.Push(dst);
|
|
var ir = IRInstruction.Create(IRFamily.LoadOrSet, IROpCode.LoadOrSet, InstructionArgument.CreateVariable(dst), InstructionArgument.CreateVariable(src));
|
|
AddInstruction(ir);
|
|
}
|
|
|
|
private void AddLoadAddress(VariableInfo src) {
|
|
var dst = _vs.CreateTempVar(src.type);
|
|
_es.Push(dst);
|
|
var ir = IRInstruction.Create(IRFamily.LoadAddress, IROpCode.LoadAddress, InstructionArgument.CreateVariable(dst), InstructionArgument.CreateVariable(src));
|
|
AddInstruction(ir);
|
|
}
|
|
|
|
private void AddSet(VariableInfo dst, VariableInfo src) {
|
|
var ir = IRInstruction.Create(IRFamily.LoadOrSet, IROpCode.LoadOrSet, InstructionArgument.CreateVariable(dst), InstructionArgument.CreateVariable(src));
|
|
AddInstruction(ir);
|
|
}
|
|
|
|
private void AddSetFromTop(VariableInfo dst) {
|
|
AddSet(dst, _es.Pop());
|
|
}
|
|
|
|
private void AddLoadConst(TypedConst src) {
|
|
var dst = _vs.CreateConstVar(src);
|
|
_es.Push(dst);
|
|
var ir = IRInstruction.Create(IRFamily.LoadConstant, IROpCode.LoadConstant, InstructionArgument.CreateVariable(dst), InstructionArgument.CreateConst(src));
|
|
AddInstruction(ir);
|
|
}
|
|
|
|
//private void AddBranch(IROpCode opCode, int target, params VariableInfo[] parameters) {
|
|
// _es.Pop(parameters.Length);
|
|
// var ir = IRInstruction.Create(IRFamily.Branch, opCode, InstructionArgument.CreateBranchTarget(target), InstructionArgument.CreateMultiVariable(parameters));
|
|
// AddInstruction(ir);
|
|
//}
|
|
|
|
private void AddBranch(Instruction il, int paramCount) {
|
|
IRBasicBlock target = GetIRBasicBlock((Instruction)il.Operand);
|
|
_curIrbb.AddOutboundBasicBlock(target);
|
|
VariableInfo[] parameters = _es.PopWithValue(paramCount);
|
|
var ir = IRInstruction.Create(IRFamily.Branch, GetBranchIRCodeByILCode(il.OpCode.Code), target, InstructionArgument.CreateMultiVariable(parameters));
|
|
AddInstruction(ir);
|
|
_es.SaveInbound(target);
|
|
}
|
|
|
|
private void AddSwitch(Instruction il) {
|
|
var cases = (Instruction[])il.Operand;
|
|
var index = _es.Pop();
|
|
var targets = cases.Select(c => GetIRBasicBlock(c)).ToArray();
|
|
foreach (var target in targets) {
|
|
_curIrbb.AddOutboundBasicBlock(target);
|
|
_es.SaveInbound(target);
|
|
}
|
|
var ir = IRInstruction.Create(IRFamily.Branch, IROpCode.Switch, targets, InstructionArgument.CreateVariable(index));
|
|
AddInstruction(ir);
|
|
}
|
|
|
|
private IRBasicBlock GetIRBasicBlock(Instruction inst) {
|
|
BasicBlock bb = _bbs.GetBasicBlockByInst(inst);
|
|
return _il2irBb[bb];
|
|
}
|
|
|
|
|
|
struct MethodReturnTypeAndParams {
|
|
public TypeSig returnType;
|
|
public List<TypeSig> parameterTypes;
|
|
}
|
|
|
|
struct GenericInst {
|
|
public IList<TypeSig> typeGenArgs;
|
|
public IList<TypeSig> methodGenArgs;
|
|
}
|
|
|
|
private GenericInst ResolveGenericContenxt(IMethod method) {
|
|
IList<TypeSig> typeGenArgs = null;
|
|
IList<TypeSig> methodGenArgs = null;
|
|
if (method is MemberRef memberRef) {
|
|
var parent = memberRef.Class;
|
|
if (parent is TypeSpec) {
|
|
if (((TypeSpec)parent).TypeSig is GenericInstSig sig)
|
|
typeGenArgs = sig.GenericArguments;
|
|
}
|
|
}
|
|
else if (method is MethodSpec methodSpec) {
|
|
if (methodSpec.Method is MemberRef mr) {
|
|
var parent = mr.Class;
|
|
if (parent is TypeSpec) {
|
|
if (((TypeSpec)parent).TypeSig is GenericInstSig sig)
|
|
typeGenArgs = sig.GenericArguments;
|
|
}
|
|
}
|
|
methodGenArgs = methodSpec.GenericInstMethodSig?.GenericArguments;
|
|
}
|
|
return new GenericInst {
|
|
typeGenArgs = typeGenArgs,
|
|
methodGenArgs = methodGenArgs,
|
|
};
|
|
}
|
|
|
|
private GenericInst ResolveGenericContenxt(IField field) {
|
|
IList<TypeSig> typeGenArgs = null;
|
|
if (field is MemberRef memberRef) {
|
|
var parent = memberRef.Class;
|
|
if (parent is TypeSpec) {
|
|
if (((TypeSpec)parent).TypeSig is GenericInstSig sig)
|
|
typeGenArgs = sig.GenericArguments;
|
|
}
|
|
}
|
|
return new GenericInst {
|
|
typeGenArgs = typeGenArgs,
|
|
methodGenArgs = null,
|
|
};
|
|
}
|
|
|
|
MethodReturnTypeAndParams MakeMethodFinalSignature(MethodSig methodSig, IList<TypeSig> typeGenArgs, IList<TypeSig> methodGenArgs) {
|
|
var ga = new GenericArguments();
|
|
if (typeGenArgs is not null)
|
|
ga.PushTypeArgs(typeGenArgs);
|
|
if (methodGenArgs is not null)
|
|
ga.PushMethodArgs(methodGenArgs);
|
|
var returnType = ga.Resolve(methodSig.RetType);
|
|
var parameterTypes = methodSig.Params.Select(p => ga.Resolve(p)).ToList();
|
|
if (methodSig.HasThis)
|
|
parameterTypes.Insert(0, _corLibTypes.UInt32);
|
|
return new MethodReturnTypeAndParams {
|
|
returnType = returnType,
|
|
parameterTypes = parameterTypes,
|
|
};
|
|
}
|
|
|
|
TypeSig MakeFieldFinalType(TypeSig type, IList<TypeSig> typeGenArgs) {
|
|
var ga = new GenericArguments();
|
|
if (typeGenArgs is not null)
|
|
ga.PushTypeArgs(typeGenArgs);
|
|
var returnType = ga.Resolve(type);
|
|
return returnType;
|
|
}
|
|
|
|
TypeSig GetFieldType(IField field) {
|
|
var typeGenArgs = ResolveGenericContenxt(field).typeGenArgs;
|
|
return MakeFieldFinalType(field.FieldSig.Type, typeGenArgs);
|
|
}
|
|
|
|
|
|
private void AddCall(IROpCode opCode, IMethod method, PrefixData? prefixData) {
|
|
|
|
MethodSig methodSig = method.MethodSig;
|
|
|
|
var mgc = ResolveGenericContenxt(method);
|
|
var mrtp = MakeMethodFinalSignature(methodSig, mgc.typeGenArgs, mgc.methodGenArgs);
|
|
|
|
|
|
|
|
|
|
AddCall(opCode, method, mrtp.returnType, mrtp.parameterTypes.ToArray(), prefixData);
|
|
//int paramCount = method.MethodSig.Params.Count + (method.MethodSig.HasThis ? 1 : 0);
|
|
//VariableInfo[] parameters = _es.PopWithValue(paramCount);
|
|
//VariableInfo ret;
|
|
//if (method.MethodSig.RetType.ElementType == ElementType.Void) {
|
|
// ret = null;
|
|
//} else {
|
|
// ret = _vs.CreateTempVar(method.MethodSig.RetType);
|
|
// _es.Push(ret);
|
|
//}
|
|
//var ir = Instruction.Create(IRFamily.Call, IROpCode.Call, ret != null ? InstructionArgument.CreateVariable(ret) : null, InstructionArgument.CreateMultiVariable(parameters));
|
|
//AddInstruction(ir);
|
|
}
|
|
|
|
private void AddCall(IROpCode opCode, object token, TypeSig returnType, TypeSig[] parameterTypes, PrefixData? prefixData) {
|
|
int paramCount = parameterTypes?.Length ?? 0;
|
|
VariableInfo[] parameters = _es.PopWithValue(paramCount);
|
|
VariableInfo ret;
|
|
if (returnType.ElementType == ElementType.Void) {
|
|
ret = null;
|
|
}
|
|
else {
|
|
ret = _vs.CreateTempVar(returnType);
|
|
_es.Push(ret);
|
|
}
|
|
var ir = IRInstruction.Create(IRFamily.Call, opCode, prefixData, ret != null ? InstructionArgument.CreateVariable(ret) : null, InstructionArgument.CreateMultiVariable(parameters));
|
|
ir.inlineOperand = token;
|
|
AddInstruction(ir);
|
|
}
|
|
|
|
private void AddNewObj(IMethod methodInfo) {
|
|
MethodSig method = methodInfo.MethodSig;
|
|
int paramCount = method.Params.Count;
|
|
VariableInfo[] parameters = _es.PopWithValue(paramCount);
|
|
VariableInfo ret = _vs.CreateTempVar(method.RetType);
|
|
_es.Push(ret);
|
|
|
|
var ir = IRInstruction.Create(IRFamily.Call, IROpCode.NewObj, ret != null ? InstructionArgument.CreateVariable(ret) : null, InstructionArgument.CreateMultiVariable(parameters));
|
|
ir.inlineOperand = methodInfo;
|
|
AddInstruction(ir);
|
|
}
|
|
|
|
private TypeSig CalcEqualType(TypeSig type) {
|
|
if (type.IsByRef) {
|
|
return _corLibTypes.IntPtr;
|
|
}
|
|
switch (type.ElementType) {
|
|
case ElementType.Boolean:
|
|
case ElementType.Char:
|
|
case ElementType.I1:
|
|
case ElementType.U1:
|
|
case ElementType.I2:
|
|
case ElementType.U2:
|
|
case ElementType.I4:
|
|
case ElementType.U4: return _corLibTypes.Int32;
|
|
case ElementType.I8:
|
|
case ElementType.U8: return _corLibTypes.Int64;
|
|
case ElementType.R4: return _corLibTypes.Single;
|
|
case ElementType.R8: return _corLibTypes.Double;
|
|
case ElementType.I:
|
|
case ElementType.U:
|
|
case ElementType.Ptr:
|
|
case ElementType.FnPtr:
|
|
case ElementType.Object:
|
|
return _corLibTypes.IntPtr;
|
|
default: return type;
|
|
}
|
|
}
|
|
|
|
private TypeSig CalcConvertResultType(Code code) {
|
|
switch (code) {
|
|
case Code.Conv_I1:
|
|
case Code.Conv_I2:
|
|
case Code.Conv_I4:
|
|
case Code.Conv_U1:
|
|
case Code.Conv_U2:
|
|
case Code.Conv_U4:
|
|
case Code.Conv_Ovf_I1:
|
|
case Code.Conv_Ovf_I2:
|
|
case Code.Conv_Ovf_I4:
|
|
case Code.Conv_Ovf_U1:
|
|
case Code.Conv_Ovf_U2:
|
|
case Code.Conv_Ovf_U4:
|
|
case Code.Conv_Ovf_I1_Un:
|
|
case Code.Conv_Ovf_I2_Un:
|
|
case Code.Conv_Ovf_I4_Un:
|
|
case Code.Conv_Ovf_U1_Un:
|
|
case Code.Conv_Ovf_U2_Un:
|
|
case Code.Conv_Ovf_U4_Un: return _corLibTypes.Int32;
|
|
case Code.Conv_I8:
|
|
case Code.Conv_U8:
|
|
case Code.Conv_Ovf_I8:
|
|
case Code.Conv_Ovf_U8:
|
|
case Code.Conv_Ovf_I8_Un:
|
|
case Code.Conv_Ovf_U8_Un: return _corLibTypes.Int64;
|
|
case Code.Conv_I:
|
|
case Code.Conv_U:
|
|
case Code.Conv_Ovf_I:
|
|
case Code.Conv_Ovf_U:
|
|
case Code.Conv_Ovf_I_Un:
|
|
case Code.Conv_Ovf_U_Un: return _corLibTypes.IntPtr;
|
|
case Code.Conv_R4: return _corLibTypes.Single;
|
|
case Code.Conv_R8: return _corLibTypes.Double;
|
|
default: throw new NotSupportedException();
|
|
}
|
|
}
|
|
|
|
private TypeSig CalcArithOpResultType(TypeSig op1, TypeSig op2) {
|
|
|
|
// int32 + int32 = int32
|
|
// int32 + native int => native int
|
|
// x + int64 => int64
|
|
// enum + x => enum
|
|
var type1 = CalcEqualType(op1).ElementType;
|
|
var type2 = CalcEqualType(op2).ElementType;
|
|
|
|
if (type1 == ElementType.ValueType || type1 == ElementType.GenericInst) {
|
|
switch (type2) {
|
|
case ElementType.I4:
|
|
case ElementType.I8:
|
|
case ElementType.I: return op1;
|
|
default: throw new NotSupportedException();
|
|
}
|
|
}
|
|
else if (type2 == ElementType.ValueType || type2 == ElementType.GenericInst) {
|
|
switch (type1) {
|
|
case ElementType.I4:
|
|
case ElementType.I8:
|
|
case ElementType.I: return op2;
|
|
default: throw new NotSupportedException();
|
|
}
|
|
}
|
|
|
|
switch (type1) {
|
|
case ElementType.I4: {
|
|
switch (type2) {
|
|
case ElementType.I4: return _corLibTypes.Int32;
|
|
case ElementType.I8: return _corLibTypes.Int64;
|
|
case ElementType.I: return _corLibTypes.IntPtr;
|
|
default: throw new NotSupportedException();
|
|
}
|
|
}
|
|
case ElementType.I8: {
|
|
switch (type2) {
|
|
case ElementType.I4:
|
|
case ElementType.I8:
|
|
case ElementType.I: return _corLibTypes.Int64;
|
|
default: throw new NotSupportedException();
|
|
}
|
|
}
|
|
case ElementType.I: {
|
|
switch (type2) {
|
|
case ElementType.I4:
|
|
case ElementType.I: return _corLibTypes.IntPtr;
|
|
case ElementType.I8: return _corLibTypes.Int64;
|
|
default: throw new NotSupportedException();
|
|
}
|
|
}
|
|
case ElementType.R4: {
|
|
switch (type2) {
|
|
case ElementType.R4: return _corLibTypes.Single;
|
|
case ElementType.R8: return _corLibTypes.Double;
|
|
default: throw new NotSupportedException();
|
|
}
|
|
}
|
|
case ElementType.R8: {
|
|
switch (type2) {
|
|
case ElementType.R4:
|
|
case ElementType.R8: return _corLibTypes.Double;
|
|
default: throw new NotSupportedException();
|
|
}
|
|
}
|
|
default: throw new NotSupportedException();
|
|
}
|
|
}
|
|
|
|
private IROpCode GetArithIROpcodeByILCode(Code code) {
|
|
switch (code) {
|
|
case Code.Add: return IROpCode.Add;
|
|
case Code.Add_Ovf: return IROpCode.Add_Ovf;
|
|
case Code.Add_Ovf_Un: return IROpCode.Add_Ovf_Un;
|
|
case Code.Sub: return IROpCode.Sub;
|
|
case Code.Sub_Ovf: return IROpCode.Sub_Ovf;
|
|
case Code.Sub_Ovf_Un: return IROpCode.Sub_Ovf_Un;
|
|
case Code.Mul: return IROpCode.Mul;
|
|
case Code.Mul_Ovf: return IROpCode.Mul_Ovf;
|
|
case Code.Mul_Ovf_Un: return IROpCode.Mul_Ovf_Un;
|
|
case Code.Div: return IROpCode.Div;
|
|
case Code.Div_Un: return IROpCode.Div_Un;
|
|
case Code.Rem: return IROpCode.Rem;
|
|
case Code.Rem_Un: return IROpCode.Rem_Un;
|
|
case Code.And: return IROpCode.And;
|
|
case Code.Or: return IROpCode.Or;
|
|
case Code.Xor: return IROpCode.Xor;
|
|
case Code.Shl: return IROpCode.Shl;
|
|
case Code.Shr: return IROpCode.Shr;
|
|
case Code.Shr_Un: return IROpCode.Shr_Un;
|
|
default: throw new NotSupportedException();
|
|
}
|
|
}
|
|
|
|
private IROpCode GetConvertIROpcodeByILCode(Code code) {
|
|
switch (code) {
|
|
case Code.Conv_I1: return IROpCode.Conv_I1;
|
|
case Code.Conv_I2: return IROpCode.Conv_I2;
|
|
case Code.Conv_I4: return IROpCode.Conv_I4;
|
|
case Code.Conv_I8: return IROpCode.Conv_I8;
|
|
case Code.Conv_U1: return IROpCode.Conv_U1;
|
|
case Code.Conv_U2: return IROpCode.Conv_U2;
|
|
case Code.Conv_U4: return IROpCode.Conv_U4;
|
|
case Code.Conv_U8: return IROpCode.Conv_U8;
|
|
case Code.Conv_I: return IROpCode.Conv_I;
|
|
case Code.Conv_U: return IROpCode.Conv_U;
|
|
case Code.Conv_R4: return IROpCode.Conv_R4;
|
|
case Code.Conv_R8: return IROpCode.Conv_R8;
|
|
case Code.Conv_Ovf_I1: return IROpCode.Conv_Ovf_I1;
|
|
case Code.Conv_Ovf_I2: return IROpCode.Conv_Ovf_I2;
|
|
case Code.Conv_Ovf_I4: return IROpCode.Conv_Ovf_I4;
|
|
case Code.Conv_Ovf_I8: return IROpCode.Conv_Ovf_I8;
|
|
case Code.Conv_Ovf_U1: return IROpCode.Conv_Ovf_U1;
|
|
case Code.Conv_Ovf_U2: return IROpCode.Conv_Ovf_U2;
|
|
case Code.Conv_Ovf_U4: return IROpCode.Conv_Ovf_U4;
|
|
case Code.Conv_Ovf_U8: return IROpCode.Conv_Ovf_U8;
|
|
case Code.Conv_Ovf_I: return IROpCode.Conv_Ovf_I;
|
|
case Code.Conv_Ovf_U: return IROpCode.Conv_Ovf_U;
|
|
case Code.Conv_Ovf_I1_Un: return IROpCode.Conv_Ovf_I1_Un;
|
|
case Code.Conv_Ovf_I2_Un: return IROpCode.Conv_Ovf_I2_Un;
|
|
case Code.Conv_Ovf_I4_Un: return IROpCode.Conv_Ovf_I4_Un;
|
|
case Code.Conv_Ovf_I8_Un: return IROpCode.Conv_Ovf_I8_Un;
|
|
case Code.Conv_Ovf_U1_Un: return IROpCode.Conv_Ovf_U1_Un;
|
|
case Code.Conv_Ovf_U2_Un: return IROpCode.Conv_Ovf_U2_Un;
|
|
case Code.Conv_Ovf_U4_Un: return IROpCode.Conv_Ovf_U4_Un;
|
|
case Code.Conv_Ovf_U8_Un: return IROpCode.Conv_Ovf_U8_Un;
|
|
case Code.Conv_Ovf_I_Un: return IROpCode.Conv_Ovf_I_Un;
|
|
case Code.Conv_Ovf_U_Un: return IROpCode.Conv_Ovf_U_Un;
|
|
default: throw new NotSupportedException();
|
|
}
|
|
}
|
|
|
|
private IROpCode GetArrayIROpcodeByILCode(Code code) {
|
|
switch (code) {
|
|
case Code.Newarr: return IROpCode.Newarr;
|
|
case Code.Ldlen: return IROpCode.LdLen;
|
|
case Code.Ldelema: return IROpCode.Ldelema;
|
|
case Code.Ldelem_I1: return IROpCode.Ldelem_I1;
|
|
case Code.Ldelem_U1: return IROpCode.Ldelem_U1;
|
|
case Code.Ldelem_I2: return IROpCode.Ldelem_I2;
|
|
case Code.Ldelem_U2: return IROpCode.Ldelem_U2;
|
|
case Code.Ldelem_I4: return IROpCode.Ldelem_I4;
|
|
case Code.Ldelem_U4: return IROpCode.Ldelem_U4;
|
|
case Code.Ldelem_I8: return IROpCode.Ldelem_I8;
|
|
case Code.Ldelem_I: return IROpCode.Ldelem_I;
|
|
case Code.Ldelem_R4: return IROpCode.Ldelem_R4;
|
|
case Code.Ldelem_R8: return IROpCode.Ldelem_R8;
|
|
case Code.Ldelem_Ref: return IROpCode.Ldelem_Ref;
|
|
case Code.Stelem_I: return IROpCode.Stelem_I;
|
|
case Code.Stelem_I1: return IROpCode.Stelem_I1;
|
|
case Code.Stelem_I2: return IROpCode.Stelem_I2;
|
|
case Code.Stelem_I4: return IROpCode.Stelem_I4;
|
|
case Code.Stelem_I8: return IROpCode.Stelem_I8;
|
|
case Code.Stelem_R4: return IROpCode.Stelem_R4;
|
|
case Code.Stelem_R8: return IROpCode.Stelem_R8;
|
|
case Code.Stelem_Ref: return IROpCode.Stelem_Ref;
|
|
case Code.Ldelem: return IROpCode.Ldelem;
|
|
case Code.Stelem: return IROpCode.Stelem;
|
|
default: throw new NotSupportedException();
|
|
}
|
|
}
|
|
|
|
private IROpCode GetBranchIRCodeByILCode(Code code) {
|
|
switch (code) {
|
|
case Code.Brfalse:
|
|
case Code.Brfalse_S: return IROpCode.BranchFalse;
|
|
case Code.Brtrue:
|
|
case Code.Brtrue_S: return IROpCode.BranchTrue;
|
|
case Code.Br:
|
|
case Code.Br_S: return IROpCode.UnconditionBranch;
|
|
case Code.Beq:
|
|
case Code.Beq_S: return IROpCode.Beq;
|
|
case Code.Bge:
|
|
case Code.Bge_S: return IROpCode.Bge;
|
|
case Code.Bgt:
|
|
case Code.Bgt_S: return IROpCode.Bgt;
|
|
case Code.Ble:
|
|
case Code.Ble_S: return IROpCode.Ble;
|
|
case Code.Blt:
|
|
case Code.Blt_S: return IROpCode.Blt;
|
|
case Code.Bne_Un:
|
|
case Code.Bne_Un_S: return IROpCode.Bne_Un;
|
|
case Code.Bge_Un:
|
|
case Code.Bge_Un_S: return IROpCode.Bge_Un;
|
|
case Code.Bgt_Un:
|
|
case Code.Bgt_Un_S: return IROpCode.Bgt_Un;
|
|
case Code.Ble_Un:
|
|
case Code.Ble_Un_S: return IROpCode.Ble_Un;
|
|
case Code.Blt_Un:
|
|
case Code.Blt_Un_S: return IROpCode.Blt_Un;
|
|
default: throw new NotSupportedException($"{code}");
|
|
}
|
|
}
|
|
|
|
private IROpCode GetCompareIRCodeByILCode(Code code) {
|
|
switch (code) {
|
|
case Code.Ceq: return IROpCode.Ceq;
|
|
case Code.Cgt: return IROpCode.Cgt;
|
|
case Code.Cgt_Un: return IROpCode.Cgt_Un;
|
|
case Code.Clt: return IROpCode.Clt;
|
|
case Code.Clt_Un: return IROpCode.Clt_Un;
|
|
default: throw new NotSupportedException($"{code}");
|
|
}
|
|
}
|
|
|
|
private IROpCode GetUnaryIRCodeByILCode(Code code) {
|
|
switch (code) {
|
|
case Code.Neg: return IROpCode.Neg;
|
|
case Code.Not: return IROpCode.Not;
|
|
default: throw new NotSupportedException($"{code}");
|
|
}
|
|
}
|
|
|
|
private IROpCode GetCallIRCodeByILCode(Code code) {
|
|
switch (code) {
|
|
case Code.Call: return IROpCode.Call;
|
|
case Code.Calli: return IROpCode.CallI;
|
|
case Code.Callvirt: return IROpCode.CallVir;
|
|
default: throw new NotSupportedException($"{code}");
|
|
}
|
|
}
|
|
|
|
private IROpCode GetUnspecIRCodeByILCode(Code code) {
|
|
switch (code) {
|
|
case Code.Ldftn: return IROpCode.Ldftn;
|
|
case Code.Ldvirtftn: return IROpCode.Ldvirtftn;
|
|
default: throw new NotSupportedException($"{code}");
|
|
}
|
|
}
|
|
|
|
|
|
private readonly Queue<BasicBlock> _pendingBBs = new Queue<BasicBlock>();
|
|
|
|
|
|
private IRBasicBlock _curIrbb;
|
|
|
|
private MethodBasicBlocks _bbs;
|
|
|
|
private Dictionary<BasicBlock, IRBasicBlock> _il2irBb;
|
|
|
|
private List<IRBasicBlock> _irbbs;
|
|
|
|
|
|
private IRBasicBlock GetIRBasicBlockByIL(Instruction inst) {
|
|
return inst != null ? _il2irBb[_bbs.GetBasicBlockByInst(inst)] : null;
|
|
}
|
|
|
|
|
|
private void SetupExceptionHandlerStart(Instruction handlerStartInst) {
|
|
var bb = _bbs.GetBasicBlockByInst(handlerStartInst);
|
|
var irbb = _il2irBb[bb];
|
|
var exceptionObj = _vs.CreateTempVar(_corLibTypes.Object);
|
|
irbb.SetInboundVariable(exceptionObj);
|
|
irbb.AddInstruction(IRInstruction.Create(IRFamily.Exception, IROpCode.LoadExceptionObject, InstructionArgument.CreateVariable(exceptionObj)));
|
|
}
|
|
|
|
public IRMethodBody Transform() {
|
|
|
|
_bbs = MethodBasicBlocks.SplitBasicBlocks(_methodDef);
|
|
|
|
var visitedBBs = new HashSet<BasicBlock>();
|
|
|
|
_il2irBb = new Dictionary<BasicBlock, IRBasicBlock>();
|
|
_irbbs = new List<IRBasicBlock>();
|
|
IRBasicBlock lastIrbb = null;
|
|
foreach (var bb in _bbs.BasicBlocks) {
|
|
|
|
IRBasicBlock irbb = new IRBasicBlock { ilbb = bb };
|
|
if (lastIrbb != null) {
|
|
lastIrbb.nextIrbb = irbb;
|
|
}
|
|
_irbbs.Add(irbb);
|
|
_il2irBb[bb] = irbb;
|
|
|
|
lastIrbb = irbb;
|
|
}
|
|
|
|
// while enter catch or filter block, push current exception object to exception object stack.
|
|
// the first instruction push top exception object to the eval stack.
|
|
foreach (var exceptionHandler in _methodDef.Body.ExceptionHandlers) {
|
|
|
|
if (exceptionHandler.HandlerStart != null) {
|
|
SetupExceptionHandlerStart(exceptionHandler.HandlerStart);
|
|
}
|
|
if (exceptionHandler.FilterStart != null) {
|
|
SetupExceptionHandlerStart(exceptionHandler.FilterStart);
|
|
}
|
|
|
|
}
|
|
|
|
_curIrbb = _irbbs[0];
|
|
|
|
BasicBlock curBb;
|
|
for (int nextBasicBlockIndex = 0; ; ) {
|
|
while (true) {
|
|
if (_pendingBBs.Count > 0) {
|
|
curBb = _pendingBBs.Dequeue();
|
|
}
|
|
else {
|
|
if (nextBasicBlockIndex >= _bbs.BasicBlocks.Count) {
|
|
curBb = null;
|
|
break;
|
|
}
|
|
curBb = _bbs.BasicBlocks[nextBasicBlockIndex++];
|
|
}
|
|
if (!visitedBBs.Contains(curBb)) {
|
|
break;
|
|
}
|
|
}
|
|
if (curBb == null) {
|
|
break;
|
|
}
|
|
visitedBBs.Add(curBb);
|
|
TransformBasicBlock(curBb, _il2irBb[curBb]);
|
|
}
|
|
|
|
var varMapper = new Dictionary<VariableInfo, VariableInfo>();
|
|
MergeInboundOutboundVariables(_irbbs, varMapper);
|
|
MapVariableToMergedVariable(_irbbs, varMapper);
|
|
|
|
|
|
var irExs = new List<IRExceptionHandler>();
|
|
foreach (ExceptionHandler exceptionHandler in _methodDef.Body.ExceptionHandlers) {
|
|
var irex = new IRExceptionHandler {
|
|
TryStart = GetIRBasicBlockByIL(exceptionHandler.TryStart),
|
|
TryEnd = GetIRBasicBlockByIL(exceptionHandler.TryEnd),
|
|
FilterStart = GetIRBasicBlockByIL(exceptionHandler.FilterStart),
|
|
HandlerStart = GetIRBasicBlockByIL(exceptionHandler.HandlerStart),
|
|
HandlerEnd = GetIRBasicBlockByIL(exceptionHandler.HandlerEnd),
|
|
CatchType = exceptionHandler.CatchType,
|
|
HandlerType = exceptionHandler.HandlerType,
|
|
};
|
|
irExs.Add(irex);
|
|
}
|
|
|
|
var irMethod = new IRMethodBody(_methodDef, irExs, _vs, _irbbs);
|
|
irMethod.ApplyOptimizations();
|
|
return irMethod;
|
|
}
|
|
|
|
|
|
private void MapVariableToMergedVariable(List<IRBasicBlock> bbs, Dictionary<VariableInfo, VariableInfo> variableMapper) {
|
|
foreach (var bb in bbs) {
|
|
foreach (var inst in bb.Instructions) {
|
|
foreach (InstructionArgument arg in inst.args) {
|
|
if (arg is InstructionArgumentVariable varArg) {
|
|
if (variableMapper.TryGetValue(varArg.value, out var value)) {
|
|
varArg.value = value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
private class VariableGroup {
|
|
public readonly List<VariableInfo> variables = new List<VariableInfo>();
|
|
|
|
public void Add(VariableInfo variable) {
|
|
variables.Add(variable);
|
|
}
|
|
|
|
public void Merge(VariableGroup group, Dictionary<VariableInfo, VariableGroup> variableGroups) {
|
|
VariableGroup srcGroup;
|
|
VariableGroup dstGroup;
|
|
if (group.variables.Count < variables.Count) {
|
|
srcGroup = group;
|
|
dstGroup = this;
|
|
} else {
|
|
srcGroup = this;
|
|
dstGroup = group;
|
|
}
|
|
|
|
foreach (var variable in srcGroup.variables) {
|
|
dstGroup.variables.Add(variable);
|
|
variableGroups[variable] = dstGroup;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void MergeInboundOutboundVariables(List<IRBasicBlock> bbs, Dictionary<VariableInfo, VariableInfo> variableMapper) {
|
|
var visited = new HashSet<IRBasicBlock>();
|
|
|
|
var variableGroups = new Dictionary<VariableInfo, VariableGroup>();
|
|
foreach (var bb in bbs) {
|
|
MergeInboundOutboundVariables(bb, visited, variableGroups);
|
|
}
|
|
foreach (var e in variableGroups) {
|
|
variableMapper.Add(e.Key, e.Value.variables[0]);
|
|
}
|
|
}
|
|
|
|
private void MergeInboundOutboundVariables(IRBasicBlock bb, HashSet<IRBasicBlock> visited, Dictionary<VariableInfo, VariableGroup> variableGroups) {
|
|
if (!visited.Add(bb)) {
|
|
return;
|
|
}
|
|
|
|
var outboundVars = bb.OutboundVariables;
|
|
foreach (var ov in outboundVars) {
|
|
if (!variableGroups.ContainsKey(ov)) {
|
|
var group = new VariableGroup();
|
|
variableGroups.Add(ov, group);
|
|
group.Add(ov);
|
|
}
|
|
}
|
|
int outBoundVarCount = outboundVars.Count;
|
|
foreach (var target in bb.OutboundBasicBlocks) {
|
|
if (target.InboundVariables.Count != outBoundVarCount) {
|
|
throw new Exception($"method:{_methodDef} InboundVariables.Count != outboundVars.Count");
|
|
}
|
|
for (int i = 0; i < outBoundVarCount; i++) {
|
|
var ov = outboundVars[i];
|
|
var iv = target.InboundVariables[i];
|
|
var ovGroup = variableGroups[ov];
|
|
if (!variableGroups.TryGetValue(iv, out var ivGroup)) {
|
|
ovGroup.Add(iv);
|
|
} else if (ivGroup != ovGroup) {
|
|
ovGroup.Merge(ivGroup, variableGroups);
|
|
}
|
|
}
|
|
}
|
|
foreach (var target in bb.OutboundBasicBlocks) {
|
|
MergeInboundOutboundVariables(target, visited, variableGroups);
|
|
}
|
|
}
|
|
}
|
|
}
|