2009-06-30 13 views
12

Ich habe in letzter Zeit LLVM betrachtet, und ich finde es eine ziemlich interessante Architektur. Beim Durchsehen des Tutorials und des Referenzmaterials kann ich jedoch keine Beispiele sehen, wie ich einen string Datentyp implementieren könnte.Wie kann ich einen String-Datentyp in LLVM implementieren?

Es gibt eine Menge Dokumentation über Integer, Reals und andere Zahlentypen und sogar Arrays, Funktionen und Strukturen, aber AFAIK nichts über Strings. Würde ich add a new data type zum Backend haben? Gibt es eine Möglichkeit, integrierte Datentypen zu verwenden? Jeder Einblick würde geschätzt werden.

Antwort

14

Was ist eine Zeichenfolge? Ein Array von Zeichen.

Was ist ein Charakter? Eine ganze Zahl.

Also, während ich kein LLVM-Experte bin, würde ich sagen, wenn Sie zB einen 8-Bit-Zeichensatz darstellen möchten, würden Sie ein Array von i8 (8-Bit-Ganzzahlen) verwenden, oder ein Zeiger auf i8. Und in der Tat, wenn wir ein einfaches Hallo Welt C-Programm:

#include <stdio.h> 

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

und wir kompilieren llvm-gcc und Dump die erzeugten LLVM Montage:

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

Beachten Sie die Referenz auf die Funktion puts erklärt am Ende der Datei. In C, puts ist

int puts(const char *s) 

in LLVM ist es

i32 @puts(i8*) 

Die Korrespondenz sollte klar sein.

Nebenbei ist das generierte LLVM hier sehr ausführlich, weil ich ohne Optimierungen kompiliert habe. Wenn Sie diejenigen einschalten, verschwinden die unnötigen Anweisungen:

$ 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*) 
+0

Hmm, Okay - wenn ich also Strings benutzen möchte, wie es viele der interpretierten Sprachen heute tun (nicht nur ein Array, sondern einschließlich Länge usw.), müsste ich das als eine Art Struktur deklarieren, die das zusätzliche Gepäck mit sich trägt - hätte es das getan um ein ganz neuer Typ im Backend zu sein? –

+0

Ja, das ist grundsätzlich richtig, aber es muss kein neuer Typ im Backend sein. Sie können einfach eine LLVM-Struktur verwenden, um die benötigten Daten zu speichern, und dann einige Funktionen definieren, die auf Ihren Zeichenfolgen-Wrapper wirken. Wie Zifre sagt, ist es wirklich eine Low-Level-virtuelle Maschine. –

+0

Okay, ich habe herausgefunden, dass Sie in llvm nette kleine Arrays erstellen können, aber ich habe nirgends gefunden, dass diese Arrays auf eine andere Größe verteilt werden (was ich tun muss, wenn ich ein String länger) –

2

Denken Sie darüber nach, wie eine Zeichenfolge in gängigen Sprachen dargestellt wird:

  • C: ein Zeiger auf ein Zeichen. Sie müssen nichts Besonderes tun.
  • C++: string ist ein komplexes Objekt mit Konstruktor, Destruktor und Kopierkonstruktor. Auf der Innenseite hält es normalerweise im Wesentlichen eine C-Saite.
  • Java/C#/...: Eine Zeichenfolge ist ein komplexes Objekt, das ein Array von Zeichen enthält.

Der Name von LLVM ist sehr selbsterklärend. Es ist wirklich "Low Level". Sie müssen Strings implementieren, wie immer Sie sie haben wollen. Es wäre dumm für LLVM, jemanden in eine bestimmte Implementierung zu zwingen.

11

[auf andere Antworten zu folgen, die erklären, was Strings sind, hier einige Implementierung Hilfe ist]

die C-Schnittstelle verwenden, die Anrufe werden Sie wie sind etwas wollen:

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

Danke dafür, ich denke, es gibt nicht genug Beispiele mit der LLVM C API. Nur eine Sache: Ich bekomme eine 'Assertion fehlgeschlagen: InitVal-> getType() == getType() -> getElementType() &&" Initialisierungsart muss GlobalVariable-Typ entsprechen ", Datei Globals.cpp, Zeile 168' dabei. Irgendeine Idee warum? –

Verwandte Themen