我打算创建一个优化的数据结构来保存汇编代码.这样我就可以完全负责将在这个结构上工作的优化算法.如果我可以在运行时编译.它将是一种动态执行.这可能吗?有人见过这样的东西吗?
我应该使用结构将结构链接到程序流程中.对象更好吗?
struct asm_code { int type; int value; int optimized; asm_code *next_to_execute; } asm_imp;
更新:我认为它会变成一个链表.
更新:我知道还有其他编译器.但这是一个军事绝密项目.所以我们不能相信任何代码.我们必须自己做这一切.
更新:好的,我想我只会生成基本的i386机器代码.但是如何在完成时跳进我的内存blob?
有可能的.动态代码生成甚至是软件渲染和图形等领域的主流.您可以在各种脚本语言中找到很多用法,在机器代码中动态编译字节码(.NET,Java,据我所知Perl.最近JavaScript也加入了俱乐部).
你也发现它也用在非常重数学的应用程序中,如果你打算在矩阵乘法中删除所有乘法,如果你计划进行数千次这样的乘法,那么它会有所不同.
我强烈建议您阅读代码的SSA表示.这是一种表示,其中每个基元被转换为所谓的三个操作数形式,并且每个变量仅被分配一次(因此相同的静态单一赋值形式).
您可以对此类代码运行高阶优化,并且可以直接将该代码转换为可执行代码.你不会在周末编写代码生成后端...
要了解SSA的外观,您可以尝试使用LLVM编译器.在他们的网站上,他们有一个小的"试用"小部件可以玩.您将C代码粘贴到窗口中,然后您可以获得与SSA表单相近的内容.
它的外观很简单:
让我们在C中采用这个整数平方根算法.(任意的例子,我只是采取了一些简单但非平凡的事情):
unsigned int isqrt32 (unsigned int value) { unsigned int g = 0; unsigned int bshift = 15; unsigned int b = 1<= temp) { g += b; value -= temp; } b>>=1; } while (bshift--); return g; }
LLVM将其变为:
define i32 @isqrt32(i32 %value) nounwind { entry: br label %bb bb: ; preds = %bb, %entry %indvar = phi i32 [ 0, %entry ], [ %indvar.next, %bb ] %b.0 = phi i32 [ 32768, %entry ], [ %tmp23, %bb ] %g.1 = phi i32 [ 0, %entry ], [ %g.0, %bb ] %value_addr.1 = phi i32 [ %value, %entry ], [ %value_addr.0, %bb ] %bshift.0 = sub i32 15, %indvar %tmp5 = shl i32 %g.1, 1 %tmp7 = add i32 %tmp5, %b.0 %tmp9 = shl i32 %tmp7, %bshift.0 %tmp12 = icmp ult i32 %value_addr.1, %tmp9 %tmp17 = select i1 %tmp12, i32 0, i32 %b.0 %g.0 = add i32 %tmp17, %g.1 %tmp20 = select i1 %tmp12, i32 0, i32 %tmp9 %value_addr.0 = sub i32 %value_addr.1, %tmp20 %tmp23 = lshr i32 %b.0, 1 %indvar.next = add i32 %indvar, 1 %exitcond = icmp eq i32 %indvar.next, 16 br i1 %exitcond, label %bb30, label %bb bb30: ; preds = %bb ret i32 %g.0 }
我知道一开始看起来很可怕.它甚至不是纯粹的SSA表格.你对这种表现的阅读越多,它就越有意义.此外,您还将了解为什么这种表示如此广泛使用.
将所需的所有信息封装到数据结构中很容易.最后,您必须决定是否要使用枚举或字符串作为操作码名称等.
顺便说一句 - 我知道我没有给你一个数据结构,但更多的是一个正式但实用的语言,并建议在哪里进一步观察.
这是一个非常好的和有趣的研究领域.
编辑:在我忘记它之前:不要忽视.NET和Java的内置功能.这些语言允许您从程序中的字节代码或源代码进行编译并执行结果.
干杯,尼尔斯
关于编辑:如何使用代码执行二进制blob:
跳转到二进制blob是依赖于操作系统和平台的.简而言之,您可以使用指令缓存,也许您必须回写数据缓存,并且可能必须在您编写代码的内存区域上启用执行权限.
在win32上,它相对容易,因为如果将代码放在堆上,指令缓存刷新似乎就足够了.
您可以使用此存根来开始:
typedef void (* voidfunc) (void); void * generate_code (void) { // reserve some space unsigned char * buffer = (unsigned char *) malloc (1024); // write a single RET-instruction buffer[0] = 0xc3; return buffer; } int main (int argc, char **args) { // generate some code: voidfunc func = (voidfunc) generate_code(); // flush instruction cache: FlushInstructionCache(GetCurrentProcess(), func, 1024); // execute the code (it does nothing atm) func(); // free memory and exit. free (func); }