Das folgende langwierige C-Programm generiert ein einfaches LLVM-Modul mit einer Funktion, die lediglich llvm.x86.sse41.round.ps
aufruft. Er gibt die Bitcodedatei aus und führt den von LLVM generierten Code aus. Meine Frage ist, wie ich Zieldreifach- und Anweisungserweiterungen wie SSE oder AVX des Host-Rechners herausfinden kann und wie ich diese Informationen dem LLVM-Modul hinzufüge oder wie ich es sonst der LLVM-Ausführungsmaschine erzähle. Hier ist, was ich tue:Ermitteln und Festlegen von Hostziel-Tripel und Befehlserweiterungen in LLVM-C API
$ cat ctest/avx-instruction-selection.c
#include <llvm-c/Core.h>
#include <llvm-c/Target.h>
#include <llvm-c/ExecutionEngine.h>
#include <llvm-c/BitWriter.h>
#include <llvm-c/Transforms/Scalar.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#if 1
const int vectorSize = 4;
const char* roundName = "llvm.x86.sse41.round.ps";
#else
const int vectorSize = 8;
const char* roundName = "llvm.x86.avx.round.ps.256";
#endif
int main()
{
LLVMModuleRef module;
LLVMExecutionEngineRef execEngine;
LLVMTargetDataRef targetData;
LLVMTypeRef floatType, vectorType, ptrType, voidType, funcType, roundType, int32Type;
LLVMValueRef func, roundFunc;
LLVMValueRef param, loaded, const1, callRound;
LLVMBuilderRef builder;
LLVMBasicBlockRef block;
const int false = 0;
LLVMInitializeX86TargetInfo();
LLVMInitializeX86Target();
LLVMInitializeX86TargetMC();
module = LLVMModuleCreateWithName("_module");
LLVMSetTarget(module, "x86_64-unknown-linux-gnu");
floatType = LLVMFloatType();
vectorType = LLVMVectorType(floatType, vectorSize);
ptrType = LLVMPointerType(vectorType, 0);
voidType = LLVMVoidType();
LLVMTypeRef roundParams[] = { ptrType };
roundType = LLVMFunctionType(voidType, roundParams, 1, false);
func = LLVMAddFunction(module, "round", roundType);
LLVMSetLinkage(func, LLVMExternalLinkage);
builder = LLVMCreateBuilder();
block = LLVMAppendBasicBlock(func, "_L1");
LLVMPositionBuilderAtEnd(builder, block);
param = LLVMGetParam(func, 0);
loaded = LLVMBuildLoad(builder, param, "");
int32Type = LLVMIntType(32);
LLVMTypeRef funcParams[] = { vectorType, int32Type } ;
funcType = LLVMFunctionType(vectorType, funcParams, 2, false);
roundFunc = LLVMAddFunction(module, roundName, funcType);
LLVMSetLinkage(roundFunc, LLVMExternalLinkage);
const1 = LLVMConstInt(int32Type, 1, false);
LLVMValueRef callParams [] = { loaded, const1 } ;
callRound = LLVMBuildCall(builder, roundFunc, callParams, 2, "");
LLVMSetInstructionCallConv(callRound, 0);
LLVMAddInstrAttribute(callRound, 0, 0);
LLVMBuildStore(builder, callRound, param);
LLVMBuildRetVoid(builder);
LLVMWriteBitcodeToFile(module, "round-avx.bc");
char *errorMsg;
LLVMCreateExecutionEngineForModule(&execEngine, module, &errorMsg);
targetData = LLVMGetExecutionEngineTargetData(execEngine);
size_t vectorSize0 = LLVMStoreSizeOfType(targetData, vectorType);
size_t vectorAlign = LLVMABIAlignmentOfType(targetData, vectorType);
float vector[vectorSize];
printf("%lx, size %lx, align %lx\n", (size_t)vector, vectorSize0, vectorAlign);
LLVMGenericValueRef genericVector = LLVMCreateGenericValueOfPointer(vector);
LLVMGenericValueRef runParams[] = { genericVector } ;
LLVMRunFunction(execEngine, func, 1, runParams);
return 0;
}
$ gcc -Wall -o ctest/avx-instruction-selection ctest/avx-instruction-selection.c `/usr/lib/llvm-3.4/bin/llvm-config --cflags --ldflags` -lLLVM-3.4
$ ctest/avx-instruction-selection
7fff590431c0, size 10, align 10
$ ls round-avx.bc
round-avx.bc
$ llvm-dis -o - round-avx.bc
; ModuleID = 'round-avx.bc'
target triple = "x86_64-unknown-linux-gnu"
define void @round(<4 x float>*) {
_L1:
%1 = load <4 x float>* %0
%2 = call <4 x float> @llvm.x86.sse41.round.ps(<4 x float> %1, i32 1)
store <4 x float> %2, <4 x float>* %0
ret void
}
; Function Attrs: nounwind readnone
declare <4 x float> @llvm.x86.sse41.round.ps(<4 x float>, i32) #0
attributes #0 = { nounwind readnone }
$ gcc -Wall -o ctest/avx-instruction-selection ctest/avx-instruction-selection.c `/usr/lib/llvm-3.5/bin/llvm-config --cflags --ldflags` -lLLVM-3.5
$ ctest/avx-instruction-selection
7ffed6170350, size 10, align 10
LLVM ERROR: Cannot select: intrinsic %llvm.x86.sse41.round.ps
$ gcc -Wall -o ctest/avx-instruction-selection ctest/avx-instruction-selection.c `/usr/lib/llvm-3.6/bin/llvm-config --cflags --ldflags` -lLLVM-3.6
$ ctest/avx-instruction-selection
7ffeae91eb40, size 10, align 10
LLVM ERROR: Target does not support MC emission!
$ gcc -Wall -o ctest/avx-instruction-selection ctest/avx-instruction-selection.c `/usr/lib/llvm-3.7/bin/llvm-config --cflags --ldflags` -lLLVM-3.7
$ ctest/avx-instruction-selection
7fffb6464ea0, size 10, align 10
LLVM ERROR: Target does not support MC emission!
$ gcc -Wall -o ctest/avx-instruction-selection ctest/avx-instruction-selection.c `/usr/lib/llvm-3.8/bin/llvm-config --cflags --ldflags` -lLLVM-3.8
$ ctest/avx-instruction-selection
7ffd5e233000, size 10, align 10
LLVM ERROR: Target does not support MC emission!
Zusammengefasst: Mit LLVM-3.4 das Beispiel funktioniert, mit LLVM-3.5 der intrinsischen Funktion round.ps
nicht gefunden werden kann und LLVM-3.6 und später sagt etwas über MC-Emissionen, die ich nicht verstehen.
Wie ich verstehe, findet LLVM-3.5 nicht die round.ps
intrinsisch und ich denke, dass es nicht finden kann, weil ich es nicht über die vorhandene SSE-Erweiterung erzählt habe. Beim Ausführen llc
kann ich die Option -mattr=sse4.1
hinzufügen, aber wie kann ich es an die Ausführungs-Engine sagen?
Zweite Frage: Wie kann ich über die LLVM-C API die verfügbaren Befehlserweiterungen wie SSE des Host-Rechners herausfinden? Auf x86 kann ich den CPUID-Befehl aufrufen, aber gibt es einen Weg, der auf allen Plattformen einheitlich funktioniert und LLVM hilft, Erweiterungen zu erkennen?
Dritte Frage: Ich habe das Ziel-Triple hart in den C-Code codiert. Wie kann ich das Host-Ziel-Triple über die LLVM-C-API herausfinden?
Letzte Frage: Was ist mit diesem MC-Emissionsfehler?
Ich sehe, dass 'llvm-config --host-target' den Inhalt LLVM_DEFAULT_TARGET_TRIPLE emittiert. Es gibt die Dateien llvm/Config/config.h und llvm/Config/llvm-config.h, die die Makros LLVM_DEFAULT_TARGET_TRIPLE und LLVM_HOST_TRIPLE enthalten. Sie sind nicht streng Teil der LLVM-C-API, aber sind einfach C. – Lemming
Wenn LLVMInitializeX86TargetInfo(), LLVMInitializeX86Target() oder beide entfernen, dann verschwindet der "MC-Emission" -Fehler und ich bekomme konsequent "Kann nicht wählen: intrinsisch% llvm.x86 .sse41.round.ps "auf allen getesteten LLVM-Versionen. – Lemming
In "llvm/Support/Host.h "Ich habe die Funktion' getHostCPUName' gefunden. Dies sollte die Frage nach verfügbaren Befehlserweiterungen auf dem Host-Rechner beantworten. – Lemming