ILRuntime寄存器模式源码解析

看到ILRuntime介绍的时候,一直好奇寄存器模式到底是干什么的,十分迫切的看起了源码

这里只讲寄存器模式的代码如何运行,如果对il指令不太了解的可以上我上一篇

ILRuntime是通过加载dll运行的,也就是说代码不会被转成il2cpp

public void LoadAssembly(System.IO.Stream stream, System.IO.Stream symbol, ISymbolReaderProvider symbolReader)
{
    var module = ModuleDefinition.ReadModule(stream); //从MONO中加载模块

    if (symbolReader != null && symbol != null)
    {
        module.ReadSymbols(symbolReader.GetSymbolReader(module, symbol)); //加载符号表
    }

    if (module.HasTypes)
    {
        List<ILType> types = new List<ILType>();

        foreach (var t in module.GetTypes()) //获取所有此模块定义的类型
        {
            ILType type = new ILType(t, this);

            mapType[t.FullName] = type;
            mapTypeToken[type.GetHashCode()] = type;
            types.Add(type);
        }
    }
    ......
}

加载后,把所有类转成ILType,里面的方法转成ILMethod,CLR绑定转成CLRType,方法转成CLRMethod

internal void InitCodeBody(bool register)
{
    if (def.HasBody)
    {
        localVarCnt = def.Body.Variables.Count;
        Dictionary<Mono.Cecil.Cil.Instruction, int> addr = new Dictionary<Mono.Cecil.Cil.Instruction, int>();

        bool noRelease = false;
        if (register)
        {
            JITCompiler jit = new JITCompiler(appdomain, declaringType, this);
            bodyRegister = jit.Compile(out stackRegisterCnt, out jumptablesR, addr, out registerSymbols);
        }
        ......
    }
}

调用ShouldUseRegisterVM会初始化body,如果使用寄存器模式,就创建JITCompiler,生成寄存器指令

例如下面的C#源码

private static int TestA(int a,int b)
{
    int c = a + b;
    int d = c * 100;
    return d;
}

转成il指令是这样子

ILRuntime用寄存器指令表示,大大优化了指令的数量,而且只生成一次,第二次使用直接运行指令,效率大大提升

也是模拟了mono jit编译

 

那指令是如何转换的呢,就在JITCompiler里面的 

void Translate(CodeBasicBlock block, Instruction ins, short locVarRegStart, ref short baseRegIdx)

会把所有指令转换成OpCodeR

struct OpCodeR
{
    [FieldOffset(0)]
    public OpCodeREnum Code;
    [FieldOffset(4)]
    public short Register1;
    [FieldOffset(6)]
    public short Register2;
    [FieldOffset(8)]
    public short Register3;
    [FieldOffset(10)]
    public short Register4;
    [FieldOffset(8)]
    public int Operand;
    [FieldOffset(8)]
    public float OperandFloat;
    [FieldOffset(12)]
    public int Operand2;
    [FieldOffset(16)]
    public int Operand3;
    [FieldOffset(12)]
    public long OperandLong;
    [FieldOffset(12)]
    public double OperandDouble;
    [FieldOffset(20)]
    public int Operand4;
    .......
}

首先会为每条需要存储值的指令分配一个寄存器索引,

并且把这类指令Code改成OpCodes.OpCodeREnum.Move

case Code.Ldarg_0:
case Code.Ldarg_1:
case Code.Ldarg_2:
case Code.Ldarg_3:
    op.Code = OpCodes.OpCodeREnum.Move;
    op.Register1 = baseRegIdx++;
    op.Register2 = (short)(code.Code - (Code.Ldarg_0));
    break;
case Code.Add:
case Code.Mul:
    op.Register1 = (short)(baseRegIdx - 2); //explicit use dest register for optimization
    op.Register2 = (short)(baseRegIdx - 2);
    op.Register3 = (short)(baseRegIdx - 1);
    baseRegIdx--;
    break;
case Code.Stloc_0:
    op.Code = OpCodes.OpCodeREnum.Move;
    op.Register1 = locVarRegStart;
    op.Register2 = --baseRegIdx;
    break;
case Code.Ldloc_0:
    op.Code = OpCodes.OpCodeREnum.Move;
    op.Register1 = baseRegIdx++;
    op.Register2 = locVarRegStart;
    break;
case Code.Ldc_I4_S:
    op.Register1 = baseRegIdx++;
    op.Operand = (sbyte)token;
    break;
转换完后,大概是这样子,用寄存器r表示
            Optimizer.ForwardCopyPropagation(blocks, hasReturn, baseRegStart);
            Optimizer.BackwardsCopyPropagation(blocks, hasReturn, baseRegStart);
            Optimizer.ForwardCopyPropagation(blocks, hasReturn, baseRegStart);
            Optimizer.EliminateConstantLoad(blocks, hasReturn);

然后再通过优化剔除不需要的指令,比如int c = a + b;用堆栈的话就需要把a推入堆栈,b推入堆栈,然后相加,再把结果存储到c中。用寄存机模式,就不要把a和b推入堆栈了,调用函数的时候已经在分配的寄存器中,一条指令搞定了以前4条指令需要做的事情,效率大幅提升

如果觉得运行时生成指令比较卡顿,ILRuntime也提供了预热机制,可以在空闲的时候初始化或生成指令

 

现在我们对ILRuntime寄存器模式有所了解了,也不是真的用了硬件层面的寄存器,而是一种模拟

看完ILRuntime源码感觉受益匪浅,代码写的很漂亮,这里只讲了一部份的内容,如果有兴趣还是很推荐阅读下源码的

  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值