Использование глобальных переменных в MCJIT

Я пытаюсь JIT-компилировать некоторые функции в существующей программе C/C++ во время выполнения, но у меня возникают проблемы с инициализацией глобальной переменной. В частности, подход, который я выбрал, заключается в использовании Clang для предварительной компиляции программы в модули битового кода IR в дополнение к исполняемому файлу. Во время выполнения программа загружает модули, преобразовывает их (специализация программы), компилирует и выполняет их. Как оказалось, у меня есть несколько глобальных переменных, которые инициализируются и изменяются во время выполнения «хост-программы». В настоящее время эти глобальные переменные также инициализируются в коде, скомпилированном JIT, тогда как вместо этого я бы хотел, чтобы они были сопоставлены с глобальными переменными хоста. Может кто-то помочь мне с этим?

Небольшая репродукция приведена ниже. Полный исходный код находится здесь. Файл somefunc.cpp предварительно компилируется во время сборки и загружается в функцию main() в testCompile.cpp. Глобальная переменная xyz инициализируется так, чтобы указывать на 25 в somefunc.cpp, но вместо этого я бы хотел, чтобы она указывала на 10, как в main(). Другими словами, утверждение в main() должно быть успешным.

Я пробовал несколько разных способов решить эту проблему. Функция ChangeGlobal() пытается (безуспешно) выполнить это updateGlobalMapping(). Второй, более хакерский подход использует новую глобальную переменную, инициализированную соответствующим образом. Я могу заставить этот последний подход работать для некоторых типов глобальных переменных, но есть ли более элегантный подход, чем этот?

//————— somefunc.h ————————
extern int *xyz;

//—————— somefunc.cpp ——————
int abc = 25;
int *xyz = &abc;

int somefunc() {
    return *xyz;
}

//—————— testCompile.cpp —————— 
class JitCompiler {
 public:
    JitCompiler(const std::string module_file);
    void LoadModule(const std::string& file);
    template <typename FnType>  
        FnType CompileFunc(FnType fn, const std::string& fn_name);
    void ChangeGlobal();

 private:
    std::unique_ptr<LLVMContext> context_;
    Module *module_;
    std::unique_ptr<ExecutionEngine> engine_;
};

void JitCompiler::ChangeGlobal() {
    // ----------------- #1: UpdateGlobalMapping -----------------
    //auto g = engine_->FindGlobalVariableNamed("xyz");
    //engine_->updateGlobalMapping(g, &xyz);
    //assert(engine_->getGlobalValueAddress("xyz") == (uint64_t) &xyz);

    // ----------------- #2: Replace with new global ————————
    // ------- Ugly hack that works for globals of type T** ----------
    auto g = engine_->FindGlobalVariableNamed("xyz");
    Constant *addr_i = ConstantInt::get(*context_, APInt(64, (uint64_t) xyz));
    auto addr = ConstantExpr::getIntToPtr(
                    addr_i, g->getType()->getPointerElementType());

    GlobalVariable *n = new GlobalVariable(
        *module_,
        g->getType()->getPointerElementType(),
        g->isConstant(),
        g->getLinkage(),
        addr,
        g->getName() + "_new");
    g->replaceAllUsesWith(n);
    n->takeName(g);
    g->eraseFromParent();
}

int main() {
    xyz = new int (10);
    JitCompiler jit("somefunc.bc");

    jit.ChangeGlobal();
    auto fn = jit.CompileFunc(&somefunc, "somefunc");
    assert(somefunc() == fn());
}

person npotti    schedule 21.11.2015    source источник


Ответы (1)


Лучшим подходом является комбинация двух представленных вами, то есть создание нового глобального с внешней связью, сопоставленной с &xyz, и замена оригинала:

auto g = engine_->FindGlobalVariableNamed("xyz");

GlobalVariable *n = new GlobalVariable(
  g->getType()->getPointerElementType(),
  g->isConstant(),
  ExternalLinkage
  nullptr,
  g->getName() + "_new");

engine_->updateGlobalMapping(n, &xyz);

g->replaceAllUsesWith(n);
n->takeName(g);
g->eraseFromParent();
person eush77    schedule 22.06.2016