【问题标题】:llvm, defining strings and arrays via c++ APIllvm,通过 c++ API 定义字符串和数组
【发布时间】:2018-08-13 02:20:57
【问题描述】:

我开发了一个玩具编译器,并尝试实现字符串和数组。 我注意到 clang 总是为这些类型创建一个全局变量,即使它们在函数中定义。 我想这是有充分理由的,所以我也尝试这样做。

我的问题是我无法弄清楚如何通过 c++ API 做到这一点。 kalidoscope tutorial 不包括字符串和数组,所以我找到的唯一来源是the documentation

在 Module 类的文档中,有函数 getOrInsertGlobal,看起来很相关,但我不明白我是如何设置全局的实际值的。函数参数仅包括变量的名称和类型。那么价值去哪儿了呢?

那么问题来了:如何在llvm c++ API中定义一个全局字符串,比如“hello”或者数组,比如[i32 1, i32 2]?任何例子都将不胜感激。

【问题讨论】:

    标签: c++ llvm


    【解决方案1】:

    你想要的是一个只读的GlobalVariable,你需要这个变量,一个初始化器,可能还有一个constant cast,这样你的所有字符串都可以有相同的类型。

    假设您的字符串是 C 类型的 - 以 null 结尾的字节序列。在这种情况下,您会希望您的字符串是一个零字节数组,以便所有数组都具有相同的类型。但是初始化器需要是正确字节数的数组,这样每个初始化器的类型都会匹配它的值。所以你使用这样的东西创建你的数组(从我编写的代码中剪切和粘贴在一起,甚至不会编译,不是最有效的方法,但确实包含你需要的大部分构建块):

    std::vector<llvm::Constant *> chars(utf8string.size());
    for(unsigned int i = 0; i < utf8string.size(); i++)
      chars[i] = ConstantInt::get(i8, utf8string[i]);
    auto init = ConstantArray::get(ArrayType::get(i8, chars.size()),
                                   entries);
    GlobalVariable * v = 
      new GlobalVariable(module, init->getType(), true,
                         GlobalVariable::ExternalLinkage, init,
                         utf8string);
    return ConstantExpr::getBitCast(v, i8->getPointerTo());
    

    请注意,GlobalVariable 是一个指向任何它被初始化的指针,所以如果你用五字节序列“test\0”初始化它,那么它将是一个指向五字节的指针。或者,如果你进行强制转换,它可以是一个指向 0 字节的指针(LLVM 允许你索引超出官方结尾),或者如果你定义了一个抽象类型,它可以是一个实例。

    【讨论】:

    • 感谢您的回答。我实施了价值构建部分。看起来工作得很好。我仍然不明白如何将全局声明插入到模块中,因为我有了值。你能向我解释一下这部分吗?
    • 模块负责发出全局变量(到 .bc 文件、可执行文件或内存中 JIT 处理的结果),因此它必须了解所有函数、常量字符串和其他所有内容. GlobalVariable() 构造函数将使用提供的名称注册新创建的对象,该名称必须是唯一的。这就是我想的。 (顺便说一句,如果等,您应该将答案标记为已接受。我认为是橙色标记。)
    • 好的,我明白了!只需在您的代码上添加一点。我们是否负责删除您在上面使用 new 创建的对象,或者 ConstantExpr 的析构函数会?很抱歉问了很多问题,我不是 C++ 专家,我努力不把内存管理弄得一团糟。到目前为止,我从未为 llvm 类型调用 new,只有模块中的 get___() 函数、类型等。
    • 对象必须与模块一样长。我不记得模块是否会删除它,或者可能是上下文,或者你是否必须这样做。但是在删除模块之前,您绝对不能删除 std::vector 以外的任何内容。
    【解决方案2】:

    在上面的答案中使用代码和@arnt 的帮助,我最终得到了以下代码来实现字符串初始化。它现在可以工作了,并且还避免了对 new 的调用,因此以后不需要任何清理。

    我发布它,希望它可能对某人有用。

    llvm::Value* EulStringToken::generateValue(llvm::Module* module, llvm::LLVMContext context) {
        //0. Defs
        auto str = this->value;
        auto charType = llvm::IntegerType::get(context, 8);
    
    
        //1. Initialize chars vector
        std::vector<llvm::Constant *> chars(str.length());
        for(unsigned int i = 0; i < str.size(); i++) {
          chars[i] = llvm::ConstantInt::get(charType, str[i]);
        }
    
        //1b. add a zero terminator too
        chars.push_back(llvm::ConstantInt::get(charType, 0));
    
    
        //2. Initialize the string from the characters
        auto stringType = llvm::ArrayType::get(charType, chars.size());
    
        //3. Create the declaration statement
        auto globalDeclaration = (llvm::GlobalVariable*) module->getOrInsertGlobal(".str", stringType);
        globalDeclaration->setInitializer(llvm::ConstantArray::get(stringType, chars));
        globalDeclaration->setConstant(true);
        globalDeclaration->setLinkage(llvm::GlobalValue::LinkageTypes::PrivateLinkage);
        globalDeclaration->setUnnamedAddr (llvm::GlobalValue::UnnamedAddr::Global);
    
    
    
        //4. Return a cast to an i8*
        return llvm::ConstantExpr::getBitCast(globalDeclaration, charType->getPointerTo());
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-09-23
      • 2014-10-30
      • 2022-07-04
      • 1970-01-01
      • 2011-04-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多