另一个问题中已经介绍了C / C ++框架中动态代码生成的简单基础。是否有关于代码示例的主题的温和介绍?

当我的需求更加温和时,我的眼睛开始流露出高度错综复杂的开源JIT编译器。

是否有关于该主题的优秀文本没有获得计算机科学博士学位?我正在寻找破旧的模式,需要注意的事项,性能考虑因素等。电子或基于树的资源同样有价值。您可以假设(不仅仅是x86)汇编语言的工作知识。

有帮助吗?

解决方案

我在模拟器中使用的模式是这样的:

typedef void (*code_ptr)();
unsigned long instruction_pointer = entry_point;
std::map<unsigned long, code_ptr> code_map;


void execute_block() {
    code_ptr f;
    std::map<unsigned long, void *>::iterator it = code_map.find(instruction_pointer);
    if(it != code_map.end()) {
        f = it->second
    } else {
        f = generate_code_block();
        code_map[instruction_pointer] = f;
    }
    f();
    instruction_pointer = update_instruction_pointer();
}

void execute() {
    while(true) {
        execute_block();
    }
}

这是一种简化,但这个想法就在那里。基本上,每次要求发动机执行“基本块”时, (通常是下一个流程控制操作或可能的整个功能),它会查看它是否已经创建。如果是这样,执行它,否则创建它,添加它然后执行。

重复冲洗:)

至于代码生成,这有点复杂,但想法是发出适当的“功能”。它可以在VM的上下文中完成基本块的工作。

编辑:请注意,我还没有展示任何优化,但你要求“温和的介绍”

编辑2:我忘了提到你可以用这种模式实现的最快速的生产速度之一。基本上,如果你从不从你的树中删除一个块(你可以解决它,如果你这样做,但如果你从来没有这样做那么简单),那么你可以“链”。阻止在一起以避免查找。这是概念。无论何时从f()返回并且即将执行“update_instruction_pointer”,如果您刚执行的块在调用,无条件跳转或根本没有在流量控制中结束,那么您可以“修复” ;它的ret指令带有直接jmp到下一个块它将执行(因为它总是相同的)如果你已经发出它。这使得它在VM中越来越频繁地执行,而在“execute_block”中越来越少。功能

其他提示

我不知道任何与JIT特别相关的来源,但我认为它非常像普通的编译器,只有在你不担心性能时才会更简单。

最简单的方法是从VM解释器开始。然后,对于每个VM指令,生成解释器将执行的汇编代码。

为了超越它,我想你会解析VM字节代码并将它们转换成某种合适的中间形式(三个地址代码?SSA?)然后像任何其他编译器一样优化和生成代码。

对于基于堆栈的VM,跟踪“当前”VM可能有所帮助。将字节代码转换为中间形式时的堆栈深度,并将每个堆栈位置视为变量。例如,如果您认为当前堆栈深度为4,并且您看到“推送”状态。在指令中,您可以生成对“stack_variable_5”的赋值。并增加编译时堆栈计数器,或类似的东西。 “添加”当堆栈深度为5时,可能生成代码“stack_variable_4 = stack_variable_4 + stack_variable_5”。并减少编译时堆栈计数器。

还可以将基于堆栈的代码转换为语法树。保持编译时堆栈。每次“推”都是“推”。指令导致被推送的事物的表示存储在堆栈上。运算符创建包含其操作数的语法树节点。例如,“X Y +”表示“X Y +”。可能导致堆栈包含“var(X)”,然后“var(X)var(Y)”。然后加号弹出两个var引用关闭并按“加(var(X),var(Y))”。

给自己一份Joel Pobar关于Rotor的书(当它出来的时候),并深入研究来源 SSCLI 。当心,精神错乱在于:)

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top