2016-08-03 21 views
0

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?

+0

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

+0

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

+0

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

Antwort

0

Nach viel herum versuchen, ich denke, die Antwort lautet wie folgt:

Ersetzen Sie die Linien

LLVMInitializeX86TargetInfo(); 
LLVMInitializeX86Target(); 
LLVMInitializeX86TargetMC(); 

von

LLVMInitializeNativeTarget(); 
LLVMInitializeNativeAsmPrinter(); 
LLVMInitializeNativeAsmParser(); 

Ersetzen Sie den Aufruf von LLVMCreateExecutionEngineForModule durch einen Aufruf der benutzerdefinierten Funktion LLVMCreateExecutionEngineForModuleCPU. Es ist die ursprüngliche Implementierung von LLVMCreateExecutionEngineForModule plus einen Anruf von setMCPU.

#define LLVM_VERSION (LLVM_VERSION_MAJOR * 100 + LLVM_VERSION_MINOR) 

LLVMBool LLVMCreateExecutionEngineForModuleCPU 
    (LLVMExecutionEngineRef *OutEE, 
    LLVMModuleRef M, 
    char **OutError) { 
    std::string Error; 
#if LLVM_VERSION < 306 
    EngineBuilder builder(unwrap(M)); 
#else 
    EngineBuilder builder(std::unique_ptr<Module>(unwrap(M))); 
#endif 
    builder.setEngineKind(EngineKind::Either) 
     .setMCPU(sys::getHostCPUName().data()) 
     .setErrorStr(&Error); 
    if (ExecutionEngine *EE = builder.create()){ 
    *OutEE = wrap(EE); 
    return 0; 
    } 
    *OutError = strdup(Error.c_str()); 
    return 1; 
} 

sollte ich auch

float vector[vectorSize] __attribute__((aligned(32))); 

, um das Array für AVX Vektoren auszurichten hinzuzufügen.

Laut einer Antwort im Thread crash JIT with AVX intrinsics ist LLVMRunFunction auf main-ähnliche Prototypen beschränkt (anscheinend nur in MCJIT). So sollten wir auch die LLVMRunFunction Sachen ersetzen durch

void (*funcPtr) (float *); 
funcPtr = LLVMGetPointerToGlobal(execEngine, func); 
funcPtr(vector);