[八卦] LLILC项目貌似挂了…

刚跟同事聊天的时候听说原来我们已经好一段时间没有跟LLILC项目组的人开过定期会议了,而且近期我们在LLVM邮件列表上试着推的GC指针功能,LLILC项目的人也没人来参与讨论…

关于LLILC是什么,请跳传送门:如何看待微软LLILC,一个新的基于LLVM的CoreCLR JIT/CoreRT AOT编译器?

Andy Ayers大大会不会抓狂了,Phoenix和LLILC都挂了的话…

============================================

LLILC项目的开发过程上可能也有些隐患。

其中很有趣很subtle的一点是:他们在项目初期决定先实现对MSIL的大范围的支持,为此抄了些捷径:

  • 生成LLVM IR后,几乎不打开LLVM的优化pass,直接去生成目标代码
  • 使用CoreCLR的GC的后备漠视——保守式GC(conservative GC),这样就可以让LLILC不处理栈上的托管指针,而是让CoreCLR自己去保守式扫描栈上的值去猜哪些是托管指针。

这俩捷径的好处是项目初期可以快速推进进度,把大部分MSIL指令都支持上,让LLILC可以编译一些小程序来测试。

但坏处在后期会慢慢浮现出来:一个编译器前端在生成LLVM IR后,如果不打开LLVM的优化pass来编译,其实无法确定到底生成的IR对不对——有没有正确实现想要表达的语义。如果有些代码生成模式已经大规模写进去了,然后才发现不对,那改起来就会极其痛苦。毕竟最终LLILC还是想要打开LLVM的优化的,不然要它来干嘛,所以这不得不面对的问题,还是早点面对了比较好。

而借由CoreCLR的conservative GC支持的初步运行也可能掩盖了一些其实难以解决的问题,等后面真的要尝试解决的时候才发现困难的话多头疼。

说来,LLILC一开始就决定放弃CoreCLR的JIT所支持的“fully interruptible code”了。这种东西用LLVM实现确实非常别扭,放弃就放弃吧。


============================================

不知道他们是不是遇到了什么解决起来很麻烦(我不想说“无法解决”)的技术问题,导致这个项目被放弃/搁置/降低优先级了。C# / CLR需要支持的功能确实比Java要复杂许多,因此LLILC也遇到了许多我们在我们的基于LLVM的JVM JIT编译器里不会遇到的问题。

同事提到,跟LLILC组的人讨论的时候,他们提出过许多问题。

其中一个是:C#有value type,在CLR / CoreCLR的实现中value type既可能被分配在栈帧里,也可能被box后分配在托管堆里;而且C#还支持pass-by-reference的参数传递模式,一个类型为value type的reference到底是引用着一个栈上还是堆上的value type实例,从被调用方法的一侧是无从知道的。然而如果用LLVM的address space来实现对普通内存(用默认address space)和托管堆内存(用某个自定义address space)的区分,LLVM就要求要把指针声明为对应的address space,这样一个指针就不允许既可能指向栈上对象又可能指向堆上对象,于是处理起来就有点麻烦。

另外,C#的value type里可以包含托管指针字段。如果一个含有托管指针字段的value type值被分配在栈上,而GC又要求对栈上的托管指针做准确式扫描(precise GC / type-exact GC),那么当然这些栈上的托管指针要能被正确处理。然而Azul给LLVM加入的gc.statepoint并不能直接处理这种情况——gc.statepoint所能指定的root set都是LLVM IR意义上的SSA value,而不包括实际在内存栈帧里的值;即便给gc.statepoint添加对栈帧内存的支持,在LLVM IR里想要精确控制整个栈帧在内存里的布局是非常困难甚至可能做不到的——是,每一个alloca内部的布局是可以精确控制的,但如果我们想精确控制某个slot在栈帧里的实际偏移量,做不到。而C#的value type是一个需要精确控制内存布局的聚合类型(aggregate type),我们不能说把里面的字段给打散了分别处理然后再聚合回来,而总是得保证整个value type的值打包在一起处理。<- 让我找找看LLILC的issues或者wiki之类的地方有没有人讨论过这个问题然后再来补充。

24 条评论