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

时间:2009-06-30 04:36:56

标签: string llvm

我最近一直在关注LLVM,我发现这是一个非常有趣的架构。但是,在查看教程和参考资料时,我看不到任何有关如何实现string数据类型的示例。

有很多关于整数,实数和其他数字类型的文档,甚至是数组,函数和结构,但AFAIK没有关于字符串的内容。我需要add a new data type到后端吗?有没有办法使用内置数据类型?任何见解都将不胜感激。

5 个答案:

答案 0 :(得分:16)

什么是字符串?一组字符。

什么是角色?整数。

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

#include <stdio.h>

int main() {
        puts("Hello, world!");
        return 0;
}

我们使用llvm-gcc编译它并转储生成的LLVM程序集:

$ 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)

在LLVM中,它是

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*)

答案 1 :(得分:11)

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

使用C接口,您需要的呼叫类似于:

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;
}

答案 2 :(得分:2)

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

  • C:指向角色的指针。你不需要做任何特别的事。
  • C ++:string是一个带有构造函数,析构函数和复制构造函数的复杂对象。在内部,它通常基本上包含C字符串。
  • Java / C#/ ...:字符串是一个包含字符数组的复杂对象。

LLVM的名字非常自我解释。它真的是“低级”。你必须以你想要的方式实现字符串。 LLVM迫使任何人进入特定的实现都是愚蠢的。

答案 3 :(得分:1)

使用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");

生成的IR为:

; 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(...)

答案 4 :(得分:0)

对于使用LLVM的C ++ API的用户,可以依靠#!/usr/bin/env sh的{​​{1}}:

IRBuilder

这将在最终的LLVM IR中表示为CreateGlobalStringPtr