How to generate code for initializing global variables with non-const values in LLVM?

2k Views Asked by At

In LLVM (specifically llvmlite), how does one declare a global variable and initialize its contents with the result of an arbitrary (runtime) expression?

I see that I can create a GlobalVariable object, but it looks like its initializer argument is expecting a Constant. What if I must run arbitrary code at startup/load time to determine its value? Where does that code go? Whose Builder do I add instructions to? Do I declare a specially-named function and/or add magic attributes to it so that it automatically execs when the module is loaded into memory at runtime?

3

There are 3 best solutions below

0
On BEST ANSWER

It entirely depends on your setup. In C or C++ with Visual Studio, C and C++ initialization functions end up getting put into a subsection of the .CRT section, and are executed by the standard runtime library.

If you compile without the CRT and have these initialization functions, they will not fire because the runtime takes care of that.

Correction edit: It appears @llvm.global_ctors exists.

I'm unsure if these will fire correctly in an environment without a runtime library that helps to execute initializers, but there you have it.

0
On

You can store the expression initializer into the global variable from main function.

// all LLVM includes
using namespace std;

LLVMContext context;
Module* module = new Module("module_1", context);
IRBuilder<> builder(context);
SymbolTable symbolTable; // some structure to map the symbol names to the LLVM value

// ...

Value* createGlobalVar(const string& id, Type* type, Value* init) {
    Value* gv = new GlobalVariable(*module, type, false, GlobalValue::PrivateLinkage, Constant::getNullValue(type), id);

    Function* mainFunc = symbolTable.get("main");
    BasicBlock* entryBB = &(mainFunc->getEntryBlock());

    BasicBlock* currentBB = &(builder.GetInsertBlock());
    auto currentInsertPoint = builder.GetInsertPoint(); // returns an iterator
    builder.SetInsertPoint(entryBB, entryBB->begin());
    builder.CreateStore(init, gv);
    builder.SetInsertBlock(currentBB, currentInsertPoint);
    symbolTable.add(id, gv);
    return gv;
}

Although you're gonna replace the initial value by an arbitrary expression, the GlobalVariable must have an initializer to indicates it belongs to the current module. Otherwise it will expects some linkage with some existing variable.

I wrote this function thinking that your global variable declaration can be found anywhere throughout the code. However, if you're generating code from an Abstract Syntax Tree (AST), it's likely your declaration will show up before main declaration, that is, before main is inserted into the symbol table. In that case, you can create the global variable and insert it into the symbol table and create the store instruction after all analysis is done.

0
On

Linking to @keyboardsmoke's answer:

You can generate llvm.global_ctors by using TransformUtils.

#include "llvm/Transforms/Utils/ModuleUtils.h"
Function* globalInitFunction = llvm::getOrCreateInitFunction(*TheModule, "_VarDeclInitializations");
// add basic block to give body to _VarDeclInitializations, etc.