Ich versuche, mich in der LLVM-Infrastruktur zu orientieren. Ich habe die LLVM-Binärdateien für Windows auf einer MinGW-Installation installiert.
Ich folge dem Tutorial auf der LLVM-Seite über die sogenannte Kaleidoskop-Sprache. Ich habe eine Quelldatei mit genau Code-Listing am Ende dieser Seite .
Auch wenn es von Bedeutung ist, baue ich mit den folgenden Flags (erhalten durch llvm-config
im Voraus, weil die Windows-Shell keine sehr bequeme Substitutionssyntax hat):
clang++ -g -O3 kaleido.cpp -o kaleido.exe -IC:/MinGW/include -DNDEBUG -D__NO_CTYPE_INLINE -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -LC:/MinGW/lib -lLLVMCore -lLLVMSupport -lpthread -lLLVMX86Disassembler -lLLVMX86AsmParser -lLLVMX86CodeGen -lLLVMSelectionDAG -lLLVMAsmPrinter -lLLVMMCParser -lLLVMX86Desc -lLLVMX86Info -lLLVMX86AsmPrinter -lLLVMX86Utils -lLLVMJIT -lLLVMRuntimeDyld -lLLVMExecutionEngine -lLLVMCodeGen -lLLVMScalarOpts -lLLVMInstCombine -lLLVMTransformUtils -lLLVMipa -lLLVMAnalysis -lLLVMTarget -lLLVMMC -lLLVMObject -lLLVMCore -lLLVMSupport -lm -limagehlp -lpsapi
Unter Verwendung der vorgeschlagenen Sprache, die im verknüpften Code implementiert ist, teste ich ein paar Top-Level-Ausdrücke. Zuerst eins mit Literalen:
%Vor%... Funktioniert wie erwartet. Dann eine Funktionsdefinition mit einem konstanten Ergebnis:
%Vor%... Wieder funktioniert es wie erwartet. Wenn Sie dies für eine Eingabe aufrufen, erhalten Sie ein festes Ergebnis:
%Vor%... Keine Überraschung. Dann eine Funktionsdefinition, die etwas mit dem Parameter tut:
%Vor%... Sieht so aus, als ob es in Ordnung ist, der Bytecode wird erzeugt. Rufen Sie es jetzt an:
%Vor%... stürzt ab.
Durch ein rudimentäres Debugging bin ich zu der Überzeugung gelangt, dass die involvierten Codeabschnitte die Bedeutung für den Ausdruck der obersten Ebene (der Aufruf von g(x)
mit einem Argument von 5) und für den Code für den Aufruf bedeuten Funktion, sind beide JIT-kompiliert erfolgreich. Ich glaube, das ist der Fall, weil ich den Funktionszeiger vor den Absturz bekomme (und ich nehme an, dass die Ausführungsmaschine einen Funktionszeiger nur zurückgibt, nachdem sie die Funktion erfolgreich kompiliert hat ). Um genau zu sein, der Absturz passiert genau an dem Punkt, an dem der Funktionszeiger ausgeführt wird, also diese Zeile in meiner Quelldatei (in HandleTopLevelExpression()
):
Höchstwahrscheinlich ist die Zeile selbst unschuldig, da sie für andere Funktionen erfolgreich ausgeführt wird. Der Schuldige befindet sich wahrscheinlich irgendwo in der Funktion, auf die FP
im letzten der obigen Beispiele zeigt, aber da dieser Code Laufzeit generiert, habe ich ihn nicht in meiner cpp
-Datei.
Irgendwelche Ideen, warum es in diesem speziellen Szenario abstürzen könnte?
UPDATE # 1: Wenn Sie den Prozess über gdb ausführen, wird dies am Absturzpunkt angezeigt:
Programm empfangenes Signal SIGILL, Unzulässige Anweisung.
Und eine Spur, die mir nichts sagt:
%Vor%AKTUALISIERUNG # 2: In dem Versuch, mehr Licht auf dieses Thema zu werfen, hier ist die Versammlung um den Crash:
%Vor% Der Absturz findet bei 00D70074
statt, wobei die Anweisung LDS EDI,EBX
ist. Es sind ein paar Adressen, die höher sind als die Adresse, auf die FP
zeigt (was mich glauben lässt, dass dies alles JIT-emittierter Code sein könnte, aber bitte nehmen Sie diese Schlussfolgerung mit einer Prise Salz, da ich über meinen Kopf hier bin).
Wie Sie sehen können, hat der Disassembler auch einen Kommentar zu dieser und den nächsten ähnlichen Zeilen geschrieben, die sagen, dass es eine illegale Verwendung des Registers ist. Um ehrlich zu sein, ich weiß nicht, warum dieses spezielle erweiterte Registerpaar für diese Anweisung illegal ist, aber wenn es illegal ist, warum ist es überhaupt dort und wie können wir den Compiler machen legalen Code produzieren?
Anscheinend erstellt LLVM VX-Präfix-AVX-Anweisungen für Sie, aber Ihr Prozessor unterstützt diesen Befehlssatz nicht (und auch nicht Ihren Disassembler).
Die AVX-fähige Decodierung Ihrer JIT-Bytes ergibt den folgenden gültigen Code:
%Vor% Wenn LLVM die native Architektur falsch erkennt oder wenn Sie es nur überschreiben möchten, können Sie das EngineBuilder
im Beispielcode ändern, zum Beispiel wie folgt:
Sie können auch die Architektur festlegen oder Attribute bereitstellen.