在运行时构建函数c ++从编译时构建的函数数量

时间:2012-07-29 01:09:40

标签: c++ code-generation bytecode

我正在创建首先解析代码的脚本语言 然后将函数(执行代码)复制到一个缓冲区\内存作为解析代码。

有一种方法可以将函数的二进制代码复制到缓冲区然后执行整个缓冲区吗? 我需要立即执行所有功能以获得更好的性能。

为了最好地理解我的问题,我想做这样的事情:

#include <vector>
using namespace std;

class RuntimeFunction; //The buffer to my runtime function

enum ByteCodeType {
    Return,
    None
};

class ByteCode {
    ByteCodeType type;
}

void ReturnRuntime() {
    return;
}

RuntimeFunction GetExecutableData(vector<ByteCode> function) {
    RuntimeFunction runtimeFunction=RuntimeFunction(sizeof(int)); //Returns int
    for (int i = 0 ; i < function.size() ; i++ ) {
        #define CurrentByteCode function[i]
        if (CurrentByteCode.Type==Return) {
            runtimeFunction.Append(&ReturnRuntime);
        } //etc.
        #undef
    }
    return runtimeFunction;
}

void* CallFunc(RuntimeFunction runtimeFunction,vector<void*> custom_parameters) {
    for (int i=custom_parameters-1;i>=0;--i) { //Invert parameters loop
        __asm {
            push custom_parameters[i]
        }
    }
    __asm {
        call runtimeFunction.pHandle
    }
}

1 个答案:

答案 0 :(得分:3)

有很多方法可以实现这一点,具体取决于您希望在运行时生成代码的深度,但一种相对简单的方法是使用线程代码和线程代码解释器。

基本上,线程代码由一组函数指针组成,解释器通过调用每个指向函数的数组。棘手的部分是你通常让每个函数返回包含指向下一个要调用的函数的指针的数组元素的地址,这允许你在解释器中不需要任何努力就实现分支和调用之类的东西

通常你会使用类似的东西:

typedef void *(*tc_func_t)(void *, runtime_state_t *);

void *interp(tc_func_t **entry, runtime_state_t *state) {
    tc_func_t *pc = *entry;
    while (pc) pc = (*pc)(pc+1, state);
    return entry+1;
}

这就是整个翻译。 runtime_state_t是某种包含某些运行时状态(通常是一个或多个堆栈)的数据结构。您可以通过创建一个tc_func_t函数指针数组并使用函数指针(可能还有数据)填充它们,以空指针结束,然后使用包含该变量的变量的地址调用interp来调用它。数组的开始。所以你可能有类似的东西:

void *add(tc_func_t *pc, runtime_state_t *state) {
    int v1 = state->data.pop();
    int v2 = state->data.pop();
    state->data.push(v1 + v2);
    return pc; }
void *push_int(tc_func_t *pc, runtime_state_t *state) {
    state->data.push((int)*pc);
    return pc+1; }
void *print(tc_func_t *pc, runtime_state_t *state) {
    cout << state->data.pop();
    return pc; }

tc_func_t program[] = {
    (tc_func_t)push_int,
    (tc_func_t)2,
    (tc_func_t)push_int,
    (tc_func_t)2,
    (tc_func_t)add,
    (tc_func_t)print,
    0
};

void run_prgram() {
    runtime_state_t  state;
    tc_func_t *entry = program;
    interp(&entry, &state);
}

调用run_program运行添加2 + 2的小程序并打印结果。

现在你可能会对interp的稍微奇怪的调用设置感到困惑,在entry参数上有一个额外的间接层。这样你就可以将interp本身用作线程代码数组中的函数,然后是指向另一个数组的指针,它将进行线程代码调用。

修改

像这样的线程代码的最大问题与性能有关 - 线程编码解释器对分支预测器非常不友好,因此性能几乎被锁定在每个分支错误预测恢复的一个线程指令调用时间。

如果您想获得更高的性能,那么您几乎必须进行全面的运行时代码生成。 LLVM为这样做提供了一个良好的,与机器无关的接口,以及用于在运行时生成相当好的代码的通用平台的非常好的优化器。