代码之家  ›  专栏  ›  技术社区  ›  a_m0d

如何在LLVM中实现字符串数据类型?

  •  13
  • a_m0d  · 技术社区  · 15 年前

    我一直在看 LLVM 最近,我发现它是一个非常有趣的建筑。然而,通过阅读教程和参考资料,我看不到任何关于如何实现 string 数据类型。

    有很多关于整数、实数和其他数字类型,甚至是数组、函数和结构的文档,但对字符串一无所知。我必须这样做吗 add a new data type 到后端?有没有办法使用内置数据类型?如有任何见解,将不胜感激。

    4 回复  |  直到 7 年前
        1
  •  24
  •   Jason C    15 年前

    什么是线?一组字符。

    因此,尽管我无论如何都不是LLVM专家,但我想,如果你想表示某个8位字符集,你应该使用一个i8数组(8位整数),或者一个指向i8的指针。事实上,如果我们有一个简单的hello world C程序:

    #include <stdio.h>
    
    int main() {
            puts("Hello, world!");
            return 0;
    }
    

    $ llvm-gcc -S -emit-llvm hello.c
    $ cat hello.s
    ; ModuleID = 'hello.c'
    target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128"
    target triple = "x86_64-linux-gnu"
    @.str = internal constant [14 x i8] c"Hello, world!\00"         ; <[14 x i8]*> [#uses=1]
    
    define i32 @main() {
    entry:
            %retval = alloca i32            ; <i32*> [#uses=2]
            %tmp = alloca i32               ; <i32*> [#uses=2]
            %"alloca point" = bitcast i32 0 to i32          ; <i32> [#uses=0]
            %tmp1 = getelementptr [14 x i8]* @.str, i32 0, i64 0            ; <i8*> [#uses=1]
            %tmp2 = call i32 @puts( i8* %tmp1 ) nounwind            ; <i32> [#uses=0]
            store i32 0, i32* %tmp, align 4
            %tmp3 = load i32* %tmp, align 4         ; <i32> [#uses=1]
            store i32 %tmp3, i32* %retval, align 4
            br label %return
    
    return:         ; preds = %entry
            %retval4 = load i32* %retval            ; <i32> [#uses=1]
            ret i32 %retval4
    }
    
    declare i32 @puts(i8*)
    

    请注意对文件末尾声明的puts函数的引用。在C中,puts是

    int puts(const char *s)
    

    i32 @puts(i8*)
    

    顺便说一句,这里生成的LLVM非常冗长,因为我编译时没有进行优化。如果启用这些选项,则不必要的说明将消失:

    $ llvm-gcc -O2 -S -emit-llvm hello.c
    $ cat hello.s 
    ; ModuleID = 'hello.c'
    target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128"
    target triple = "x86_64-linux-gnu"
    @.str = internal constant [14 x i8] c"Hello, world!\00"         ; <[14 x i8]*> [#uses=1]
    
    define i32 @main() nounwind  {
    entry:
            %tmp2 = tail call i32 @puts( i8* getelementptr ([14 x i8]* @.str, i32 0, i64 0) ) nounwind              ; <i32> [#uses=0]
            ret i32 0
    }
    
    declare i32 @puts(i8*)
    
        2
  •  16
  •   Bernard    14 年前

    [为了跟进解释字符串是什么的其他答案,这里有一些实现帮助]

    LLVMValueRef llvmGenLocalStringVar(const char* data, int len)
    {
      LLVMValueRef glob = LLVMAddGlobal(mod, LLVMArrayType(LLVMInt8Type(), len), "string");
    
      // set as internal linkage and constant
      LLVMSetLinkage(glob, LLVMInternalLinkage);
      LLVMSetGlobalConstant(glob, TRUE);
    
      // Initialize with string:
      LLVMSetInitializer(glob, LLVMConstString(data, len, TRUE));
    
      return glob;
    }
    
        3
  •  6
  •   Robin    5 年前

    对于那些使用LLVM的C++ API的人,你可以依靠 IRBuilder CreateGlobalStringPtr :

    Builder.CreateGlobalStringPtr(StringRef("Hello, world!"));
    

    这将表示为 i8* 在最后的LLVM IR中。

        4
  •  3
  •   Zifre    15 年前

    请考虑如何在通用语言中表示字符串:

    • C:指向字符的指针。你不必做任何特别的事。
    • C++: string 是一个具有构造函数、析构函数和复制构造函数的复杂对象。在内部,它通常包含一个C字符串。

    LLVM的名字是不言自明的。这真的是“低水平”。您必须实现字符串的任何形式。LLVM强迫任何人进入特定的实现是愚蠢的。

        5
  •  3
  •   maiquynhtruong    6 年前

    使用C API,而不是使用 LLVMConstString ,你可以用 LLVMBuildGlobalString . 下面是我的实现

    int main() {
        printf("Hello World, %s!\n", "there");
        return;
    }
    

    使用C API:

    LLVMTypeRef main_type = LLVMFunctionType(LLVMVoidType(), NULL, 0, false);
    LLVMValueRef main = LLVMAddFunction(mod, "main", main_type);
    
    LLVMTypeRef param_types[] = { LLVMPointerType(LLVMInt8Type(), 0) };
    LLVMTypeRef llvm_printf_type = LLVMFunctionType(LLVMInt32Type(), param_types, 0, true);
    LLVMValueRef llvm_printf = LLVMAddFunction(mod, "printf", llvm_printf_type);
    
    LLVMBasicBlockRef entry = LLVMAppendBasicBlock(main, "entry");
    LLVMPositionBuilderAtEnd(builder, entry);
    
    LLVMValueRef format = LLVMBuildGlobalStringPtr(builder, "Hello World, %s!\n", "format");
    LLVMValueRef value = LLVMBuildGlobalStringPtr(builder, "there", "value");
    
    LLVMValueRef args[] = { format, value };
    LLVMBuildCall(builder, llvm_printf, args, 2, "printf");
    
    LLVMBuildRetVoid(builder);
    

    LLVMValueRef format = LLVMBuildGlobalStringPtr(builder, "Hello World, %s!\n", "format");
    LLVMValueRef value = LLVMBuildGlobalStringPtr(builder, "there", "value");
    

    ; ModuleID = 'printf.bc'
    source_filename = "my_module"
    target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
    
    @format = private unnamed_addr constant [18 x i8] c"Hello World, %s!\0A\00"
    @value = private unnamed_addr constant [6 x i8] c"there\00"
    
    define void @main() {
    entry:
      %printf = call i32 (...) @printf(i8* getelementptr inbounds ([18 x i8], [18 x i8]* @format, i32 0, i32 0), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @value, i32 0, i32 0))
      ret void
    }
    
    declare i32 @printf(...)