我最近一直在关注LLVM,我发现这是一个非常有趣的架构。但是,在查看教程和参考资料时,我看不到任何有关如何实现string数据类型的示例。
有很多关于整数,实数和其他数字类型的文档,甚至是数组,函数和结构,但AFAIK没有关于字符串的内容。我需要add a new data type到后端吗?有没有办法使用内置数据类型?任何见解都将不胜感激。
答案 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)
考虑字符串如何用通用语言表示:
string
是一个带有构造函数,析构函数和复制构造函数的复杂对象。在内部,它通常基本上包含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
。