汇编代码的数据结构? [研究]

时间:2008-09-20 15:14:05

标签: c++ c dynamic compilation

我打算创建一个优化的数据结构来保存汇编代码。这样我就可以完全负责将在这个结构上工作的优化算法。如果我可以在运行时编译。它将是一种动态执行。这可能吗?有人见过这样的东西吗?

我应该使用结构将结构链接到程序流程中。对象更好吗?

struct asm_code {
   int type;
   int value;
   int optimized;
   asm_code *next_to_execute;
 } asm_imp;

更新:我认为它会像链接列表一样。

更新:我知道还有其他编译器。但这是一个军事绝密项目。所以我们不能相信任何代码。我们必须自己做这一切。

更新:好的,我想我只会生成基本的i386机器代码。但是如何在完成后跳入我的内存blob?

5 个答案:

答案 0 :(得分:7)

有可能。动态代码生成甚至是软件渲染和图形等领域的主流。您可以在各种脚本语言中找到很多用法,在机器代码中动态编译字节码(.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<<bshift;
    do {
        unsigned int temp = (g+g+b)<<bshift;
        if (value >= 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表格。你对这种表现的阅读越多,它就越有意义。而且你也会发现为什么这种表现形式如今被广泛使用。

将所需的所有信息封装到数据结构中非常简单。最后,您必须决定是否要使用枚举或字符串作为操作码名称等。

Btw - 我知道我没有给你一个数据结构,但更多的是一个正式但实用的语言,并建议在哪里进一步观察。

这是一个非常好的和有趣的研究领域。

编辑:在我忘记它之前:不要忽视.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);
}

答案 1 :(得分:1)

我假设你想要一个数据结构来保存某种指令模板,可能是从现有的机器代码中解析出来的,类似于:

add r1, r2, <int>

您将拥有此结构的数组,您将对此数组执行一些优化,可能会更改其大小或构建新数组,并生成相应的机器代码。

如果您的目标计算机使用可变宽度指令(例如x86),则无法在不实际解析构建阵列的指令的情况下确定数组大小。在实际生成优化数组的所有指令之前,您无法确切地确定需要多少缓冲区。你可以做一个很好的估计。

结帐GNU Lightning。它可能对你有用。

答案 2 :(得分:0)

在99%的案例中,表现差异可以忽略不计。类的主要优点是OOP生成的代码比过程代码更好,更容易理解。

我不确定你编码的语言是什么 - 请注意,在C#中,类和结构之间的主要区别在于结构是值类型而类是引用类型。在这种情况下,您可能希望从结构开始,但仍然向它们添加行为(构造函数,方法)。

答案 3 :(得分:0)

不讨论优化自己的代码的技术价值,在C ++代码中,在POD结构或完整对象之间进行选择主要是一个封装点。

内联代码将让编译器优化(或不优化)使用的构造函数/访问器。不会有性能损失。

首先,设置构造函数

如果您正在使用C ++编译器,请至少创建一个构造函数:

struct asm_code {
   asm_code()
      : type(0), value(0), optimized(0) {}

   asm_code(int type_, int value_, int optimized_)
      : type(type_), value(value_), optimized(_optimized) {}

   int type;
   int value;
   int optimized;
 };

至少,您的代码中不会有未定义的结构。

每种数据组合都可以吗?

使用像您一样使用的结构意味着任何类型都是可能的,具有任何值,并且任何类型都是优化的。例如,如果我设置type = 25,value = 1205并优化= -500,那么它就是Ok。

如果您不希望用户在结构中放置随机值,请添加内联访问者:

struct asm_code {

   int getType() { return type ; }
   void setType(int type_) { VERIFY_TYPE(type_) ; type = type_ ; }

   // Etc.

   private :
      int type;
      int value;
      int optimized;
 };

这将允许您控制结构中设置的内容,并更轻松地调试代码(甚至可以对代码进行运行时验证)

答案 4 :(得分:0)

经过一番阅读,我的结论是普通的lisp最适合这项任务。 使用lisp宏我有巨大的力量。